Skip to content

Commit b983869

Browse files
committed
✨(frontend) add EmojiPicker in DocumentTitle
As discussed in #1358
1 parent e571e91 commit b983869

File tree

9 files changed

+164
-65
lines changed

9 files changed

+164
-65
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ and this project adheres to
1414
### Added
1515

1616
- ✨(api) add API route to fetch document content #1206
17+
- ✨(frontend) doc emojis improvements #1381
18+
- add an EmojiPicker in the document tree and document title
19+
- remove emoji buttons in menus
1720

1821
### Changed
1922

@@ -27,6 +30,8 @@ and this project adheres to
2730
- ✨unify tab focus style for better visual consistency #1341
2831
- ♿hide decorative icons, label menus, avoid accessible name… #1362
2932
- ♻️(tilt) use helm dev-backend chart
33+
- 🩹(frontend) on main pages do not display leading emoji as page icon #1381
34+
- 🩹(frontend) handle properly emojis in interlinking #1381
3035

3136
### Removed
3237

src/frontend/apps/impress/src/features/docs/doc-editor/components/EmojiPicker.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const EmojiPicker = ({
1919
const { i18n } = useTranslation();
2020

2121
return (
22-
<Box>
22+
<Box $position="absolute" $zIndex={1000} $margin="2rem 0 0 0">
2323
<Picker
2424
data={emojiData}
2525
locale={i18n.resolvedLanguage}

src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-inline-content/Interlinking/InterlinkingLinkInlineContent.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,17 @@ const LinkSelected = ({ url, title }: LinkSelectedProps) => {
7373
transition: background-color 0.2s ease-in-out;
7474
`}
7575
>
76-
<Box $display="inline-block" $css={css`margin-right: 0.3rem; `}>
77-
{emoji ? <Icon iconName={emoji} $size="16px" /> : <SelectedPageIcon width={11.5}/>}
76+
<Box
77+
$display="inline-block"
78+
$css={css`
79+
margin-right: 0.3rem;
80+
`}
81+
>
82+
{emoji ? (
83+
<Icon iconName={emoji} $size="16px" />
84+
) : (
85+
<SelectedPageIcon width={11.5} />
86+
)}
7887
</Box>
7988
<Text $weight="500" spellCheck="false" $size="16px" $display="inline">
8089
{titleWithoutEmoji}

src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx

Lines changed: 88 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,18 @@ import { css } from 'styled-components';
55

66
import { Box, Text } from '@/components';
77
import { useCunninghamTheme } from '@/cunningham';
8-
import { Doc, useDocStore, useTrans } from '@/docs/doc-management';
8+
import {
9+
Doc,
10+
getEmojiAndTitle,
11+
useDocStore,
12+
useTrans,
13+
} from '@/docs/doc-management';
14+
import SimpleFileIcon from '@/features/docs/doc-management/assets/simple-document.svg';
915
import { useDocTitleUpdate } from '@/features/docs/doc-management/hooks/useDocTitleUpdate';
1016
import { useResponsiveStore } from '@/stores';
1117

18+
import { DocIcon } from '../../doc-management/components/DocIcon';
19+
1220
interface DocTitleProps {
1321
doc: Doc;
1422
}
@@ -42,18 +50,26 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
4250
const { isDesktop } = useResponsiveStore();
4351
const { t } = useTranslation();
4452
const { colorsTokens } = useCunninghamTheme();
45-
const [titleDisplay, setTitleDisplay] = useState(doc.title);
53+
const { emoji, titleWithoutEmoji } = getEmojiAndTitle(doc.title ?? '');
54+
const { spacingsTokens } = useCunninghamTheme();
4655

4756
const { untitledDocument } = useTrans();
57+
const [titleDisplay, setTitleDisplay] = useState(titleWithoutEmoji);
4858

4959
const { updateDocTitle } = useDocTitleUpdate();
5060

5161
const handleTitleSubmit = useCallback(
5262
(inputText: string) => {
53-
const sanitizedTitle = updateDocTitle(doc, inputText.trim());
54-
setTitleDisplay(sanitizedTitle);
63+
const sanitizedTitle = updateDocTitle(
64+
doc,
65+
emoji ? `${emoji} ${inputText.trim()}` : inputText.trim(),
66+
);
67+
const { titleWithoutEmoji: sanitizedTitleWithoutEmoji } =
68+
getEmojiAndTitle(sanitizedTitle);
69+
70+
setTitleDisplay(sanitizedTitleWithoutEmoji);
5571
},
56-
[doc, updateDocTitle],
72+
[doc, updateDocTitle, emoji],
5773
);
5874

5975
const handleKeyDown = (e: React.KeyboardEvent) => {
@@ -64,43 +80,75 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
6480
};
6581

6682
useEffect(() => {
67-
setTitleDisplay(doc.title);
68-
}, [doc]);
83+
setTitleDisplay(titleWithoutEmoji);
84+
}, [doc, titleWithoutEmoji]);
6985

7086
return (
71-
<Tooltip content={t('Rename')} aria-hidden={true} placement="top">
72-
<Box
73-
as="span"
74-
role="textbox"
75-
className="--docs--doc-title-input"
76-
contentEditable
77-
defaultValue={titleDisplay || undefined}
78-
onKeyDownCapture={handleKeyDown}
79-
suppressContentEditableWarning={true}
80-
aria-label={`${t('Document title')}`}
81-
aria-multiline={false}
82-
onBlurCapture={(event) =>
83-
handleTitleSubmit(event.target.textContent || '')
84-
}
85-
$color={colorsTokens['greyscale-1000']}
86-
$minHeight="40px"
87-
$padding={{ right: 'big' }}
88-
$css={css`
89-
&[contenteditable='true']:empty:not(:focus):before {
90-
content: '${untitledDocument}';
91-
color: grey;
92-
pointer-events: none;
93-
font-style: italic;
87+
<Box
88+
$direction="row"
89+
$align="flex-end"
90+
$gap={spacingsTokens['s']}
91+
$minHeight="40px"
92+
>
93+
<Tooltip content={t('Document emoji')} aria-hidden={true} placement="top">
94+
<Box
95+
$css={css`
96+
height: 58px;
97+
cursor: pointer;
98+
`}
99+
>
100+
<DocIcon
101+
emojiPicker
102+
docId={doc.id}
103+
title={doc.title}
104+
emoji={emoji}
105+
$size="50px"
106+
defaultIcon={
107+
<SimpleFileIcon
108+
width="50px"
109+
height="50px"
110+
aria-hidden="true"
111+
aria-label={t('Simple document icon')}
112+
color={colorsTokens['primary-500']}
113+
/>
114+
}
115+
/>
116+
</Box>
117+
</Tooltip>
118+
119+
<Tooltip content={t('Rename')} aria-hidden={true} placement="top">
120+
<Box
121+
as="span"
122+
role="textbox"
123+
className="--docs--doc-title-input"
124+
contentEditable
125+
defaultValue={titleDisplay || undefined}
126+
onKeyDownCapture={handleKeyDown}
127+
suppressContentEditableWarning={true}
128+
aria-label={`${t('Document title')}`}
129+
aria-multiline={false}
130+
onBlurCapture={(event) =>
131+
handleTitleSubmit(event.target.textContent || '')
94132
}
95-
font-size: ${isDesktop
96-
? css`var(--c--theme--font--sizes--h2)`
97-
: css`var(--c--theme--font--sizes--sm)`};
98-
font-weight: 700;
99-
outline: none;
100-
`}
101-
>
102-
{titleDisplay}
103-
</Box>
104-
</Tooltip>
133+
$color={colorsTokens['greyscale-1000']}
134+
$padding={{ right: 'big' }}
135+
$css={css`
136+
&[contenteditable='true']:empty:not(:focus):before {
137+
content: '${untitledDocument}';
138+
color: grey;
139+
pointer-events: none;
140+
font-style: italic;
141+
}
142+
font-size: ${isDesktop
143+
? css`var(--c--theme--font--sizes--h2)`
144+
: css`var(--c--theme--font--sizes--sm)`};
145+
font-weight: 700;
146+
outline: none;
147+
`}
148+
>
149+
{titleDisplay}
150+
</Box>
151+
</Tooltip>
152+
</Box>
105153
);
106154
};

src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
KEY_DOC,
2121
KEY_LIST_DOC,
2222
ModalRemoveDoc,
23+
getEmojiAndTitle,
2324
useCopyDocLink,
2425
useCreateFavoriteDoc,
2526
useDeleteFavoriteDoc,
@@ -33,6 +34,7 @@ import {
3334
import { useAnalytics } from '@/libs';
3435
import { useResponsiveStore } from '@/stores';
3536

37+
import { useDocTitleUpdate } from '../../doc-management/hooks/useDocTitleUpdate';
3638
import { useCopyCurrentEditorToClipboard } from '../hooks/useCopyCurrentEditorToClipboard';
3739

3840
const ModalExport = Export?.ModalExport;
@@ -92,6 +94,13 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
9294
});
9395
}, [selectHistoryModal.isOpen, queryClient]);
9496

97+
// Emoji Management
98+
const { emoji } = getEmojiAndTitle(doc.title ?? '');
99+
const { updateDocEmoji } = useDocTitleUpdate();
100+
const removeEmoji = () => {
101+
updateDocEmoji(doc.id, doc.title ?? '', '');
102+
};
103+
95104
const options: DropdownMenuOption[] = [
96105
...(isSmallMobile
97106
? [
@@ -127,6 +136,15 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
127136
},
128137
testId: `docs-actions-${doc.is_favorite ? 'unpin' : 'pin'}-${doc.id}`,
129138
},
139+
...(emoji
140+
? [
141+
{
142+
label: t('Remove emoji'),
143+
icon: 'emoji_emotions',
144+
callback: removeEmoji,
145+
},
146+
]
147+
: []),
130148
{
131149
label: t('Version history'),
132150
icon: 'history',

src/frontend/apps/impress/src/features/docs/doc-management/assets/simple-document.svg

Lines changed: 0 additions & 2 deletions
Loading

src/frontend/apps/impress/src/features/docs/doc-management/components/DocIcon.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ export const DocIcon = ({
2929
onEmojiUpdate,
3030
...textProps
3131
}: DocIconProps) => {
32-
if (!emojiPicker && !emoji) {
33-
return defaultIcon;
34-
}
35-
3632
const { t } = useTranslation();
3733
const { updateDocEmoji } = useDocTitleUpdate();
3834

@@ -44,6 +40,10 @@ export const DocIcon = ({
4440
left: number;
4541
}>({ top: 0, left: 0 });
4642

43+
if (!emojiPicker && !emoji) {
44+
return defaultIcon;
45+
}
46+
4747
const toggleEmojiPicker = (e: React.MouseEvent) => {
4848
if (emojiPicker) {
4949
e.stopPropagation();

src/frontend/apps/impress/src/features/docs/doc-management/components/SimpleDocItem.tsx

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import { useResponsiveStore } from '@/stores';
1010
import PinnedDocumentIcon from '../assets/pinned-document.svg';
1111
import SimpleFileIcon from '../assets/simple-document.svg';
1212

13-
import { DocIcon } from './DocIcon';
14-
1513
const ItemTextCss = css`
1614
overflow: hidden;
1715
text-overflow: ellipsis;
@@ -63,16 +61,12 @@ export const SimpleDocItem = ({
6361
color={colorsTokens['primary-500']}
6462
/>
6563
) : (
66-
<DocIcon
67-
defaultIcon={
68-
<SimpleFileIcon
69-
aria-hidden="true"
70-
aria-label={t('Simple document icon')}
71-
color={colorsTokens['primary-500']}
72-
/>
73-
}
74-
$size="25px"
75-
docId={doc.id}
64+
<SimpleFileIcon
65+
width="32px"
66+
height="32px"
67+
aria-hidden="true"
68+
aria-label={t('Simple document icon')}
69+
color={colorsTokens['primary-500']}
7670
/>
7771
)}
7872
</Box>

0 commit comments

Comments
 (0)