feature: add due dates to tasks
This commit is contained in:
@ -111,5 +111,19 @@ export default createGlobalStyle`
|
||||
resize: none;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #262c49;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #7367f0;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
${mixin.placeholderColor(color.textLight)}
|
||||
`;
|
||||
|
@ -17,6 +17,7 @@ const theme: DefaultTheme = {
|
||||
primary: '194, 198, 220',
|
||||
secondary: '255, 255, 255',
|
||||
},
|
||||
border: '65, 69, 97',
|
||||
bg: {
|
||||
primary: '16, 22, 58',
|
||||
secondary: '38, 44, 73',
|
||||
|
@ -4,9 +4,15 @@ import TaskDetails from 'shared/components/TaskDetails';
|
||||
import PopupMenu, { Popup, usePopup } from 'shared/components/PopupMenu';
|
||||
import MemberManager from 'shared/components/MemberManager';
|
||||
import { useRouteMatch, useHistory } from 'react-router';
|
||||
import { useFindTaskQuery, useAssignTaskMutation, useUnassignTaskMutation } from 'shared/generated/graphql';
|
||||
import {
|
||||
useFindTaskQuery,
|
||||
useUpdateTaskDueDateMutation,
|
||||
useAssignTaskMutation,
|
||||
useUnassignTaskMutation,
|
||||
} from 'shared/generated/graphql';
|
||||
import UserIDContext from 'App/context';
|
||||
import MiniProfile from 'shared/components/MiniProfile';
|
||||
import DueDateManager from 'shared/components/DueDateManager';
|
||||
|
||||
type DetailsProps = {
|
||||
taskID: string;
|
||||
@ -32,12 +38,18 @@ const Details: React.FC<DetailsProps> = ({
|
||||
refreshCache,
|
||||
}) => {
|
||||
const { userID } = useContext(UserIDContext);
|
||||
const { showPopup } = usePopup();
|
||||
const { showPopup, hidePopup } = usePopup();
|
||||
const history = useHistory();
|
||||
const match = useRouteMatch();
|
||||
const [currentMemberTask, setCurrentMemberTask] = useState('');
|
||||
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
|
||||
const { loading, data, refetch } = useFindTaskQuery({ variables: { taskID } });
|
||||
const [updateTaskDueDate] = useUpdateTaskDueDateMutation({
|
||||
onCompleted: () => {
|
||||
refetch();
|
||||
refreshCache();
|
||||
},
|
||||
});
|
||||
const [assignTask] = useAssignTaskMutation({
|
||||
onCompleted: () => {
|
||||
refetch();
|
||||
@ -56,6 +68,7 @@ const Details: React.FC<DetailsProps> = ({
|
||||
if (!data) {
|
||||
return <div>loading</div>;
|
||||
}
|
||||
console.log(data.findTask);
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
@ -108,6 +121,29 @@ const Details: React.FC<DetailsProps> = ({
|
||||
);
|
||||
}}
|
||||
onOpenAddLabelPopup={onOpenAddLabelPopup}
|
||||
onOpenDueDatePopop={(task, $targetRef) => {
|
||||
showPopup(
|
||||
$targetRef,
|
||||
|
||||
<Popup
|
||||
title={'Change Due Date'}
|
||||
tab={0}
|
||||
onClose={() => {
|
||||
hidePopup();
|
||||
}}
|
||||
>
|
||||
<DueDateManager
|
||||
task={task}
|
||||
onDueDateChange={(t, newDueDate) => {
|
||||
console.log(`${newDueDate}`);
|
||||
updateTaskDueDate({ variables: { taskID: t.id, dueDate: newDueDate } });
|
||||
hidePopup();
|
||||
}}
|
||||
onCancel={() => {}}
|
||||
/>
|
||||
</Popup>,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
@ -229,14 +229,14 @@ const ProjectAction = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 15px;
|
||||
color: var(--color-text);
|
||||
color: rgba(${props => props.theme.colors.text.primary});
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--color-text-hover);
|
||||
color: rgba(${props => props.theme.colors.text.secondary});
|
||||
}
|
||||
`;
|
||||
|
||||
@ -490,15 +490,15 @@ const Project = () => {
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Tags size={13} color="var(--color-icon)" />
|
||||
<Tags width={13} height={13} />
|
||||
<ProjectActionText>Labels</ProjectActionText>
|
||||
</ProjectAction>
|
||||
<ProjectAction>
|
||||
<ToggleOn size={13} color="var(--color-icon)" />
|
||||
<ToggleOn width={13} height={13} />
|
||||
<ProjectActionText>Fields</ProjectActionText>
|
||||
</ProjectAction>
|
||||
<ProjectAction>
|
||||
<Bolt size={13} color="var(--color-icon)" />
|
||||
<Bolt width={13} height={13} />
|
||||
<ProjectActionText>Rules</ProjectActionText>
|
||||
</ProjectAction>
|
||||
</ProjectActions>
|
||||
|
4
web/src/citadel.d.ts
vendored
4
web/src/citadel.d.ts
vendored
@ -32,8 +32,8 @@ type LoginFormData = {
|
||||
};
|
||||
|
||||
type DueDateFormData = {
|
||||
endDate: Date;
|
||||
endTime: string | null;
|
||||
endDate: string;
|
||||
endTime: string;
|
||||
};
|
||||
|
||||
type LoginProps = {
|
||||
|
1
web/src/projects.d.ts
vendored
1
web/src/projects.d.ts
vendored
@ -35,6 +35,7 @@ type Task = {
|
||||
taskGroup: InnerTaskGroup;
|
||||
name: string;
|
||||
position: number;
|
||||
dueDate?: string;
|
||||
labels: TaskLabel[];
|
||||
description?: string | null;
|
||||
assigned?: Array<TaskUser>;
|
||||
|
@ -107,6 +107,7 @@ type ButtonProps = {
|
||||
variant?: 'filled' | 'outline' | 'flat' | 'lineDown' | 'gradient' | 'relief';
|
||||
color?: 'primary' | 'danger' | 'success' | 'warning' | 'dark';
|
||||
disabled?: boolean;
|
||||
type?: 'button' | 'submit';
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
@ -116,6 +117,7 @@ const Button: React.FC<ButtonProps> = ({
|
||||
fontSize = '14px',
|
||||
color = 'primary',
|
||||
variant = 'filled',
|
||||
type = 'button',
|
||||
onClick,
|
||||
className,
|
||||
children,
|
||||
@ -128,38 +130,38 @@ const Button: React.FC<ButtonProps> = ({
|
||||
switch (variant) {
|
||||
case 'filled':
|
||||
return (
|
||||
<Filled onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||
<Filled type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||
<Text fontSize={fontSize}>{children}</Text>
|
||||
</Filled>
|
||||
);
|
||||
case 'outline':
|
||||
return (
|
||||
<Outline onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||
<Outline type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||
<Text fontSize={fontSize}>{children}</Text>
|
||||
</Outline>
|
||||
);
|
||||
case 'flat':
|
||||
return (
|
||||
<Flat onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||
<Flat type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||
<Text fontSize={fontSize}>{children}</Text>
|
||||
</Flat>
|
||||
);
|
||||
case 'lineDown':
|
||||
return (
|
||||
<LineDown onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||
<LineDown type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||
<Text fontSize={fontSize}>{children}</Text>
|
||||
<LineX color={color} />
|
||||
</LineDown>
|
||||
);
|
||||
case 'gradient':
|
||||
return (
|
||||
<Gradient onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||
<Gradient type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||
<Text fontSize={fontSize}>{children}</Text>
|
||||
</Gradient>
|
||||
);
|
||||
case 'relief':
|
||||
return (
|
||||
<Relief onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||
<Relief type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||
<Text fontSize={fontSize}>{children}</Text>
|
||||
</Relief>
|
||||
);
|
||||
|
@ -22,7 +22,7 @@ export const EditorTextarea = styled(TextareaAutosize)`
|
||||
padding: 0;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
color: var(--color-input-text-focus);
|
||||
color: rgba(${props => props.theme.colors.text.secondary});
|
||||
&:focus {
|
||||
border: none;
|
||||
outline: none;
|
||||
@ -84,7 +84,7 @@ export const ListCardContainer = styled.div<{ isActive: boolean; editable: boole
|
||||
position: relative;
|
||||
|
||||
background-color: ${props =>
|
||||
props.isActive && !props.editable ? mixin.darken('#262c49', 0.1) : 'var(--color-background)'};
|
||||
props.isActive && !props.editable ? mixin.darken('#262c49', 0.1) : `rgba(${props.theme.colors.bg.secondary})`};
|
||||
`;
|
||||
|
||||
export const ListCardInnerContainer = styled.div`
|
||||
@ -145,7 +145,7 @@ export const CardTitle = styled.span`
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
word-wrap: break-word;
|
||||
color: var(--color-text);
|
||||
color: rgba(${props => props.theme.colors.text.primary});
|
||||
`;
|
||||
|
||||
export const CardMembers = styled.div`
|
||||
|
@ -1,12 +1,16 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import DueDateManager from '.';
|
||||
import BaseStyles from 'App/BaseStyles';
|
||||
import NormalizeStyles from 'App/NormalizeStyles';
|
||||
import { theme } from 'App/ThemeStyles';
|
||||
import styled, { ThemeProvider } from 'styled-components';
|
||||
import { Popup } from '../PopupMenu';
|
||||
import styled from 'styled-components';
|
||||
import DueDateManager from '.';
|
||||
|
||||
const PopupWrapper = styled.div`
|
||||
width: 300px;
|
||||
width: 310px;
|
||||
`;
|
||||
|
||||
export default {
|
||||
component: DueDateManager,
|
||||
title: 'DueDateManager',
|
||||
@ -20,44 +24,50 @@ export default {
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<PopupWrapper>
|
||||
<Popup title={null} tab={0}>
|
||||
<DueDateManager
|
||||
task={{
|
||||
id: '1',
|
||||
taskGroup: { name: 'General', id: '1', position: 1 },
|
||||
name: 'Hello, world',
|
||||
position: 1,
|
||||
labels: [
|
||||
{
|
||||
id: 'soft-skills',
|
||||
assignedDate: new Date().toString(),
|
||||
projectLabel: {
|
||||
createdDate: new Date().toString(),
|
||||
id: 'label-soft-skills',
|
||||
name: 'Soft Skills',
|
||||
labelColor: {
|
||||
id: '1',
|
||||
name: 'white',
|
||||
colorHex: '#fff',
|
||||
position: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
description: 'hello!',
|
||||
assigned: [
|
||||
{
|
||||
<>
|
||||
<NormalizeStyles />
|
||||
<BaseStyles />
|
||||
<ThemeProvider theme={theme}>
|
||||
<PopupWrapper>
|
||||
<Popup title={null} tab={0}>
|
||||
<DueDateManager
|
||||
task={{
|
||||
id: '1',
|
||||
profileIcon: { url: null, initials: null, bgColor: null },
|
||||
fullName: 'Jordan Knott',
|
||||
},
|
||||
],
|
||||
}}
|
||||
onCancel={action('cancel')}
|
||||
onDueDateChange={action('due date change')}
|
||||
/>
|
||||
</Popup>
|
||||
</PopupWrapper>
|
||||
taskGroup: { name: 'General', id: '1', position: 1 },
|
||||
name: 'Hello, world',
|
||||
position: 1,
|
||||
labels: [
|
||||
{
|
||||
id: 'soft-skills',
|
||||
assignedDate: new Date().toString(),
|
||||
projectLabel: {
|
||||
createdDate: new Date().toString(),
|
||||
id: 'label-soft-skills',
|
||||
name: 'Soft Skills',
|
||||
labelColor: {
|
||||
id: '1',
|
||||
name: 'white',
|
||||
colorHex: '#fff',
|
||||
position: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
description: 'hello!',
|
||||
assigned: [
|
||||
{
|
||||
id: '1',
|
||||
profileIcon: { url: null, initials: null, bgColor: null },
|
||||
fullName: 'Jordan Knott',
|
||||
},
|
||||
],
|
||||
}}
|
||||
onCancel={action('cancel')}
|
||||
onDueDateChange={action('due date change')}
|
||||
/>
|
||||
</Popup>
|
||||
</PopupWrapper>
|
||||
</ThemeProvider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,7 @@
|
||||
import styled from 'styled-components';
|
||||
import Button from 'shared/components/Button';
|
||||
import { mixin } from 'shared/utils/styles';
|
||||
import Input from 'shared/components/Input';
|
||||
|
||||
export const Wrapper = styled.div`
|
||||
display: flex
|
||||
@ -8,7 +10,37 @@ display: flex
|
||||
background: #262c49;
|
||||
font-family: 'Droid Sans', sans-serif;
|
||||
border: none;
|
||||
}
|
||||
& .react-datepicker__triangle {
|
||||
display: none;
|
||||
}
|
||||
& .react-datepicker-popper {
|
||||
z-index: 10000;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
& .react-datepicker-time__header {
|
||||
color: rgba(${props => props.theme.colors.text.primary});
|
||||
}
|
||||
& .react-datepicker__time-list-item {
|
||||
color: rgba(${props => props.theme.colors.text.primary});
|
||||
}
|
||||
& .react-datepicker__time-container .react-datepicker__time
|
||||
.react-datepicker__time-box ul.react-datepicker__time-list
|
||||
li.react-datepicker__time-list-item:hover {
|
||||
color: rgba(${props => props.theme.colors.text.secondary});
|
||||
background: rgba(${props => props.theme.colors.bg.secondary});
|
||||
}
|
||||
& .react-datepicker__time-container .react-datepicker__time {
|
||||
background: rgba(${props => props.theme.colors.bg.primary});
|
||||
}
|
||||
& .react-datepicker--time-only {
|
||||
background: rgba(${props => props.theme.colors.bg.primary});
|
||||
border: 1px solid rgba(${props => props.theme.colors.border});
|
||||
}
|
||||
|
||||
& .react-datepicker * {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
& .react-datepicker__day-name {
|
||||
color: #c2c6dc;
|
||||
@ -56,6 +88,9 @@ display: flex
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
& .react-datepicker__header--time {
|
||||
border-bottom: 1px solid rgba(${props => props.theme.colors.border});
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
@ -66,21 +101,10 @@ export const DueDatePickerWrapper = styled.div`
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
export const ConfirmAddDueDate = styled.div`
|
||||
background-color: #5aac44;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
color: #fff;
|
||||
export const ConfirmAddDueDate = styled(Button)`
|
||||
float: left;
|
||||
margin: 0 4px 0 0;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
padding: 6px 12px;
|
||||
text-align: center;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
`;
|
||||
|
||||
export const CancelDueDate = styled.div`
|
||||
@ -92,6 +116,12 @@ export const CancelDueDate = styled.div`
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
export const DueDateInput = styled(Input)`
|
||||
margin-top: 15px;
|
||||
margin-bottom: 5px;
|
||||
padding-right: 10px;
|
||||
`;
|
||||
|
||||
export const ActionWrapper = styled.div`
|
||||
padding-top: 8px;
|
||||
width: 100%;
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, forwardRef } from 'react';
|
||||
import moment from 'moment';
|
||||
import styled from 'styled-components';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import { Cross } from 'shared/icons';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { Wrapper, ActionWrapper, DueDatePickerWrapper, ConfirmAddDueDate, CancelDueDate } from './Styles';
|
||||
import { Wrapper, ActionWrapper, DueDateInput, DueDatePickerWrapper, ConfirmAddDueDate, CancelDueDate } from './Styles';
|
||||
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import { getYear, getMonth } from 'date-fns';
|
||||
@ -17,6 +17,14 @@ type DueDateManagerProps = {
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
const Form = styled.form`
|
||||
padding-top: 25px;
|
||||
`;
|
||||
|
||||
const FormField = styled.div`
|
||||
width: 50%;
|
||||
display: inline-block;
|
||||
`;
|
||||
const HeaderSelectLabel = styled.div`
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
@ -104,12 +112,17 @@ const HeaderActions = styled.div`
|
||||
const DueDateManager: React.FC<DueDateManagerProps> = ({ task, onDueDateChange, onCancel }) => {
|
||||
const now = moment();
|
||||
const [textStartDate, setTextStartDate] = useState(now.format('YYYY-MM-DD'));
|
||||
|
||||
const [startDate, setStartDate] = useState(new Date());
|
||||
useEffect(() => {
|
||||
setTextStartDate(moment(startDate).format('YYYY-MM-DD'));
|
||||
}, [startDate]);
|
||||
|
||||
const [textEndTime, setTextEndTime] = useState(now.format('h:mm A'));
|
||||
const [endTime, setEndTime] = useState(now.toDate());
|
||||
useEffect(() => {
|
||||
setTextEndTime(moment(endTime).format('h:mm A'));
|
||||
}, [endTime]);
|
||||
|
||||
const years = _.range(2010, getYear(new Date()) + 10, 1);
|
||||
const months = [
|
||||
'January',
|
||||
@ -125,85 +138,143 @@ const DueDateManager: React.FC<DueDateManagerProps> = ({ task, onDueDateChange,
|
||||
'November',
|
||||
'December',
|
||||
];
|
||||
const { register, handleSubmit, errors, setError, formState } = useForm<DueDateFormData>();
|
||||
const { register, handleSubmit, errors, setValue, setError, formState } = useForm<DueDateFormData>();
|
||||
const saveDueDate = (data: any) => {
|
||||
console.log(data);
|
||||
const newDate = moment(`${data.endDate} ${data.endTime}`, 'YYYY-MM-DD h:mm A');
|
||||
if (newDate.isValid()) {
|
||||
onDueDateChange(task, newDate.toDate());
|
||||
}
|
||||
};
|
||||
console.log(errors);
|
||||
register({ name: 'endTime' }, { required: 'End time is required' });
|
||||
useEffect(() => {
|
||||
setValue('endTime', now.format('h:mm A'));
|
||||
}, []);
|
||||
const CustomTimeInput = forwardRef(({ value, onClick }: any, $ref: any) => {
|
||||
return (
|
||||
<DueDateInput
|
||||
id="endTime"
|
||||
name="endTime"
|
||||
ref={$ref}
|
||||
onChange={e => {
|
||||
console.log(`onCahnge ${e.currentTarget.value}`);
|
||||
setTextEndTime(e.currentTarget.value);
|
||||
setValue('endTime', e.currentTarget.value);
|
||||
}}
|
||||
width="100%"
|
||||
variant="alternate"
|
||||
label="Date"
|
||||
onClick={onClick}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
});
|
||||
console.log(`textStartDate ${textStartDate}`);
|
||||
return (
|
||||
<Wrapper>
|
||||
<form>
|
||||
<input
|
||||
type="text"
|
||||
id="endDate"
|
||||
name="endDate"
|
||||
onChange={e => {
|
||||
setTextStartDate(e.currentTarget.value);
|
||||
}}
|
||||
value={textStartDate}
|
||||
ref={register({
|
||||
required: 'End due date is required.',
|
||||
validate: value => {
|
||||
const isValid = moment(value, 'YYYY-MM-DD').isValid();
|
||||
console.log(`${value} - ${isValid}`);
|
||||
return isValid;
|
||||
},
|
||||
})}
|
||||
/>
|
||||
</form>
|
||||
<DueDatePickerWrapper>
|
||||
<DatePicker
|
||||
useWeekdaysShort
|
||||
renderCustomHeader={({
|
||||
date,
|
||||
changeYear,
|
||||
changeMonth,
|
||||
decreaseMonth,
|
||||
increaseMonth,
|
||||
prevMonthButtonDisabled,
|
||||
nextMonthButtonDisabled,
|
||||
}) => (
|
||||
<HeaderActions>
|
||||
<HeaderButton onClick={decreaseMonth} disabled={prevMonthButtonDisabled}>
|
||||
Prev
|
||||
</HeaderButton>
|
||||
<HeaderSelectLabel>
|
||||
{months[date.getMonth()]}
|
||||
<HeaderSelect value={getYear(date)} onChange={({ target: { value } }) => changeYear(parseInt(value))}>
|
||||
{years.map(option => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</HeaderSelect>
|
||||
</HeaderSelectLabel>
|
||||
<HeaderSelectLabel>
|
||||
{date.getFullYear()}
|
||||
<HeaderSelect
|
||||
value={months[getMonth(date)]}
|
||||
onChange={({ target: { value } }) => changeMonth(months.indexOf(value))}
|
||||
>
|
||||
{months.map(option => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</HeaderSelect>
|
||||
</HeaderSelectLabel>
|
||||
<Form onSubmit={handleSubmit(saveDueDate)}>
|
||||
<FormField>
|
||||
<DueDateInput
|
||||
id="endDate"
|
||||
name="endDate"
|
||||
width="100%"
|
||||
variant="alternate"
|
||||
label="Date"
|
||||
onChange={e => {
|
||||
setTextStartDate(e.currentTarget.value);
|
||||
}}
|
||||
value={textStartDate}
|
||||
ref={register({
|
||||
required: 'End date is required.',
|
||||
})}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField>
|
||||
<DatePicker
|
||||
selected={endTime}
|
||||
onChange={date => {
|
||||
const changedDate = moment(date ?? new Date());
|
||||
console.log(`changed ${date}`);
|
||||
setEndTime(changedDate.toDate());
|
||||
setValue('endTime', changedDate.format('h:mm A'));
|
||||
}}
|
||||
showTimeSelect
|
||||
showTimeSelectOnly
|
||||
timeIntervals={15}
|
||||
timeCaption="Time"
|
||||
dateFormat="h:mm aa"
|
||||
customInput={<CustomTimeInput />}
|
||||
/>
|
||||
</FormField>
|
||||
<DueDatePickerWrapper>
|
||||
<DatePicker
|
||||
useWeekdaysShort
|
||||
renderCustomHeader={({
|
||||
date,
|
||||
changeYear,
|
||||
changeMonth,
|
||||
decreaseMonth,
|
||||
increaseMonth,
|
||||
prevMonthButtonDisabled,
|
||||
nextMonthButtonDisabled,
|
||||
}) => (
|
||||
<HeaderActions>
|
||||
<HeaderButton onClick={decreaseMonth} disabled={prevMonthButtonDisabled}>
|
||||
Prev
|
||||
</HeaderButton>
|
||||
<HeaderSelectLabel>
|
||||
{months[date.getMonth()]}
|
||||
<HeaderSelect value={getYear(date)} onChange={({ target: { value } }) => changeYear(parseInt(value))}>
|
||||
{years.map(option => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</HeaderSelect>
|
||||
</HeaderSelectLabel>
|
||||
<HeaderSelectLabel>
|
||||
{date.getFullYear()}
|
||||
<HeaderSelect
|
||||
value={months[getMonth(date)]}
|
||||
onChange={({ target: { value } }) => changeMonth(months.indexOf(value))}
|
||||
>
|
||||
{months.map(option => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</HeaderSelect>
|
||||
</HeaderSelectLabel>
|
||||
|
||||
<HeaderButton onClick={increaseMonth} disabled={nextMonthButtonDisabled}>
|
||||
Next
|
||||
</HeaderButton>
|
||||
</HeaderActions>
|
||||
)}
|
||||
selected={startDate}
|
||||
inline
|
||||
onChange={date => setStartDate(date ?? new Date())}
|
||||
/>
|
||||
</DueDatePickerWrapper>
|
||||
<ActionWrapper>
|
||||
<ConfirmAddDueDate onClick={() => onDueDateChange(task, startDate)}>Save</ConfirmAddDueDate>
|
||||
<CancelDueDate onClick={onCancel}>
|
||||
<Cross size={16} color="#c2c6dc" />
|
||||
</CancelDueDate>
|
||||
</ActionWrapper>
|
||||
<HeaderButton onClick={increaseMonth} disabled={nextMonthButtonDisabled}>
|
||||
Next
|
||||
</HeaderButton>
|
||||
</HeaderActions>
|
||||
)}
|
||||
selected={startDate}
|
||||
inline
|
||||
onChange={date => {
|
||||
setStartDate(date ?? new Date());
|
||||
}}
|
||||
/>
|
||||
</DueDatePickerWrapper>
|
||||
<ActionWrapper>
|
||||
<ConfirmAddDueDate
|
||||
type="submit"
|
||||
onClick={() => {
|
||||
// const newDate = moment(startDate).format('YYYY-MM-DD');
|
||||
// const newTime = moment(endTime).format('h:mm A');
|
||||
// onDueDateChange(task, moment(`${newDate} ${newTime}`, 'YYYY-MM-DD h:mm A').toDate());
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</ConfirmAddDueDate>
|
||||
<CancelDueDate onClick={onCancel}>
|
||||
<Cross size={16} color="#c2c6dc" />
|
||||
</CancelDueDate>
|
||||
</ActionWrapper>
|
||||
</Form>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components/macro';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styled, { css } from 'styled-components/macro';
|
||||
|
||||
const InputWrapper = styled.div<{ width: string }>`
|
||||
position: relative;
|
||||
@ -32,7 +32,13 @@ const InputLabel = styled.span<{ width: string }>`
|
||||
}
|
||||
`;
|
||||
|
||||
const InputInput = styled.input<{ hasIcon: boolean; width: string; focusBg: string; borderColor: string }>`
|
||||
const InputInput = styled.input<{
|
||||
hasValue: boolean;
|
||||
hasIcon: boolean;
|
||||
width: string;
|
||||
focusBg: string;
|
||||
borderColor: string;
|
||||
}>`
|
||||
width: ${props => props.width};
|
||||
font-size: 14px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
@ -54,6 +60,14 @@ const InputInput = styled.input<{ hasIcon: boolean; width: string; focusBg: stri
|
||||
color: rgba(115, 103, 240);
|
||||
transform: translate(-3px, -90%);
|
||||
}
|
||||
${props =>
|
||||
props.hasValue &&
|
||||
css`
|
||||
& ~ ${InputLabel} {
|
||||
color: rgba(115, 103, 240);
|
||||
transform: translate(-3px, -90%);
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const Icon = styled.div`
|
||||
@ -68,24 +82,66 @@ type InputProps = {
|
||||
width?: string;
|
||||
placeholder?: string;
|
||||
icon?: JSX.Element;
|
||||
id?: string;
|
||||
name?: string;
|
||||
className?: string;
|
||||
value?: string;
|
||||
onClick?: (e: React.MouseEvent<HTMLInputElement>) => void;
|
||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
};
|
||||
|
||||
const Input: React.FC<InputProps> = ({ width = 'auto', variant = 'normal', label, placeholder, icon }) => {
|
||||
const borderColor = variant === 'normal' ? 'rgba(0, 0, 0, 0.2)' : '#414561';
|
||||
const focusBg = variant === 'normal' ? 'rgba(38, 44, 73, )' : 'rgba(16, 22, 58, 1)';
|
||||
return (
|
||||
<InputWrapper width={width}>
|
||||
<InputInput
|
||||
hasIcon={typeof icon !== 'undefined'}
|
||||
width={width}
|
||||
placeholder={placeholder}
|
||||
focusBg={focusBg}
|
||||
borderColor={borderColor}
|
||||
/>
|
||||
{label && <InputLabel width={width}>{label}</InputLabel>}
|
||||
<Icon>{icon && icon}</Icon>
|
||||
</InputWrapper>
|
||||
);
|
||||
};
|
||||
const Input = React.forwardRef(
|
||||
(
|
||||
{
|
||||
width = 'auto',
|
||||
variant = 'normal',
|
||||
label,
|
||||
placeholder,
|
||||
icon,
|
||||
name,
|
||||
onChange,
|
||||
className,
|
||||
onClick,
|
||||
value: initialValue,
|
||||
id,
|
||||
}: InputProps,
|
||||
$ref: any,
|
||||
) => {
|
||||
const [value, setValue] = useState(initialValue ?? '');
|
||||
useEffect(() => {
|
||||
if (initialValue) {
|
||||
setValue(initialValue);
|
||||
}
|
||||
}, [initialValue]);
|
||||
const borderColor = variant === 'normal' ? 'rgba(0, 0, 0, 0.2)' : '#414561';
|
||||
const focusBg = variant === 'normal' ? 'rgba(38, 44, 73, )' : 'rgba(16, 22, 58, 1)';
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setValue(e.currentTarget.value);
|
||||
if (onChange) {
|
||||
onChange(e);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<InputWrapper className={className} width={width}>
|
||||
<InputInput
|
||||
hasValue={value !== ''}
|
||||
ref={$ref}
|
||||
id={id}
|
||||
name={name}
|
||||
onClick={onClick}
|
||||
onChange={handleChange}
|
||||
value={value}
|
||||
hasIcon={typeof icon !== 'undefined'}
|
||||
width={width}
|
||||
placeholder={placeholder}
|
||||
focusBg={focusBg}
|
||||
borderColor={borderColor}
|
||||
/>
|
||||
{label && <InputLabel width={width}>{label}</InputLabel>}
|
||||
<Icon>{icon && icon}</Icon>
|
||||
</InputWrapper>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default Input;
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
} from 'shared/utils/draggables';
|
||||
|
||||
import { Container, BoardWrapper } from './Styles';
|
||||
import moment from 'moment';
|
||||
|
||||
interface SimpleProps {
|
||||
taskGroups: Array<TaskGroup>;
|
||||
@ -165,6 +166,14 @@ const SimpleLists: React.FC<SimpleProps> = ({
|
||||
taskGroupID={taskGroup.id}
|
||||
description=""
|
||||
labels={task.labels.map(label => label.projectLabel)}
|
||||
dueDate={
|
||||
task.dueDate
|
||||
? {
|
||||
isPastDue: false,
|
||||
formattedDate: moment(task.dueDate).format('MMM D, YYYY'),
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
title={task.name}
|
||||
members={task.assigned}
|
||||
onClick={() => {
|
||||
|
@ -66,6 +66,7 @@ export const Default = () => {
|
||||
onMemberProfile={action('profile')}
|
||||
onOpenAddMemberPopup={action('open add member popup')}
|
||||
onOpenAddLabelPopup={action('open add label popup')}
|
||||
onOpenDueDatePopop={action('open due date popup')}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
@ -39,6 +39,7 @@ import {
|
||||
} from './Styles';
|
||||
|
||||
import convertDivElementRefToBounds from 'shared/utils/boundingRect';
|
||||
import moment from 'moment';
|
||||
|
||||
type TaskContentProps = {
|
||||
onEditContent: () => void;
|
||||
@ -106,6 +107,7 @@ type TaskDetailsProps = {
|
||||
onDeleteTask: (task: Task) => void;
|
||||
onOpenAddMemberPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||
onOpenAddLabelPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||
onOpenDueDatePopop: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||
onMemberProfile: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void;
|
||||
onCloseModal: () => void;
|
||||
};
|
||||
@ -118,6 +120,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
||||
onCloseModal,
|
||||
onOpenAddMemberPopup,
|
||||
onOpenAddLabelPopup,
|
||||
onOpenDueDatePopop,
|
||||
onMemberProfile,
|
||||
}) => {
|
||||
const [editorOpen, setEditorOpen] = useState(false);
|
||||
@ -143,6 +146,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
||||
const onAddMember = () => {
|
||||
onOpenAddMemberPopup(task, $addMemberRef);
|
||||
};
|
||||
const $dueDateLabel = useRef<HTMLDivElement>(null);
|
||||
const $addLabelRef = useRef<HTMLDivElement>(null);
|
||||
const onAddLabel = () => {
|
||||
onOpenAddLabelPopup(task, $addLabelRef);
|
||||
@ -229,7 +233,15 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
||||
</TaskDetailsAddLabel>
|
||||
</TaskDetailLabels>
|
||||
<TaskDetailSectionTitle>Due Date</TaskDetailSectionTitle>
|
||||
<NoDueDateLabel>No due date</NoDueDateLabel>
|
||||
{task.dueDate ? (
|
||||
<NoDueDateLabel ref={$dueDateLabel} onClick={() => onOpenDueDatePopop(task, $dueDateLabel)}>
|
||||
{moment(task.dueDate).format('MMM D [at] h:mm A')}
|
||||
</NoDueDateLabel>
|
||||
) : (
|
||||
<NoDueDateLabel ref={$dueDateLabel} onClick={() => onOpenDueDatePopop(task, $dueDateLabel)}>
|
||||
No due date
|
||||
</NoDueDateLabel>
|
||||
)}
|
||||
</TaskDetailsSidebar>
|
||||
</TaskDetailsWrapper>
|
||||
</>
|
||||
|
@ -110,6 +110,7 @@ export type Task = {
|
||||
name: Scalars['String'];
|
||||
position: Scalars['Float'];
|
||||
description?: Maybe<Scalars['String']>;
|
||||
dueDate?: Maybe<Scalars['Time']>;
|
||||
assigned: Array<ProjectMember>;
|
||||
labels: Array<TaskLabel>;
|
||||
};
|
||||
@ -310,6 +311,16 @@ export type UpdateTaskLocationPayload = {
|
||||
task: Task;
|
||||
};
|
||||
|
||||
export type UpdateTaskGroupName = {
|
||||
taskGroupID: Scalars['UUID'];
|
||||
name: Scalars['String'];
|
||||
};
|
||||
|
||||
export type UpdateTaskDueDate = {
|
||||
taskID: Scalars['UUID'];
|
||||
dueDate?: Maybe<Scalars['Time']>;
|
||||
};
|
||||
|
||||
export type Mutation = {
|
||||
__typename?: 'Mutation';
|
||||
createRefreshToken: RefreshToken;
|
||||
@ -325,6 +336,7 @@ export type Mutation = {
|
||||
updateProjectLabelColor: ProjectLabel;
|
||||
createTaskGroup: TaskGroup;
|
||||
updateTaskGroupLocation: TaskGroup;
|
||||
updateTaskGroupName: TaskGroup;
|
||||
deleteTaskGroup: DeleteTaskGroupPayload;
|
||||
addTaskLabel: Task;
|
||||
removeTaskLabel: Task;
|
||||
@ -333,6 +345,7 @@ export type Mutation = {
|
||||
updateTaskDescription: Task;
|
||||
updateTaskLocation: UpdateTaskLocationPayload;
|
||||
updateTaskName: Task;
|
||||
updateTaskDueDate: Task;
|
||||
deleteTask: DeleteTaskPayload;
|
||||
assignTask: Task;
|
||||
unassignTask: Task;
|
||||
@ -400,6 +413,11 @@ export type MutationUpdateTaskGroupLocationArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationUpdateTaskGroupNameArgs = {
|
||||
input: UpdateTaskGroupName;
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteTaskGroupArgs = {
|
||||
input: DeleteTaskGroupInput;
|
||||
};
|
||||
@ -440,6 +458,11 @@ export type MutationUpdateTaskNameArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationUpdateTaskDueDateArgs = {
|
||||
input: UpdateTaskDueDate;
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteTaskArgs = {
|
||||
input: DeleteTaskInput;
|
||||
};
|
||||
@ -658,7 +681,7 @@ export type FindProjectQuery = (
|
||||
& Pick<TaskGroup, 'id' | 'name' | 'position'>
|
||||
& { tasks: Array<(
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'id' | 'name' | 'position' | 'description'>
|
||||
& Pick<Task, 'id' | 'name' | 'position' | 'description' | 'dueDate'>
|
||||
& { taskGroup: (
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'id' | 'name' | 'position'>
|
||||
@ -698,7 +721,7 @@ export type FindTaskQuery = (
|
||||
{ __typename?: 'Query' }
|
||||
& { findTask: (
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'id' | 'name' | 'description' | 'position'>
|
||||
& Pick<Task, 'id' | 'name' | 'description' | 'dueDate' | 'position'>
|
||||
& { taskGroup: (
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'id'>
|
||||
@ -852,6 +875,20 @@ export type UpdateTaskDescriptionMutation = (
|
||||
) }
|
||||
);
|
||||
|
||||
export type UpdateTaskDueDateMutationVariables = {
|
||||
taskID: Scalars['UUID'];
|
||||
dueDate?: Maybe<Scalars['Time']>;
|
||||
};
|
||||
|
||||
|
||||
export type UpdateTaskDueDateMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { updateTaskDueDate: (
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'id' | 'dueDate'>
|
||||
) }
|
||||
);
|
||||
|
||||
export type UpdateTaskGroupLocationMutationVariables = {
|
||||
taskGroupID: Scalars['UUID'];
|
||||
position: Scalars['Float'];
|
||||
@ -1298,6 +1335,7 @@ export const FindProjectDocument = gql`
|
||||
name
|
||||
position
|
||||
description
|
||||
dueDate
|
||||
taskGroup {
|
||||
id
|
||||
name
|
||||
@ -1370,6 +1408,7 @@ export const FindTaskDocument = gql`
|
||||
id
|
||||
name
|
||||
description
|
||||
dueDate
|
||||
position
|
||||
taskGroup {
|
||||
id
|
||||
@ -1705,6 +1744,40 @@ export function useUpdateTaskDescriptionMutation(baseOptions?: ApolloReactHooks.
|
||||
export type UpdateTaskDescriptionMutationHookResult = ReturnType<typeof useUpdateTaskDescriptionMutation>;
|
||||
export type UpdateTaskDescriptionMutationResult = ApolloReactCommon.MutationResult<UpdateTaskDescriptionMutation>;
|
||||
export type UpdateTaskDescriptionMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskDescriptionMutation, UpdateTaskDescriptionMutationVariables>;
|
||||
export const UpdateTaskDueDateDocument = gql`
|
||||
mutation updateTaskDueDate($taskID: UUID!, $dueDate: Time) {
|
||||
updateTaskDueDate(input: {taskID: $taskID, dueDate: $dueDate}) {
|
||||
id
|
||||
dueDate
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type UpdateTaskDueDateMutationFn = ApolloReactCommon.MutationFunction<UpdateTaskDueDateMutation, UpdateTaskDueDateMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useUpdateTaskDueDateMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useUpdateTaskDueDateMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useUpdateTaskDueDateMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [updateTaskDueDateMutation, { data, loading, error }] = useUpdateTaskDueDateMutation({
|
||||
* variables: {
|
||||
* taskID: // value for 'taskID'
|
||||
* dueDate: // value for 'dueDate'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useUpdateTaskDueDateMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<UpdateTaskDueDateMutation, UpdateTaskDueDateMutationVariables>) {
|
||||
return ApolloReactHooks.useMutation<UpdateTaskDueDateMutation, UpdateTaskDueDateMutationVariables>(UpdateTaskDueDateDocument, baseOptions);
|
||||
}
|
||||
export type UpdateTaskDueDateMutationHookResult = ReturnType<typeof useUpdateTaskDueDateMutation>;
|
||||
export type UpdateTaskDueDateMutationResult = ApolloReactCommon.MutationResult<UpdateTaskDueDateMutation>;
|
||||
export type UpdateTaskDueDateMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskDueDateMutation, UpdateTaskDueDateMutationVariables>;
|
||||
export const UpdateTaskGroupLocationDocument = gql`
|
||||
mutation updateTaskGroupLocation($taskGroupID: UUID!, $position: Float!) {
|
||||
updateTaskGroupLocation(input: {taskGroupID: $taskGroupID, position: $position}) {
|
||||
|
@ -30,6 +30,7 @@ query findProject($projectId: String!) {
|
||||
name
|
||||
position
|
||||
description
|
||||
dueDate
|
||||
taskGroup {
|
||||
id
|
||||
name
|
||||
|
@ -3,6 +3,7 @@ query findTask($taskID: UUID!) {
|
||||
id
|
||||
name
|
||||
description
|
||||
dueDate
|
||||
position
|
||||
taskGroup {
|
||||
id
|
||||
|
12
web/src/shared/graphql/updateTaskDueDate.graphqls
Normal file
12
web/src/shared/graphql/updateTaskDueDate.graphqls
Normal file
@ -0,0 +1,12 @@
|
||||
mutation updateTaskDueDate($taskID: UUID!, $dueDate: Time) {
|
||||
updateTaskDueDate (
|
||||
input: {
|
||||
taskID: $taskID
|
||||
dueDate: $dueDate
|
||||
}
|
||||
) {
|
||||
id
|
||||
dueDate
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,12 @@
|
||||
import React from 'react';
|
||||
import Icon, { IconProps } from './Icon';
|
||||
|
||||
type Props = {
|
||||
size: number | string;
|
||||
color: string;
|
||||
};
|
||||
|
||||
const Bolt = ({ size, color }: Props) => {
|
||||
const Bolt: React.FC<IconProps> = ({ width = '16px', height = '16px' }) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" width={size} height={size}>
|
||||
<path
|
||||
fill={color}
|
||||
d="M296 160H180.6l42.6-129.8C227.2 15 215.7 0 200 0H56C44 0 33.8 8.9 32.2 20.8l-32 240C-1.7 275.2 9.5 288 24 288h118.7L96.6 482.5c-3.6 15.2 8 29.5 23.3 29.5 8.4 0 16.4-4.4 20.8-12l176-304c9.3-15.9-2.2-36-20.7-36z"
|
||||
/>
|
||||
</svg>
|
||||
<Icon width={width} height={height} viewBox="0 0 320 512">
|
||||
<path d="M296 160H180.6l42.6-129.8C227.2 15 215.7 0 200 0H56C44 0 33.8 8.9 32.2 20.8l-32 240C-1.7 275.2 9.5 288 24 288h118.7L96.6 482.5c-3.6 15.2 8 29.5 23.3 29.5 8.4 0 16.4-4.4 20.8-12l176-304c9.3-15.9-2.2-36-20.7-36z" />
|
||||
</Icon>
|
||||
);
|
||||
};
|
||||
|
||||
Bolt.defaultProps = {
|
||||
size: 16,
|
||||
color: '#000',
|
||||
};
|
||||
|
||||
export default Bolt;
|
||||
|
28
web/src/shared/icons/Icon.tsx
Normal file
28
web/src/shared/icons/Icon.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components/macro';
|
||||
|
||||
export type IconProps = {
|
||||
width: number | string;
|
||||
height: number | string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
width: number | string;
|
||||
height: number | string;
|
||||
viewBox: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Svg = styled.svg`
|
||||
fill: rgba(${props => props.theme.colors.text.primary});
|
||||
`;
|
||||
|
||||
const Icon: React.FC<Props> = ({ width, height, viewBox, className, children }) => {
|
||||
return (
|
||||
<Svg className={className} width={width} height={height} xmlns="http://www.w3.org/2000/svg" viewBox={viewBox}>
|
||||
{children}
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default Icon;
|
@ -1,24 +1,12 @@
|
||||
import React from 'react';
|
||||
import Icon, { IconProps } from './Icon';
|
||||
|
||||
type Props = {
|
||||
size: number | string;
|
||||
color: string;
|
||||
};
|
||||
|
||||
const Tags = ({ size, color }: Props) => {
|
||||
const Tags: React.FC<IconProps> = ({ width = '16px', height = '16px' }) => {
|
||||
return (
|
||||
<svg width={size} height={size} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512">
|
||||
<path
|
||||
fill={color}
|
||||
d="M497.941 225.941L286.059 14.059A48 48 0 0 0 252.118 0H48C21.49 0 0 21.49 0 48v204.118a48 48 0 0 0 14.059 33.941l211.882 211.882c18.744 18.745 49.136 18.746 67.882 0l204.118-204.118c18.745-18.745 18.745-49.137 0-67.882zM112 160c-26.51 0-48-21.49-48-48s21.49-48 48-48 48 21.49 48 48-21.49 48-48 48zm513.941 133.823L421.823 497.941c-18.745 18.745-49.137 18.745-67.882 0l-.36-.36L527.64 323.522c16.999-16.999 26.36-39.6 26.36-63.64s-9.362-46.641-26.36-63.64L331.397 0h48.721a48 48 0 0 1 33.941 14.059l211.882 211.882c18.745 18.745 18.745 49.137 0 67.882z"
|
||||
/>
|
||||
</svg>
|
||||
<Icon width={width} height={height} viewBox="0 0 640 512">
|
||||
<path d="M497.941 225.941L286.059 14.059A48 48 0 0 0 252.118 0H48C21.49 0 0 21.49 0 48v204.118a48 48 0 0 0 14.059 33.941l211.882 211.882c18.744 18.745 49.136 18.746 67.882 0l204.118-204.118c18.745-18.745 18.745-49.137 0-67.882zM112 160c-26.51 0-48-21.49-48-48s21.49-48 48-48 48 21.49 48 48-21.49 48-48 48zm513.941 133.823L421.823 497.941c-18.745 18.745-49.137 18.745-67.882 0l-.36-.36L527.64 323.522c16.999-16.999 26.36-39.6 26.36-63.64s-9.362-46.641-26.36-63.64L331.397 0h48.721a48 48 0 0 1 33.941 14.059l211.882 211.882c18.745 18.745 18.745 49.137 0 67.882z" />
|
||||
</Icon>
|
||||
);
|
||||
};
|
||||
|
||||
Tags.defaultProps = {
|
||||
size: 16,
|
||||
color: '#000',
|
||||
};
|
||||
|
||||
export default Tags;
|
||||
|
@ -1,24 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
type Props = {
|
||||
size: number | string;
|
||||
color: string;
|
||||
};
|
||||
import Icon, { IconProps } from './Icon';
|
||||
|
||||
const ToggleOn = ({ size, color }: Props) => {
|
||||
const ToggleOn: React.FC<IconProps> = ({ width = '16px', height = '16px' }) => {
|
||||
return (
|
||||
<svg width={size} height={size} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
|
||||
<path
|
||||
fill={color}
|
||||
d="M384 64H192C86 64 0 150 0 256s86 192 192 192h192c106 0 192-86 192-192S490 64 384 64zm0 320c-70.8 0-128-57.3-128-128 0-70.8 57.3-128 128-128 70.8 0 128 57.3 128 128 0 70.8-57.3 128-128 128z"
|
||||
/>
|
||||
</svg>
|
||||
<Icon width={width} height={height} viewBox="0 0 576 512">
|
||||
<path d="M384 64H192C86 64 0 150 0 256s86 192 192 192h192c106 0 192-86 192-192S490 64 384 64zm0 320c-70.8 0-128-57.3-128-128 0-70.8 57.3-128 128-128 70.8 0 128 57.3 128 128 0 70.8-57.3 128-128 128z" />
|
||||
</Icon>
|
||||
);
|
||||
};
|
||||
|
||||
ToggleOn.defaultProps = {
|
||||
size: 16,
|
||||
color: '#000',
|
||||
};
|
||||
|
||||
export default ToggleOn;
|
||||
|
Reference in New Issue
Block a user