(frontend) enhance UI components and improve document management

- Updated DropdownMenu to include index-based styling for better visual
consistency.
- Refactored QuickSearchStyle to remove unnecessary transitions for
smoother performance.
- Adjusted modal styles in cunningham-style.css for improved layout.
- Changed BlockNoteEditor to update block type from 'heading' to
'paragraph' for better content structure.
- Enhanced DocHeader and DocToolBox components with updated color themes
for improved visibility.
- Modified ModalRemoveDoc to change size and clean up unnecessary props
for better usability.
- Improved Heading and TableContent components to handle empty states
more gracefully.
- Updated DocsGrid to conditionally render content based on document
availability, enhancing user experience.
- Refined LeftPanel components for better layout and visual hierarchy,
including adjustments to padding and separators.
This commit is contained in:
Nathan Panchout
2025-01-09 09:14:20 +01:00
committed by Anthony LC
parent 684b77cbe6
commit 098df5c0b5
19 changed files with 160 additions and 111 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -77,7 +77,7 @@ export const DropdownMenu = ({
{topMessage}
</Text>
)}
{options.map((option) => {
{options.map((option, index) => {
if (option.show !== undefined && !option.show) {
return;
}
@@ -104,6 +104,16 @@ export const DropdownMenu = ({
$gap={spacings['base']}
$css={css`
border: none;
${index === 0 &&
css`
border-top-left-radius: 4px;
border-top-right-radius: 4px;
`}
${index === options.length - 1 &&
css`
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
`}
font-size: var(--c--theme--font--sizes--sm);
color: var(--c--theme--colors--greyscale-1000);
font-weight: 500;

View File

@@ -71,8 +71,6 @@ export const QuickSearchStyle = createGlobalStyle`
flex:1;
overflow-y: auto;
overscroll-behavior: contain;
transition: 100ms ease;
transition-property: height;
}
[cmdk-vercel-shortcuts] {

View File

@@ -545,8 +545,8 @@ input:-webkit-autofill:focus {
color: var(--c--theme--colors--greyscale-600);
}
.c__modal__footer {
margin-top: -1rem;
.c__modal__close .c__button {
padding: 0 !important;
}
.c__modal--full .c__modal__content {

View File

@@ -10,7 +10,7 @@ import * as Y from 'yjs';
import { Box, TextErrors } from '@/components';
import { useAuthStore } from '@/core/auth';
import { Doc, Role, currentDocRole } from '@/features/docs/doc-management';
import { Doc } from '@/features/docs/doc-management';
import { useUploadFile } from '../hook';
import { useHeadings } from '../hook/useHeadings';
@@ -167,23 +167,6 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
);
useHeadings(editor);
/**
* With the collaboration it gets complicated to create the initial block
* better to let Blocknote manage, then we update the block with the content.
*/
useEffect(() => {
if (doc.content || currentDocRole(doc.abilities) !== Role.OWNER) {
return;
}
setTimeout(() => {
editor.updateBlock(editor.document[0], {
type: 'heading',
content: '',
});
}, 100);
}, [editor, doc.content, doc.abilities]);
useEffect(() => {
setEditor(editor);

View File

@@ -42,7 +42,7 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
{docIsPublic && (
<Box
aria-label={t('Public document')}
$color={colors['primary-600']}
$color={colors['primary-800']}
$background={colors['primary-100']}
$radius={spacings['3xs']}
$direction="row"
@@ -54,8 +54,15 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
border: 1px solid var(--c--theme--colors--primary-300, #e3e3fd);
`}
>
<Icon data-testid="public-icon" iconName="public" />
<Text>{t('Public document')}</Text>
<Icon
$theme="primary"
$variation="800"
data-testid="public-icon"
iconName="public"
/>
<Text $theme="primary" $variation="800">
{t('Public document')}
</Text>
</Box>
)}
<Box $direction="row" $align="center" $width="100%">

View File

@@ -212,6 +212,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
$theme="primary"
$padding={{ all: 'xs' }}
$css={css`
border-radius: 4px;
&:hover {
background-color: ${colors['greyscale-100']};
}

View File

@@ -46,7 +46,6 @@ export const ModalRemoveDoc = ({ onClose, doc }: ModalRemoveDocProps) => {
<Modal
isOpen
closeOnClickOutside
hideCloseButton
onClose={() => onClose()}
rightActions={
<>
@@ -72,17 +71,14 @@ export const ModalRemoveDoc = ({ onClose, doc }: ModalRemoveDocProps) => {
</Button>
</>
}
size={ModalSize.SMALL}
size={ModalSize.MEDIUM}
title={
<Text $size="h6" as="h6" $margin={{ all: '0' }} $align="flex-start">
{t('Delete a doc')}
</Text>
}
>
<Box
$margin={{ bottom: 'xl' }}
aria-label={t('Content modal to delete document')}
>
<Box aria-label={t('Content modal to delete document')}>
{!isError && (
<Text $size="sm" $variation="600">
{t('Are you sure you want to delete the document "{{title}}"?', {

View File

@@ -65,7 +65,7 @@ export const Heading = ({
$width="100%"
$padding={{ vertical: 'xtiny', left: leftPaddingMap[level] }}
$variation={isActive ? '1000' : '700'}
$weight={isActive ? 'bold' : 'normal'}
$weight={isHighlight ? 'bold' : 'normal'}
$css="overflow-wrap: break-word;"
$hasTransition
aria-selected={isHighlight}

View File

@@ -78,7 +78,12 @@ export const TableContent = () => {
setIsHover(false);
};
if (!editor) {
if (
!editor ||
!headings ||
headings.length === 0 ||
(headings.length === 1 && !headings[0].contentText)
) {
return null;
}

View File

@@ -1,4 +1,4 @@
import { Button, Loader } from '@openfun/cunningham-react';
import { Button } from '@openfun/cunningham-react';
import { useTranslation } from 'react-i18next';
import { InView } from 'react-intersection-observer';
import { css } from 'styled-components';
@@ -36,7 +36,7 @@ export const DocsGrid = ({
}),
});
const loading = isFetching || isLoading;
const hasDocs = data?.pages.some((page) => page.results.length > 0);
const loadMore = (inView: boolean) => {
if (!inView || loading) {
return;
@@ -63,7 +63,7 @@ export const DocsGrid = ({
overflow-y: auto;
`}
>
<DocsGridLoader isLoading={isRefetching} />
<DocsGridLoader isLoading={isRefetching || loading} />
<Card
data-testid="docs-grid"
$height="100%"
@@ -87,48 +87,46 @@ export const DocsGrid = ({
{title}
</Text>
<Box $gap="6px">
<Box
$direction="row"
$padding={{ horizontal: 'xs' }}
$gap="20px"
data-testid="docs-grid-header"
>
<Box $flex={6} $padding="3xs">
<Text $size="xs" $variation="600" $weight="500">
{t('Name')}
</Text>
</Box>
{isDesktop && (
<Box $flex={2} $padding="3xs">
<Text $size="xs" $weight="500" $variation="600">
{t('Updated at')}
</Text>
</Box>
)}
<Box $flex={1.15} $align="flex-end" $padding="3xs" />
</Box>
{/* Body */}
{data?.pages.map((currentPage) => {
return currentPage.results.map((doc) => (
<DocsGridItem doc={doc} key={doc.id} />
));
})}
</Box>
{loading && (
<Box
data-testid="docs-grid-loader"
$padding="md"
$align="center"
$justify="center"
$width="100%"
>
<Loader />
{!hasDocs && (
<Box $padding={{ vertical: 'sm' }} $align="center" $justify="center">
<Text $size="sm" $variation="600" $weight="700">
{t('No documents found')}
</Text>
</Box>
)}
{hasDocs && (
<Box $gap="6px">
<Box
$direction="row"
$padding={{ horizontal: 'xs' }}
$gap="20px"
data-testid="docs-grid-header"
>
<Box $flex={6} $padding="3xs">
<Text $size="xs" $variation="600" $weight="500">
{t('Name')}
</Text>
</Box>
{isDesktop && (
<Box $flex={2} $padding="3xs">
<Text $size="xs" $weight="500" $variation="600">
{t('Updated at')}
</Text>
</Box>
)}
<Box $flex={1.15} $align="flex-end" $padding="3xs" />
</Box>
{/* Body */}
{data?.pages.map((currentPage) => {
return currentPage.results.map((doc) => (
<DocsGridItem doc={doc} key={doc.id} />
));
})}
</Box>
)}
{hasNextPage && !loading && (
<InView
data-testid="infinite-scroll-trigger"

View File

@@ -0,0 +1,4 @@
export * from './DocsGrid';
export * from './DocsGridActions';
export * from './SimpleDocItem';
export * from './DocsGridLoader';

View File

@@ -0,0 +1 @@
export * from './components';

View File

@@ -1,2 +1,3 @@
export * from './doc-editor';
export * from './doc-management';
export * from './docs-grid';

View File

@@ -79,7 +79,6 @@ export const LeftPanelTargetFilters = () => {
font-weight: ${isActive ? 700 : undefined};
&:hover {
background-color: ${colors['greyscale-100']};
font-weight: 700;
}
`}
>

View File

@@ -22,7 +22,7 @@ export const LeftPanelContent = () => {
flex: 0 0 auto;
`}
>
<SeparatedSection>
<SeparatedSection showSeparator={false}>
<LeftPanelTargetFilters />
</SeparatedSection>
</Box>

View File

@@ -1,10 +1,14 @@
import { css } from 'styled-components';
import { Box, SeparatedSection } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { useDocStore } from '@/features/docs';
import { SimpleDocItem } from '@/features/docs/docs-grid/components/SimpleDocItem';
export const LeftPanelDocContent = () => {
const { currentDoc } = useDocStore();
const { spacingsTokens } = useCunninghamTheme();
const spacing = spacingsTokens();
if (!currentDoc) {
return null;
}
@@ -17,7 +21,15 @@ export const LeftPanelDocContent = () => {
>
<SeparatedSection showSeparator={false}>
<Box $padding={{ horizontal: 'sm' }}>
<SimpleDocItem doc={currentDoc} showAccesses={true} />
<Box
$css={css`
padding: ${spacing['2xs']};
border-radius: 4px;
background-color: var(--c--theme--colors--greyscale-100);
`}
>
<SimpleDocItem doc={currentDoc} showAccesses={true} />
</Box>
</Box>
</SeparatedSection>
</Box>

View File

@@ -0,0 +1,52 @@
import { useModal } from '@openfun/cunningham-react';
import { css } from 'styled-components';
import { Box, StyledLink } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { Doc, DocsGridActions, SimpleDocItem } from '@/features/docs';
import { DocShareModal } from '@/features/docs/doc-share/component/DocShareModal';
import { useResponsiveStore } from '@/stores';
type LeftPanelFavoriteItemProps = {
doc: Doc;
};
export const LeftPanelFavoriteItem = ({ doc }: LeftPanelFavoriteItemProps) => {
const shareModal = useModal();
const { spacingsTokens } = useCunninghamTheme();
const { isDesktop } = useResponsiveStore();
const spacing = spacingsTokens();
return (
<Box
$direction="row"
$align="center"
$justify="space-between"
$css={css`
padding: ${spacing['2xs']};
border-radius: 4px;
.pinned-actions {
opacity: ${isDesktop ? 0 : 1};
}
&:hover {
cursor: pointer;
background-color: var(--c--theme--colors--greyscale-100);
.pinned-actions {
opacity: 1;
}
}
`}
key={doc.id}
>
<StyledLink href={`/docs/${doc.id}`}>
<SimpleDocItem showAccesses doc={doc} />
</StyledLink>
<div className="pinned-actions">
<DocsGridActions doc={doc} openShareModal={shareModal.open} />
</div>
{shareModal.isOpen && (
<DocShareModal doc={doc} onClose={shareModal.close} />
)}
</Box>
);
};

View File

@@ -1,16 +1,11 @@
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import {
Box,
InfiniteScroll,
SeparatedSection,
StyledLink,
Text,
} from '@/components';
import { Box, InfiniteScroll, Text } from '@/components';
import { HorizontalSeparator } from '@/components/separators/HorizontalSeparator';
import { useCunninghamTheme } from '@/cunningham';
import { useInfiniteDocs } from '@/features/docs';
import { SimpleDocItem } from '@/features/docs/docs-grid/components/SimpleDocItem';
import { LeftPanelFavoriteItem } from './LeftPanelFavoriteItem';
export const LeftPanelFavorites = () => {
const { t } = useTranslation();
@@ -23,17 +18,18 @@ export const LeftPanelFavorites = () => {
is_favorite: true,
});
const invitations = docs.data?.pages.flatMap((page) => page.results) || [];
const favoriteDocs = docs.data?.pages.flatMap((page) => page.results) || [];
if (invitations.length === 0) {
if (favoriteDocs.length === 0) {
return null;
}
return (
<SeparatedSection showSeparator={true}>
<Box>
<HorizontalSeparator $withPadding={false} />
<Box
$justify="center"
$padding={{ horizontal: 'sm' }}
$padding={{ horizontal: 'sm', top: 'sm' }}
$gap={spacing['2xs']}
$height="100%"
data-testid="left-panel-favorites"
@@ -51,25 +47,11 @@ export const LeftPanelFavorites = () => {
isLoading={docs.isFetchingNextPage}
next={() => void docs.fetchNextPage()}
>
{invitations.map((doc) => (
<Box
$css={css`
padding: ${spacing['2xs']};
border-radius: 4px;
&:hover {
cursor: pointer;
background-color: var(--c--theme--colors--greyscale-100);
}
`}
key={doc.id}
>
<StyledLink href={`/docs/${doc.id}`}>
<SimpleDocItem showAccesses doc={doc} />
</StyledLink>
</Box>
{favoriteDocs.map((doc) => (
<LeftPanelFavoriteItem key={doc.id} doc={doc} />
))}
</InfiniteScroll>
</Box>
</SeparatedSection>
</Box>
);
};