From a53a1640e15c31048e4aa158b6cd1e1fd27dc664 Mon Sep 17 00:00:00 2001 From: Maksim Sviridov Date: Fri, 24 Nov 2023 15:04:35 +0300 Subject: [PATCH] feat(GoalCriteria): make criteria form inside popup --- src/components/GoalActivityFeed.tsx | 42 +-- src/components/GoalCriteria/GoalCriteria.tsx | 273 ++++++++++--------- 2 files changed, 166 insertions(+), 149 deletions(-) diff --git a/src/components/GoalActivityFeed.tsx b/src/components/GoalActivityFeed.tsx index 80ff39986..85b5c22d0 100644 --- a/src/components/GoalActivityFeed.tsx +++ b/src/components/GoalActivityFeed.tsx @@ -1,5 +1,5 @@ import { nullable } from '@taskany/bricks'; -import { forwardRef, useCallback } from 'react'; +import { forwardRef, useCallback, useMemo } from 'react'; import dynamic from 'next/dynamic'; import { ModalEvent, dispatchModalEvent } from '../utils/dispatchModal'; @@ -155,6 +155,28 @@ export const GoalActivityFeed = forwardRef { + if (goal._criteria?.length) { + return goal._criteria.map((criteria) => ({ + id: criteria.id, + title: criteria.title, + weight: criteria.weight, + criteriaGoal: + criteria.criteriaGoal != null + ? { + id: criteria.criteriaGoal.id, + title: criteria.criteriaGoal.title, + stateColor: criteria.criteriaGoal.state?.hue || 0, + href: routes.goal(criteria.criteriaGoal._shortId), + } + : null, + isDone: criteria.isDone, + })); + } + + return []; + }, [goal._criteria]); + return ( <> - {nullable(goal._criteria.length || goal._isEditable, () => ( + {nullable(criteriaList || goal._isEditable, () => ( ({ - id: criteria.id, - title: criteria.title, - weight: criteria.weight, - criteriaGoal: - criteria.criteriaGoal != null - ? { - id: criteria.criteriaGoal.id, - title: criteria.criteriaGoal.title, - stateColor: criteria.criteriaGoal.state?.hue || 0, - href: routes.goal(criteria.criteriaGoal._shortId), - } - : null, - isDone: criteria.isDone, - }))} + list={criteriaList} /> ))} {nullable(lastStateComment, (value) => ( diff --git a/src/components/GoalCriteria/GoalCriteria.tsx b/src/components/GoalCriteria/GoalCriteria.tsx index a09317e48..6c448fa23 100644 --- a/src/components/GoalCriteria/GoalCriteria.tsx +++ b/src/components/GoalCriteria/GoalCriteria.tsx @@ -1,6 +1,16 @@ import React, { useCallback, useMemo, useRef, useState } from 'react'; import styled, { css } from 'styled-components'; -import { Text, nullable, Table, MenuItem, TableRow, TableCell, Dropdown, useClickOutside } from '@taskany/bricks'; +import { + Text, + nullable, + Table, + MenuItem, + TableRow, + TableCell, + Dropdown, + useClickOutside, + Popup, +} from '@taskany/bricks'; import { IconTargetOutline, IconCircleOutline, @@ -100,10 +110,6 @@ const StyledTextHeading = styled(Text)` border-bottom: 1px solid ${gray4}; `; -const StyledIconTableCell = styled(TableCell)` - padding-top: calc(${gapXs} + 5px); // offset by input vertical center -`; - const StyledBadge = styled(Badge)` padding: 0; `; @@ -112,22 +118,14 @@ const StyledGoalBadge = styled(GoalBadge)` padding: 0; `; -const useGoalSuggestions = (value = '') => { - const [query, setQuery] = useState(() => value); +const StyledCriteriaRow = styled(TableRow)<{ active: boolean }>` + padding: 2.5px 5px; + margin: 0 -5px; - const { data: suggestions } = trpc.goal.suggestions.useQuery( - { - input: query, - limit: 5, - }, - { - staleTime: 0, - cacheTime: 0, - }, - ); - - return [suggestions, setQuery] as [typeof suggestions, React.Dispatch>]; -}; + background-color: ${({ active }) => (active ? gray4 : 'unset')}; + border-radius: ${radiusS}; + transition: background-color 0.3s ease; +`; type CriteriaFormData = NonNullable['values']>; type CriteriaValidityData = React.ComponentProps['validityData']; @@ -178,6 +176,7 @@ const CriteriaItem: React.FC = ({ const { criteriaGoal, title } = criteria; const [mode, setMode] = useState<'view' | 'edit'>(() => calculateModeCriteria(criteria)); const formRef = useRef(null); + const popupRef = useRef(null); useClickOutside(formRef, () => { setMode('view'); @@ -228,73 +227,74 @@ const CriteriaItem: React.FC = ({ }, [criteria.title, onCancel]); return ( - - {nullable( - mode === 'edit', - () => ( - <> - - - - - {renderForm({ onEditCancel: handleCancel, ref: formRef })} - - - ), - <> - - {nullable( - criteriaGoal, - (goal) => ( - onClick(criteria)} + <> + + + {nullable( + criteriaGoal, + (goal) => ( + onClick(criteria)} + /> + ), + onUpdateState({ ...criteria, isDone: !criteria.isDone })} /> - ), - onUpdateState({ ...criteria, isDone: !criteria.isDone })} - /> - } - text={title} - />, + } + text={title} + />, + )} + + + {nullable(criteria.weight > 0, () => ( + + {criteria.weight} + + ))} + + + } + placement="right" + items={availableActions} + renderItem={(props) => ( + + {props.item.label} + )} - - - {nullable(criteria.weight > 0, () => ( - - {criteria.weight} - - ))} - - - } - placement="right" - items={availableActions} - renderItem={(props) => ( - - {props.item.label} - - )} - /> - - , - )} - + /> + + + {nullable(mode === 'edit', () => ( + + {renderForm({ onEditCancel: handleCancel, ref: formRef })} + + ))} + ); }; @@ -328,8 +328,19 @@ export const GoalCriteria: React.FC = ({ onConvertToGoal, validateGoalCriteriaBindings, }) => { - const [suggestions = [], setQuery] = useGoalSuggestions(); const [addingCriteria, setAddingCriteria] = useState(false); + const [query, setQuery] = useState(''); + + const { data: suggestions = [] } = trpc.goal.suggestions.useQuery( + { + input: query, + limit: 5, + }, + { + staleTime: 0, + cacheTime: 0, + }, + ); const sortedCriteriaItems = useMemo(() => { const sorted = list.reduce>( @@ -428,52 +439,50 @@ export const GoalCriteria: React.FC = ({ onClick={onGoalClick} canEdit={canEdit} renderForm={(props) => ( - - 0 ? String(criteria.weight) : '', - } - : undefined - } - validityData={{ - title: dataForValidate.title.filter((title) => title !== criteria.title), - sumOfCriteria: dataForValidate.sumOfCriteria - criteria.weight, - }} - items={suggestions?.map((goal) => ({ - id: goal.id, - title: goal.title, - stateColor: goal.state?.hue, - }))} - onSubmit={handleFormSubmit(props.onEditCancel)} - onInputChange={(val = '') => setQuery(val)} - onCancel={props.onEditCancel} - renderItem={(props) => ( - - - - )} - validateBindingsFor={validateGoalCriteriaBindings} - /> - + 0 ? String(criteria.weight) : '', + } + : undefined + } + validityData={{ + title: dataForValidate.title.filter((title) => title !== criteria.title), + sumOfCriteria: dataForValidate.sumOfCriteria - criteria.weight, + }} + items={suggestions?.map((goal) => ({ + id: goal.id, + title: goal.title, + stateColor: goal.state?.hue, + }))} + onSubmit={handleFormSubmit(props.onEditCancel)} + onInputChange={(val = '') => setQuery(val)} + onCancel={props.onEditCancel} + renderItem={(props) => ( + + + + )} + validateBindingsFor={validateGoalCriteriaBindings} + /> )} /> ))}