Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/group #42

Merged
merged 7 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 58 additions & 6 deletions components/Group/Form/Fields/AreaCheckbox.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import Box from '@mui/material/Box';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
Expand All @@ -9,27 +10,78 @@ export default function AreaCheckbox({
itemValue,
name,
value,
onChange,
control,
}) {
const [isPhysicalArea, setIsPhysicalArea] = useState(false);

const getPhysicalArea = (data) =>
options.find((option) => data.includes(option.name));

const handleChange = (val) =>
control.onChange({ target: { name, value: val } });

const physicalAreaValue = getPhysicalArea(value)?.name || '';

const toggleIsPhysicalArea = () => {
const updatedValue = value.filter((v) => !getPhysicalArea([v]));
handleChange(updatedValue);
setIsPhysicalArea((pre) => !pre);
};

const handleCheckboxChange = (_value) => {
const updatedValue = value.includes(_value)
? value.filter((v) => v !== _value)
: [...value, _value];
handleChange(updatedValue);
};

const handlePhysicalAreaChange = ({ target }) => {
const updatedValue = value
.filter((v) => !getPhysicalArea([v]))
.concat(target.value);
handleChange(updatedValue);
};

const physicalAreaControl = {
onChange: handlePhysicalAreaChange,
onBlur: handlePhysicalAreaChange,
};

useEffect(() => {
if (value.find((v) => getPhysicalArea([v]))) setIsPhysicalArea(true);
}, [value]);

return (
<>
<Box sx={{ display: 'flex', label: { whiteSpace: 'nowrap' } }}>
<FormControlLabel control={<Checkbox />} label="實體活動" />
<FormControlLabel
control={<Checkbox onClick={toggleIsPhysicalArea} />}
label="實體活動"
checked={isPhysicalArea}
/>
<Select
name={name}
options={options}
placeholder="地點"
value={value}
value={physicalAreaValue}
itemLabel={itemLabel}
itemValue={itemValue}
onChange={onChange}
control={physicalAreaControl}
/>
</Box>
<div>
<FormControlLabel control={<Checkbox />} label="線上" />
<FormControlLabel
control={<Checkbox onClick={() => handleCheckboxChange('線上')} />}
label="線上"
checked={value.includes('線上')}
/>
</div>
<div>
<FormControlLabel control={<Checkbox />} label="待討論" />
<FormControlLabel
control={<Checkbox onClick={() => handleCheckboxChange('待討論')} />}
label="待討論"
checked={value.includes('待討論')}
/>
</div>
</>
);
Expand Down
11 changes: 8 additions & 3 deletions components/Group/Form/Fields/Select.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { useState } from 'react';
import FormControl from '@mui/material/FormControl';
import MuiSelect from '@mui/material/Select';
import MenuItem from '@mui/material/MenuItem';

export default function Select({
id,
name,
value,
placeholder,
options = [],
itemLabel = 'label',
fullWidth = true,
multiple,
onChange,
sx,
disabled,
control,
value,
error,
}) {
const getValue = (any, key) => (typeof any === 'object' ? any[key] : any);
const renderValue = (selected) => {
Expand All @@ -37,7 +40,8 @@ export default function Select({
...sx,
}}
value={value}
onChange={onChange}
disabled={disabled}
{...control}
>
{placeholder && (
<MenuItem disabled value="" sx={{ fontSize: 14 }}>
Expand All @@ -53,6 +57,7 @@ export default function Select({
</MenuItem>
))}
</MuiSelect>
<span className="error-message">{error}</span>
</FormControl>
);
}
30 changes: 19 additions & 11 deletions components/Group/Form/Fields/TagsField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,44 @@ import ClearIcon from '@mui/icons-material/Clear';
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline';
import { StyledChip, StyledTagsField } from '../Form.styled';

function TagsField({ label, helperText, ...props }) {
const [tags, setTags] = useState([]);
function TagsField({ name, helperText, control, value = [] }) {
const [input, setInput] = useState('');
const [error, setError] = useState('');

const handleInput = (e) => {
const { value } = e.target;
if (value.length > 8) setError('標籤最多 8 個字');
const _value = e.target.value;
if (_value.length > 8) setError('標籤最多 8 個字');
else setError('');
setInput(value);
setInput(_value);
};

const handleKeyDown = (e) => {
if (error) return;
const tag = input.trim();
if (e.key !== 'Enter' || !tag) return;
if (tags.indexOf(tag) > -1) return;
setTags((pre) => [...pre, tag]);
if (value.indexOf(tag) > -1) return;
setInput('');
control.onChange({
target: {
name,
value: [...value, tag],
},
});
};

const handleDelete = (tag) => () => {
setTags((pre) => pre.filter((t) => t !== tag));
control.onChange({
target: {
name,
value: value.filter((t) => t !== tag),
},
});
};

return (
<>
<StyledTagsField>
{tags.map((tag) => (
{value.map((tag) => (
<StyledChip
key={tag}
label={tag}
Expand All @@ -42,9 +51,8 @@ function TagsField({ label, helperText, ...props }) {
onDelete={handleDelete(tag)}
/>
))}
{tags.length < 8 && (
{value.length < 8 && (
<input
{...props}
value={input}
onChange={handleInput}
onKeyDown={handleKeyDown}
Expand Down
17 changes: 4 additions & 13 deletions components/Group/Form/Fields/TextField.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
import MuiTextField from '@mui/material/TextField';
import { useEffect, useState } from 'react';

export default function TextField({
id,
placeholder,
multiline,
name,
value,
onChange,
helperText,
max,
errorMessage,
control,
value,
error,
}) {
const [error, setError] = useState('');

useEffect(() => {
if (value.length > max) setError(errorMessage);
else setError('');
}, [max, value]);

return (
<>
<MuiTextField
Expand All @@ -31,8 +22,8 @@ export default function TextField({
value={value}
multiline={multiline}
rows={multiline && 10}
onChange={onChange}
helperText={helperText}
{...control}
/>
<span className="error-message">{error}</span>
</>
Expand Down
14 changes: 11 additions & 3 deletions components/Group/Form/Fields/Upload.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import DeleteSvg from '@/public/assets/icons/delete.svg';
import { StyledUpload } from '../Form.styled';
import UploadSvg from './UploadSvg';

export default function Upload({ name, onChange }) {
const [preview, setPreview] = useState('');
export default function Upload({ name, value, control }) {
const [preview, setPreview] = useState(value || '');
const [error, setError] = useState('');
const inputRef = useRef();

Expand All @@ -17,17 +17,25 @@ export default function Upload({ name, onChange }) {
value: file,
},
};
onChange(event);
control.onChange(event);
};

const handleFile = (file) => {
const imageType = /image.*/;
const maxSize = 500 * 1024; // 500 KB

setPreview('');
setError('');
if (!file.type.match(imageType)) {
setError('僅支援上傳圖片唷!');
return;
}

if (file.size > maxSize) {
setError('圖片最大限制 500 KB');
return;
}

const reader = new FileReader();
reader.onload = (e) => setPreview(e.target.result);
reader.readAsDataURL(file);
Expand Down
58 changes: 18 additions & 40 deletions components/Group/Form/Fields/index.jsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,34 @@
import { useId } from 'react';
import AreaCheckbox from './AreaCheckbox';
import Select from './Select';
import TagsField from './TagsField';
import TextField from './TextField';
import Upload from './Upload';
import Wrapper from './Wrapper';
import useWrapperProps from './useWrapperProps';

const Fields = {};
const withWrapper = (Component) => (props) => {
const id = useId();
const formItemId = `form-item-${id}`;
const { required, label, tooltip } = props;

Fields.AreaCheckbox = (props) => {
const wrapperProps = useWrapperProps(props);
return (
<Wrapper {...wrapperProps}>
<AreaCheckbox {...props} />
<Wrapper
id={formItemId}
required={required}
label={label}
tooltip={tooltip}
>
<Component {...props} />
</Wrapper>
);
};

Fields.Select = (props) => {
const wrapperProps = useWrapperProps(props);
return (
<Wrapper {...wrapperProps}>
<Select {...props} />
</Wrapper>
);
};

Fields.TagsField = (props) => {
const wrapperProps = useWrapperProps(props);
return (
<Wrapper {...wrapperProps}>
<TagsField {...props} />
</Wrapper>
);
};

Fields.TextField = (props) => {
const wrapperProps = useWrapperProps(props);
return (
<Wrapper {...wrapperProps}>
<TextField {...props} />
</Wrapper>
);
};

Fields.Upload = (props) => {
const wrapperProps = useWrapperProps(props);
return (
<Wrapper {...wrapperProps}>
<Upload {...props} />
</Wrapper>
);
const Fields = {
AreaCheckbox: withWrapper(AreaCheckbox),
Select: withWrapper(Select),
TagsField: withWrapper(TagsField),
TextField: withWrapper(TextField),
Upload: withWrapper(Upload),
};

export default Fields;
8 changes: 0 additions & 8 deletions components/Group/Form/Fields/useWrapperProps.jsx

This file was deleted.

12 changes: 12 additions & 0 deletions components/Group/Form/Form.styled.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ export const StyledChip = styled(Chip)`
}
`;

export const StyledSwitchWrapper = styled.div`
padding: 4px 16px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16px;
font-weight: 500;
color: #293a3d;
border: 1px solid rgba(0, 0, 0, 0.23);
border-radius: 4px;
`;

export const StyledTagsField = styled.div(
({ theme }) => `
position: relative;
Expand Down
Loading
Loading