From 97e9de44277b09b37ba40c058836b15d65a0018e Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 14 Mar 2025 11:39:02 +0100 Subject: [PATCH] fix: remove useless rotf future flag (#23139) --- examples/getstarted/config/features.js | 1 - .../components/FormInputs/Relations.tsx | 579 +----------------- .../EditView/components/InputRenderer.tsx | 5 +- packages/core/types/src/modules/features.ts | 1 - tests/e2e/app-template/config/features.js | 1 - 5 files changed, 2 insertions(+), 585 deletions(-) diff --git a/examples/getstarted/config/features.js b/examples/getstarted/config/features.js index 79773ee2d0..c05749138c 100644 --- a/examples/getstarted/config/features.js +++ b/examples/getstarted/config/features.js @@ -1,6 +1,5 @@ module.exports = ({ env }) => ({ future: { unstablePreviewSideEditor: true, - unstableRelationsOnTheFly: true, }, }); diff --git a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations.tsx b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations.tsx index f931eb3988..6dbce3b5b7 100644 --- a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations.tsx +++ b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations.tsx @@ -141,220 +141,6 @@ export interface RelationsFormValue { * At present we do not expose this to plugin developers, however, they are able to overwrite it themselves should * they wish to do so. */ -const UnstableRelationsField = React.forwardRef( - ({ disabled, label, ...props }, ref) => { - const [currentPage, setCurrentPage] = React.useState(1); - const { document, model: documentModel } = useDoc(); - const documentId = document?.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 }) => ({ - componentId: id, - componentUID: uid, - })); - - const isSubmitting = useForm('RelationsList', (state) => state.isSubmitting); - - React.useEffect(() => { - setCurrentPage(1); - }, [isSubmitting]); - - /** - * We'll always have a documentId in a created entry, so we look for a componentId first. - * Same with `uid` and `documentModel`. - */ - const id = componentId ? componentId.toString() : documentId; - const model = componentUID ?? documentModel; - - /** - * The `name` prop is a complete path to the field, e.g. `field1.field2.field3`. - * Where the above example would a nested field within two components, however - * we only require the field on the component not the complete path since we query - * individual components. Therefore we split the string and take the last item. - */ - 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: id!, - params: { - ...params, - pageSize: RELATIONS_TO_DISPLAY, - page: currentPage, - }, - }, - { - refetchOnMountOrArgChange: true, - skip: !id, - selectFromResult: (result) => { - return { - ...result, - data: { - ...result.data, - results: result.data?.results ? result.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; - - /** - * Items that are already connected, but reordered would be in - * this list, so to get an accurate figure, we remove them. - */ - const relationsConnected = - (field.value?.connect ?? []).filter( - (rel: Relation) => data.results.findIndex((relation) => relation.id === rel.id) === -1 - ).length ?? 0; - const relationsDisconnected = field.value?.disconnect?.length ?? 0; - - const relationsCount = realServerRelationsCount + relationsConnected - relationsDisconnected; - - /** - * This is it, the source of truth for reordering in conjunction with partial loading & updating - * of relations. Relations on load are given __temp_key__ when fetched, because we don't want to - * create brand new keys everytime the data updates, just keep adding them onto the newly loaded ones. - */ - 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, - }; - - /** - * Tidy up our data. - */ - const transformations = pipe( - removeConnected(ctx), - removeDisconnected(ctx), - addLabelAndHref(ctx) - ); - - const transformedRels = transformations([...data.results]); - - /** - * THIS IS CRUCIAL. If you don't sort by the __temp_key__ which comes from fractional indexing - * then the list will be in the wrong order. - */ - return [...transformedRels, ...(field.value?.connect ?? [])].sort((a, b) => { - if (a.__temp_key__ < b.__temp_key__) return -1; - if (a.__temp_key__ > b.__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: RelationsInputProps['onChange'] = (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)) { - // Remove any existing relation so they can be replaced with the new one - 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 ( - - - 0 ? `(${relationsCount})` : ''}`} - model={model} - onChange={handleConnect} - {...props} - /> - {'pagination' in data && - data.pagination && - data.pagination.pageCount > data.pagination.page ? ( - } - // prevent the label from line-wrapping - shrink={0} - > - {formatMessage({ - id: getTranslation('relation.loadMore'), - defaultMessage: 'Load More', - })} - - ) : null} - - - - ); - } -); - const RelationsField = React.forwardRef( ({ disabled, label, ...props }, ref) => { const [currentPage, setCurrentPage] = React.useState(1); @@ -846,240 +632,6 @@ interface RelationsListProps extends Pick { - const ariaDescriptionId = React.useId(); - const { formatMessage } = useIntl(); - const listRef = React.useRef(null); - const outerListRef = React.useRef(null); - const [overflow, setOverflow] = React.useState<'top' | 'bottom' | 'top-bottom'>(); - const [liveText, setLiveText] = React.useState(''); - const field = useField(name); - - React.useEffect(() => { - if (data.length <= RELATIONS_TO_DISPLAY) { - return setOverflow(undefined); - } - - const handleNativeScroll = (e: Event) => { - const el = e.target as HTMLUListElement; - const parentScrollContainerHeight = (el.parentNode as HTMLDivElement).scrollHeight; - const maxScrollBottom = el.scrollHeight - el.scrollTop; - - if (el.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: number) => `${index + 1} of ${data.length}`; - - const handleMoveItem: UseDragAndDropOptions['onMoveItem'] = (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), - } - ) - ); - - /** - * Splicing mutates the array, so we need to create a new array - */ - 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__; - - /** - * We're moving the relation between two other relations, so - * we need to generate a new key that keeps the order - */ - const [newKey] = generateNKeysBetween(startKey, endKey, 1); - - newData.splice(oldIndex, 1); - newData.splice(newIndex, 0, { ...currentRow, __temp_key__: newKey }); - - /** - * Now we diff against the server to understand what's different so we - * can keep the connect array nice and tidy. It also needs reversing because - * we reverse the relations from the server in the first place. - */ - 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 = { - ...relation, - ...{ - apiData: { - id: relation.id, - documentId: relation.documentId, - locale: relation.locale, - position, - }, - }, - }; - - return [...acc, relationWithPosition]; - } - - return acc; - }, []) - .toReversed(); - - field.onChange(`${name}.connect`, connectedRelations); - }; - - const handleGrabItem: UseDragAndDropOptions['onGrabItem'] = (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: UseDragAndDropOptions['onDropItem'] = (index) => { - const { href: _href, label, ...item } = data[index]; - - setLiveText( - formatMessage( - { - id: getTranslation('dnd.drop-item'), - defaultMessage: `{item}, dropped. Final position in list: {position}.`, - }, - { - item: label ?? item.documentId, - position: getItemPos(index), - } - ) - ); - }; - - const handleCancel: UseDragAndDropOptions['onCancel'] = (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(name, 'RelationsList'); - - /** - * These relation types will only ever have one item - * in their list, so you can't reorder a single item! - */ - 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 ( - - - {formatMessage({ - id: getTranslation('dnd.instructions'), - defaultMessage: `Press spacebar to grab and re-order`, - })} - - {liveText} - {/* @ts-expect-error – width is expected, but we've not needed to pass it before. */} - data[index].id} - innerElementType="ol" - > - {UnstableListItem} - - - ); -}; - const RelationsList = ({ data, serverData, @@ -1369,128 +921,6 @@ interface ListItemProps extends Pick }; } -const CustomTextButton = styled(TextButton)` - & > span { - font-size: ${({ theme }) => theme.fontSizes[2]}; - } -`; - -const UnstableListItem = ({ data, index, style }: ListItemProps) => { - const { - ariaDescribedBy, - canDrag = false, - disabled = false, - handleCancel, - handleDisconnect, - handleDropItem, - handleGrabItem, - handleMoveItem, - name, - relations, - } = data; - const { formatMessage } = useIntl(); - - const { id, label, status } = relations[index]; - - const [{ handlerId, isDragging, handleKeyDown }, relationRef, dropRef, dragRef, dragPreviewRef] = - useDragAndDrop, HTMLDivElement>( - canDrag && !disabled, - { - type: `${ItemTypes.RELATION}_${name}`, - index, - item: { - displayedValue: label, - status, - id: id, - index, - }, - onMoveItem: handleMoveItem, - onDropItem: handleDropItem, - onGrabItem: handleGrabItem, - onCancel: handleCancel, - dropSensitivity: DROP_SENSITIVITY.REGULAR, - } - ); - - const composedRefs = useComposedRefs(relationRef, dragRef); - - React.useEffect(() => { - dragPreviewRef(getEmptyImage()); - }, [dragPreviewRef]); - - return ( - - {isDragging ? ( - - ) : ( - - - {canDrag ? ( - - - - ) : null} - - - - {/* eslint-disable-next-line no-console */} - console.log('OPEN MODAL')}> - {label} - - - - {status ? : null} - - - - handleDisconnect(relations[index])} - disabled={disabled} - label={formatMessage({ - id: getTranslation('relation.disconnect'), - defaultMessage: 'Remove', - })} - variant="ghost" - size="S" - > - - - - - )} - - ); -}; - const ListItem = ({ data, index, style }: ListItemProps) => { const { ariaDescribedBy, @@ -1661,13 +1091,6 @@ const RelationItemPlaceholder = () => ( ); const MemoizedRelationsField = React.memo(RelationsField); -const MemoizedUnstableRelationsField = React.memo(UnstableRelationsField); -export { - MemoizedRelationsField as RelationsInput, - MemoizedUnstableRelationsField as UnstableRelationsInput, - FlexWrapper, - DisconnectButton, - LinkEllipsis, -}; +export { MemoizedRelationsField as RelationsInput, FlexWrapper, DisconnectButton, LinkEllipsis }; export type { RelationsFieldProps }; diff --git a/packages/core/content-manager/admin/src/pages/EditView/components/InputRenderer.tsx b/packages/core/content-manager/admin/src/pages/EditView/components/InputRenderer.tsx index c55aa9cda5..4fd6a11c54 100644 --- a/packages/core/content-manager/admin/src/pages/EditView/components/InputRenderer.tsx +++ b/packages/core/content-manager/admin/src/pages/EditView/components/InputRenderer.tsx @@ -18,7 +18,7 @@ import { BlocksInput } from './FormInputs/BlocksInput/BlocksInput'; import { ComponentInput } from './FormInputs/Component/Input'; import { DynamicZone, useDynamicZone } from './FormInputs/DynamicZone/Field'; import { NotAllowedInput } from './FormInputs/NotAllowed'; -import { RelationsInput, UnstableRelationsInput } from './FormInputs/Relations'; +import { RelationsInput } from './FormInputs/Relations'; import { UIDInput } from './FormInputs/UID'; import { Wysiwyg } from './FormInputs/Wysiwyg/Field'; @@ -142,9 +142,6 @@ const InputRenderer = ({ visible, hint: providedHint, ...props }: InputRendererP case 'dynamiczone': return ; case 'relation': - if (window.strapi.future.isEnabled('unstableRelationsOnTheFly')) { - return ; - } return ; case 'richtext': return ; diff --git a/packages/core/types/src/modules/features.ts b/packages/core/types/src/modules/features.ts index 1cdc9a6f38..e9fd552354 100644 --- a/packages/core/types/src/modules/features.ts +++ b/packages/core/types/src/modules/features.ts @@ -1,7 +1,6 @@ export interface FeaturesConfig { future?: { unstablePreviewSideEditor?: boolean; - unstableRelationsOnTheFly?: boolean; }; } diff --git a/tests/e2e/app-template/config/features.js b/tests/e2e/app-template/config/features.js index 447ab97613..55baa873fa 100644 --- a/tests/e2e/app-template/config/features.js +++ b/tests/e2e/app-template/config/features.js @@ -1,6 +1,5 @@ module.exports = ({ env }) => ({ future: { unstablePreviewSideEditor: env.bool('STRAPI_FEATURES_UNSTABLE_PREVIEW_SIDE_EDITOR', false), - unstableRelationsOnTheFly: env.bool('STRAPI_FEATURES_UNSTABLE_RELATIONS_ON_THE_FLY', false), }, });