✨(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:
committed by
Anthony LC
parent
098df5c0b5
commit
f02dcae52a
@@ -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>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user