-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(GoalParentDropdown): create component
- Loading branch information
1 parent
ec2b249
commit 73794f9
Showing
5 changed files
with
158 additions
and
0 deletions.
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
src/components/GoalParentDropdown/GoalParentDropdown.i18n/en.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"Suggestions": "", | ||
"Create project": "" | ||
} |
17 changes: 17 additions & 0 deletions
17
src/components/GoalParentDropdown/GoalParentDropdown.i18n/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/* eslint-disable */ | ||
// Do not edit, use generator to update | ||
import { i18n, fmt, I18nLangSet } from 'easy-typed-intl'; | ||
import getLang from '../../../utils/getLang'; | ||
|
||
import ru from './ru.json'; | ||
import en from './en.json'; | ||
|
||
export type I18nKey = keyof typeof ru & keyof typeof en; | ||
type I18nLang = 'ru' | 'en'; | ||
|
||
const keyset: I18nLangSet<I18nKey> = {}; | ||
|
||
keyset['ru'] = ru; | ||
keyset['en'] = en; | ||
|
||
export const tr = i18n<I18nLang, I18nKey>(keyset, fmt, getLang); |
4 changes: 4 additions & 0 deletions
4
src/components/GoalParentDropdown/GoalParentDropdown.i18n/ru.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"Suggestions": "Предложения", | ||
"Create project": "Новый проект" | ||
} |
28 changes: 28 additions & 0 deletions
28
src/components/GoalParentDropdown/GoalParentDropdown.module.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
.DropdownTrigger { | ||
min-width: 180px; | ||
} | ||
|
||
.DropdownPanelItem { | ||
display: flex; | ||
align-items: center; | ||
justify-content: space-between; | ||
gap: var(--gap-s); | ||
width: 100%; | ||
} | ||
|
||
.DropdownPanelItem_id { | ||
color: var(--text-secondary); | ||
} | ||
|
||
.DropdownTriggerValue { | ||
display: inline-block; | ||
white-space: nowrap; | ||
width: 130px; | ||
text-overflow: ellipsis; | ||
overflow: hidden; | ||
} | ||
|
||
.CreateProjectButton { | ||
margin-bottom: var(--gap-xs); | ||
color: var(--text-secondary); | ||
} |
105 changes: 105 additions & 0 deletions
105
src/components/GoalParentDropdown/GoalParentDropdown.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import { Text, Button } from '@taskany/bricks/harmony'; | ||
import { useState, useEffect, useMemo, ComponentProps, useCallback } from 'react'; | ||
import { nullable } from '@taskany/bricks'; | ||
import { IconAddOutline } from '@taskany/icons'; | ||
|
||
import { useLocalStorage } from '../../hooks/useLocalStorage'; | ||
import { trpc } from '../../utils/trpcClient'; | ||
import { Dropdown, DropdownTrigger, DropdownPanel } from '../Dropdown/Dropdown'; | ||
import { ModalEvent, dispatchModalEvent } from '../../utils/dispatchModal'; | ||
|
||
import s from './GoalParentDropdown.module.css'; | ||
import { tr } from './GoalParentDropdown.i18n'; | ||
|
||
interface GoalParentDropdownProps { | ||
error?: ComponentProps<typeof DropdownTrigger>['error']; | ||
label?: ComponentProps<typeof DropdownTrigger>['label']; | ||
value?: { id: string; title: string }; | ||
query?: string; | ||
placeholder?: string; | ||
disabled?: boolean; | ||
onChange?: (project: { id: string; title: string }) => void; | ||
} | ||
|
||
export const GoalParentDropdown = ({ | ||
label, | ||
query = '', | ||
value, | ||
placeholder, | ||
disabled, | ||
onChange, | ||
...props | ||
}: GoalParentDropdownProps) => { | ||
const [inputState, setInputState] = useState(query); | ||
const [recentProjectsCache] = useLocalStorage('recentProjectsCache', {}); | ||
|
||
useEffect(() => { | ||
setInputState(query); | ||
}, [query]); | ||
|
||
const { data } = trpc.project.suggestions.useQuery( | ||
{ | ||
query: inputState, | ||
}, | ||
{ | ||
enabled: inputState.length >= 2, | ||
cacheTime: 0, | ||
staleTime: 0, | ||
}, | ||
); | ||
|
||
const recentProjects = Object.values(recentProjectsCache) | ||
.sort((a, b) => b.rate - a.rate) | ||
.slice(0, 10) // top 10 | ||
.map((p) => p.cache); | ||
|
||
const items = useMemo<typeof recentProjects | typeof data>( | ||
() => (data && data?.length > 0 ? data : recentProjects), | ||
[data, recentProjects], | ||
); | ||
|
||
const handleCreateProject = useCallback(() => { | ||
dispatchModalEvent(ModalEvent.GoalCreateModal)(); | ||
dispatchModalEvent(ModalEvent.ProjectCreateModal)(); | ||
}, []); | ||
|
||
return ( | ||
<Dropdown> | ||
<DropdownTrigger label={label} className={s.DropdownTrigger} {...props} readOnly={disabled}> | ||
{nullable(value, ({ title }) => ( | ||
<Text size="s" as="span" className={s.DropdownTriggerValue} title={title}> | ||
{title} | ||
</Text> | ||
))} | ||
</DropdownTrigger> | ||
<DropdownPanel | ||
width={320} | ||
value={value} | ||
title={tr('Suggestions')} | ||
items={items} | ||
placeholder={placeholder} | ||
inputState={inputState} | ||
setInputState={setInputState} | ||
onChange={onChange} | ||
renderItem={(props) => ( | ||
<div className={s.DropdownPanelItem}> | ||
<Text size="s" weight="bold" as="span"> | ||
{props.item.title} | ||
</Text> | ||
<Text size="s" as="span" className={s.DropdownPanelItem_id}> | ||
{props.item.id} | ||
</Text> | ||
</div> | ||
)} | ||
> | ||
<Button | ||
text={tr('Create project')} | ||
view="ghost" | ||
iconLeft={<IconAddOutline size="s" />} | ||
onClick={handleCreateProject} | ||
className={s.CreateProjectButton} | ||
/> | ||
</DropdownPanel> | ||
</Dropdown> | ||
); | ||
}; |