(frontend) enhance DocShareModal and footer components

- Refactored DocShareModal to improve user experience with dynamic list
height and responsive design adjustments.
- Introduced new styling for modal elements using createGlobalStyle for
better visual consistency.
- Updated footer button text from 'Ok' to 'OK' for improved clarity.
- Enhanced user selection handling and search functionality within the
modal for better document sharing experience.
This commit is contained in:
Nathan Panchout
2025-01-09 09:14:55 +01:00
committed by Anthony LC
parent 098df5c0b5
commit f02dcae52a
2 changed files with 128 additions and 94 deletions

View File

@@ -1,7 +1,7 @@
import { Modal, ModalSize } from '@openfun/cunningham-react'; import { Modal, ModalSize } from '@openfun/cunningham-react';
import { useMemo, useState } from 'react'; import { useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { css } from 'styled-components'; import { createGlobalStyle, css } from 'styled-components';
import { useDebouncedCallback } from 'use-debounce'; import { useDebouncedCallback } from 'use-debounce';
import { Box } from '@/components'; import { Box } from '@/components';
@@ -29,20 +29,33 @@ import { DocShareMemberItem } from './DocShareMemberItem';
import { DocShareModalFooter } from './DocShareModalFooter'; import { DocShareModalFooter } from './DocShareModalFooter';
import { DocShareModalInviteUserRow } from './DocShareModalInviteUserByEmail'; import { DocShareModalInviteUserRow } from './DocShareModalInviteUserByEmail';
const ShareModalStyle = createGlobalStyle`
.c__modal__title {
padding-bottom: 0 !important;
}
`;
type Props = { type Props = {
doc: Doc; doc: Doc;
onClose: () => void; onClose: () => void;
}; };
export const DocShareModal = ({ doc, onClose }: Props) => { export const DocShareModal = ({ doc, onClose }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const selectedUsersRef = useRef<HTMLDivElement>(null);
const { isDesktop } = useResponsiveStore(); const { isDesktop } = useResponsiveStore();
const [selectedUsers, setSelectedUsers] = useState<User[]>([]); const [selectedUsers, setSelectedUsers] = useState<User[]>([]);
const [userQuery, setUserQuery] = useState(''); const [userQuery, setUserQuery] = useState('');
const [inputValue, setInputValue] = useState(''); const [inputValue, setInputValue] = useState('');
const [listHeight, setListHeight] = useState<string>('400px');
const canShare = doc.abilities.accesses_manage; const canShare = doc.abilities.accesses_manage;
const showMemberSection = inputValue === '' && selectedUsers.length === 0; const showMemberSection = inputValue === '' && selectedUsers.length === 0;
const showFooter = selectedUsers.length === 0 && !inputValue;
const onSelect = (user: User) => { const onSelect = (user: User) => {
setSelectedUsers((prev) => [...prev, user]); setSelectedUsers((prev) => [...prev, user]);
@@ -73,7 +86,10 @@ export const DocShareModal = ({ doc, onClose }: Props) => {
const count = membersQuery.data?.pages[0]?.count ?? 1; const count = membersQuery.data?.pages[0]?.count ?? 1;
return { return {
groupName: t('Share with {{count}} users', { groupName:
count === 1
? t('Document owner')
: t('Share with {{count}} users', {
count: count, count: count,
}), }),
elements: members, elements: members,
@@ -147,7 +163,20 @@ export const DocShareModal = ({ doc, onClose }: Props) => {
}); });
}; };
const handleRef = (node: HTMLDivElement) => {
const contentHeight = isDesktop ? '690px' : '100dvh - 34px'; // 690px is the height of the content in desktop ad 34px is the height of the modal title in mobile
const inputHeight = canShare ? 70 : 0;
const marginTop = 11;
const footerHeight = node?.clientHeight ?? 0;
const selectedUsersHeight = selectedUsersRef.current?.clientHeight ?? 0;
const height = `calc(${contentHeight} - ${footerHeight}px - ${selectedUsersHeight}px - ${inputHeight}px - ${marginTop}px)`;
setListHeight(height);
};
return ( return (
<>
<Modal <Modal
isOpen isOpen
closeOnClickOutside closeOnClickOutside
@@ -157,26 +186,28 @@ export const DocShareModal = ({ doc, onClose }: Props) => {
onClose={onClose} onClose={onClose}
title={<Box $align="flex-start">{t('Share the document')}</Box>} title={<Box $align="flex-start">{t('Share the document')}</Box>}
> >
<ShareModalStyle />
<Box <Box
aria-label={t('Share modal')} aria-label={t('Share modal')}
$direction="column" $height={isDesktop ? '690px' : `calc(100dvh - 34px)`}
$overflow="hidden"
$justify="space-between" $justify="space-between"
> >
<Box <Box
$flex={1} $flex={1}
className="toto"
$css={css` $css={css`
overflow-y: auto;
[cmdk-list] { [cmdk-list] {
overflow-y: auto; overflow-y: auto;
height: ${isDesktop height: ${listHeight};
? '400px'
: 'calc(100vh - 49px - 68px - 229px)'};
} }
`} `}
> >
<div ref={selectedUsersRef}>
{canShare && selectedUsers.length > 0 && ( {canShare && selectedUsers.length > 0 && (
<Box $padding={{ horizontal: 'base' }} $margin={{ top: '11px' }}> <Box
$padding={{ horizontal: 'base' }}
$margin={{ top: '11px' }}
>
<DocShareAddMemberList <DocShareAddMemberList
doc={doc} doc={doc}
selectedUsers={selectedUsers} selectedUsers={selectedUsers}
@@ -189,6 +220,7 @@ export const DocShareModal = ({ doc, onClose }: Props) => {
/> />
</Box> </Box>
)} )}
</div>
<Box data-testid="doc-share-quick-search"> <Box data-testid="doc-share-quick-search">
<QuickSearch <QuickSearch
@@ -239,10 +271,12 @@ export const DocShareModal = ({ doc, onClose }: Props) => {
</QuickSearch> </QuickSearch>
</Box> </Box>
</Box> </Box>
{selectedUsers.length === 0 && !inputValue && (
<DocShareModalFooter doc={doc} onClose={onClose} /> <Box ref={handleRef}>
)} {showFooter && <DocShareModalFooter doc={doc} onClose={onClose} />}
</Box>
</Box> </Box>
</Modal> </Modal>
</>
); );
}; };

View File

@@ -61,7 +61,7 @@ export const DocShareModalFooter = ({ doc, onClose }: Props) => {
{t('Copy link')} {t('Copy link')}
</Button> </Button>
<Button onClick={onClose} color="primary"> <Button onClick={onClose} color="primary">
{t('Ok')} {t('OK')}
</Button> </Button>
</Box> </Box>
</Box> </Box>