From c28a359c09061c5cb5de04e36dd3d1fe36123827 Mon Sep 17 00:00:00 2001 From: Guergana Tzatchkova Date: Wed, 23 Oct 2024 10:42:44 -0700 Subject: [PATCH] Publish dialog (#602) --- client/assets/icon_info.png | Bin 0 -> 866 bytes .../Application/Dialogs/AdjustFile.tsx | 6 +- .../components/Application/Dialogs/Config.tsx | 6 +- .../components/Application/Dialogs/Create.tsx | 6 +- .../Application/Dialogs/FileUpload.tsx | 240 +++++++----------- .../Application/Dialogs/IndexFiles.tsx | 6 +- .../Application/Dialogs/Publish.tsx | 8 +- .../Dialogs/assets/dialog_publish.png | Bin 0 -> 2805 bytes client/components/Editors/Base/Help.tsx | 10 +- client/components/Editors/Base/Section.tsx | 4 +- client/components/Editors/Portal/Layout.tsx | 59 ++--- .../Editors/Portal/Sections/Ckan.tsx | 15 +- .../Editors/Portal/Sections/Github.tsx | 16 +- .../Editors/Portal/Sections/Zenodo.tsx | 16 +- client/components/Parts/Cards/Help.tsx | 50 +++- client/components/Parts/Dialogs/Note.tsx | 2 +- client/components/Parts/Dialogs/OneButton.tsx | 2 +- client/components/Parts/Dialogs/Pick.tsx | 6 +- client/components/Parts/Dialogs/TwoButton.tsx | 2 +- client/components/Parts/Fields/Input.tsx | 27 +- client/components/Parts/Fields/Multiline.tsx | 7 +- .../components/Parts/Fields/Multiselect.tsx | 6 +- client/components/Parts/Fields/Select.tsx | 6 +- client/components/Parts/Fields/YesNo.tsx | 8 +- client/components/Parts/Tabs/Dialog.tsx | 95 +++++++ 25 files changed, 364 insertions(+), 239 deletions(-) create mode 100644 client/assets/icon_info.png create mode 100644 client/components/Application/Dialogs/assets/dialog_publish.png create mode 100644 client/components/Parts/Tabs/Dialog.tsx diff --git a/client/assets/icon_info.png b/client/assets/icon_info.png new file mode 100644 index 0000000000000000000000000000000000000000..fa495714beacb3233152d3f0631aeb9cd33a1ee7 GIT binary patch literal 866 zcmV-o1D*VdP)gDP6KB=Yg93gZ!Ks#3-Y`UzlH2Lh z2~6ZC(8c3l1(zwtQ8g>kEZp!lIv(1M4NX@ z>KSzb1Q_Zmvbt6=a#Isy)Zcrx?Ei32=Fs*{qKh3->m=j#KQ5{)(av2z!Bs20dg|dmdiDQFKnNa))ga)1T-Izq^JXBn{-HupbPb z_|*4+J=8_BzIodC{*%ngbOeh5)PAqfG}t`E3q}q*+N)F49MWoa!)h6@7!+xTLAwfd znPQkNj_L=7E4S-8RKR+x@w3ye8i01y0JJNEJ!Y32EJH|Xe*HNl@!Q{boMJiY#3+JE skeoKutA2i)E+vJ@0m=tUM!^KO|CpRKbFNT>E&u=k07*qoM6N<$f{b90VE_OC literal 0 HcmV?d00001 diff --git a/client/components/Application/Dialogs/AdjustFile.tsx b/client/components/Application/Dialogs/AdjustFile.tsx index fc498e8f9..960c57782 100644 --- a/client/components/Application/Dialogs/AdjustFile.tsx +++ b/client/components/Application/Dialogs/AdjustFile.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import Box from '@mui/material/Box' import DisplaySettingsIcon from '@mui/icons-material/DisplaySettings' -import ConfirmDialog from '../../Parts/Dialogs/OneButton' +import OneButtonDialog from '../../Parts/Dialogs/OneButton' import SelectField from '../../Parts/Fields/Select' import InputField from '../../Parts/Fields/Input' import * as store from '@client/store' @@ -17,7 +17,7 @@ export default function AdjustFileDialog() { const newType = record.type !== type ? type : undefined return ( - - + ) } diff --git a/client/components/Application/Dialogs/Config.tsx b/client/components/Application/Dialogs/Config.tsx index 041452ea7..c50faf10c 100644 --- a/client/components/Application/Dialogs/Config.tsx +++ b/client/components/Application/Dialogs/Config.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import Box from '@mui/material/Box' import SaveIcon from '@mui/icons-material/Save' -import ConfirmDialog from '../../Parts/Dialogs/OneButton' +import OneButtonDialog from '../../Parts/Dialogs/OneButton' import ConfigEditor from '../../Editors/Config' import * as store from '@client/store' @@ -13,7 +13,7 @@ export default function ConfigDialog() { if (!newConfig) return null return ( - - + ) } diff --git a/client/components/Application/Dialogs/Create.tsx b/client/components/Application/Dialogs/Create.tsx index 5a214ce92..eece2db5e 100644 --- a/client/components/Application/Dialogs/Create.tsx +++ b/client/components/Application/Dialogs/Create.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import Box from '@mui/material/Box' import LinearProgress from '@mui/material/LinearProgress' -import ConfirmDialog from '../../Parts/Dialogs/OneButton' +import OneButtonDialog from '../../Parts/Dialogs/OneButton' import MultilineField from '../../Parts/Fields/Multiline' import InputField from '../../Parts/Fields/Input' import Columns from '../../Parts/Grids/Columns' @@ -85,7 +85,7 @@ export default function CreateDialog() { } return ( - - + ) } diff --git a/client/components/Application/Dialogs/FileUpload.tsx b/client/components/Application/Dialogs/FileUpload.tsx index 7c6700825..0e2f14b1c 100644 --- a/client/components/Application/Dialogs/FileUpload.tsx +++ b/client/components/Application/Dialogs/FileUpload.tsx @@ -5,8 +5,6 @@ import IconButton from '@mui/material/IconButton' import DialogContent from '@mui/material/DialogContent' import CloseIcon from '@mui/icons-material/Close' import * as store from '@client/store' -import Tabs from '@mui/material/Tabs' -import Tab from '@mui/material/Tab' import { styled } from '@mui/material/styles' import uploadFilesDialogImg from '../../../assets/dialog_upload_files.png' import iconUploadFileImg from '../../../assets/icon_upload_file.png' @@ -18,6 +16,7 @@ import TextField from '@mui/material/TextField' import CircularProgress from '@mui/material/CircularProgress' import InputAdornment from '@mui/material/InputAdornment' import * as helpers from '../../../helpers' +import DialogTabs from '../../Parts/Tabs/Dialog' export default function FileUploadDialog() { const [errorMessage, setErrorMessage] = React.useState('') @@ -26,21 +25,10 @@ export default function FileUploadDialog() { store.closeDialog() } - const dialogTab = store.useStore((state) => state.dialogTab) - - const [value, setValue] = React.useState(dialogTab ? dialogTab : 0) - const [remoteUrlValue, setRemoteUrlValue] = React.useState('') const [loading, setLoading] = React.useState(false) - function a11yProps(index: number) { - return { - id: `simple-tab-${index}`, - 'aria-controls': `simple-tabpanel-${index}`, - } - } - // by default the height of the tabs is set by its content, to avoid the height jump // when changing the tabs we assign the second tab whatever is the height of the first one const tabRefForHeight = React.useRef(null) @@ -50,13 +38,6 @@ export default function FileUploadDialog() { tabRefForHeight.current ? setTabHeight(tabRefForHeight.current.clientHeight) : null }, [tabRefForHeight]) - // the event needs to be passed even if not used, disabling here so there's - // no unused variable error in the typescript check - // @ts-ignore - const handleChange = (event: React.SyntheticEvent, newValue: number) => { - setValue(newValue) - } - const inputFileRef = React.useRef(null) const inputFolderRef = React.useRef(null) @@ -97,6 +78,8 @@ export default function FileUploadDialog() { } } + const tabLabels = ['From your computer', 'Add external data'] + const isWebkitDirectorySupported = 'webkitdirectory' in document.createElement('input') if (!isWebkitDirectorySupported) return null @@ -132,142 +115,113 @@ export default function FileUploadDialog() { Image Folder Dialog - - - - - - - - - theme.palette.primary.main, - }, - }} - > - ) => { - if (ev.target.files) { - await store.addFiles(ev.target.files) - store.openDialog('openLocation') - } + + + + theme.palette.primary.main, + }, }} - /> - - - Icon Upload File + > + ) => { + if (ev.target.files) { + await store.addFiles(ev.target.files) + store.openDialog('openLocation') + } + }} + /> + + + Icon Upload File + + Add one or more Excel or csv files + + Select + - Add one or more Excel or csv files - - Select - - - - theme.palette.primary.main, - }, - }} - > - ) => { - if (ev.target.files) { - store.addFiles(ev.target.files) - store.closeDialog() - } + + theme.palette.primary.main, + }, }} - // @ts-expect-error - webkitdirectory="" - /> - - - Icon Upload File + > + ) => { + if (ev.target.files) { + store.addFiles(ev.target.files) + store.closeDialog() + } + }} + // @ts-expect-error + webkitdirectory="" + /> + + + Icon Upload File + + Add one or more folders + + Select + - Add one or more folders - - Select - - - - - - - - + + + - Link to the external table: - - - - {loading ? ( - + Link to the external table: + + + - ) : null} + {loading ? ( + + ) : null} + + onAddRemoteConfirm(remoteUrlValue)} + /> - onAddRemoteConfirm(remoteUrlValue)} - /> - - + + ) } -function CustomTabPanel(props: { - children?: React.ReactNode - index: number - value: number -}) { - const { children, value, index, ...other } = props - - return ( - - ) -} - function AddRemoteTextfield(props: { errorMessage?: string value: string diff --git a/client/components/Application/Dialogs/IndexFiles.tsx b/client/components/Application/Dialogs/IndexFiles.tsx index 1f22f56de..482a22a06 100644 --- a/client/components/Application/Dialogs/IndexFiles.tsx +++ b/client/components/Application/Dialogs/IndexFiles.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import DeleteIcon from '@mui/icons-material/Delete' -import ConfirmDialog from '../../Parts/Dialogs/OneButton' +import OneButtonDialog from '../../Parts/Dialogs/OneButton' import LinearSensor from '../../Parts/Sensors/Linear' import * as store from '@client/store' import { client } from '@client/client' @@ -10,7 +10,7 @@ export default function IndexFilesDialog() { const notIndexedFiles = store.useStore(store.getNotIndexedFiles) return ( - {progress > 0 && progress < 100 && } - + ) } diff --git a/client/components/Application/Dialogs/Publish.tsx b/client/components/Application/Dialogs/Publish.tsx index 1a327999f..0cc922674 100644 --- a/client/components/Application/Dialogs/Publish.tsx +++ b/client/components/Application/Dialogs/Publish.tsx @@ -4,7 +4,7 @@ import Box from '@mui/material/Box' import Link from '@mui/material/Link' import LinearProgress from '@mui/material/LinearProgress' import CheckIcon from '@mui/icons-material/Check' -import ConfirmDialog from '../../Parts/Dialogs/OneButton' +import OneButtonDialog from '../../Parts/Dialogs/OneButton' import PortalEditor from '../../Editors/Portal' import * as helpers from '../../../helpers' import * as types from '../../../types' @@ -29,7 +29,7 @@ export default function PublishDialog() { } return ( - - + { @@ -62,6 +62,6 @@ export default function PublishDialog() { {' '} )} - + ) } diff --git a/client/components/Application/Dialogs/assets/dialog_publish.png b/client/components/Application/Dialogs/assets/dialog_publish.png new file mode 100644 index 0000000000000000000000000000000000000000..6ca3f20339c2bdaee955a4a04e5f684b8f8df18e GIT binary patch literal 2805 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 Hu0mjf_ew;( literal 0 HcmV?d00001 diff --git a/client/components/Editors/Base/Help.tsx b/client/components/Editors/Base/Help.tsx index a902126b7..d214136c7 100644 --- a/client/components/Editors/Base/Help.tsx +++ b/client/components/Editors/Base/Help.tsx @@ -4,13 +4,19 @@ import * as types from '../../../types' interface EditorHelpProps { helpItem: types.IHelpItem + withIcon?: boolean } export default function EditorHelp(props: EditorHelpProps) { - const { helpItem } = props + const { helpItem, withIcon } = props return ( - + {helpItem.description} diff --git a/client/components/Editors/Base/Section.tsx b/client/components/Editors/Base/Section.tsx index 857dbb691..ff1d4db1e 100644 --- a/client/components/Editors/Base/Section.tsx +++ b/client/components/Editors/Base/Section.tsx @@ -5,7 +5,7 @@ import Columns from '../../Parts/Grids/Columns' import HeadingBox from './Heading/Box' export interface EditorItemProps { - name: string + name?: string onHeadingClick?: () => void onBackClick?: () => void } @@ -26,7 +26,7 @@ export default function EditorItem(props: React.PropsWithChildren props.onHeadingClick && props.onHeadingClick()}> - {props.name} + {props.name ? {props.name} : null} diff --git a/client/components/Editors/Portal/Layout.tsx b/client/components/Editors/Portal/Layout.tsx index 97f35c87b..392954483 100644 --- a/client/components/Editors/Portal/Layout.tsx +++ b/client/components/Editors/Portal/Layout.tsx @@ -1,43 +1,38 @@ import Box from '@mui/material/Box' -import Columns from '../../Parts/Grids/Columns' -import MenuPanel from '../../Parts/Panels/Menu' -import EditorHelp from '../Base/Help' +import publishDialogImg from '../../Application/Dialogs/assets/dialog_publish.png' import CkanSection from './Sections/Ckan' import GithubSection from './Sections/Github' import ZenodoSection from './Sections/Zenodo' -import { useStore } from './store' -import * as types from '../../../types' - -const MENU_ITEMS: types.IMenuItem[] = [ - { section: 'ckan', name: 'Ckan' }, - { section: 'github', name: 'Github' }, - { section: 'zenodo', name: 'Zenodo' }, -] +import DialogTabs from '../../Parts/Tabs/Dialog' export default function Layout() { - const section = useStore((state) => state.descriptor.type) - const helpItem = useStore((state) => state.helpItem) - const updateHelp = useStore((state) => state.updateHelp) - const updateDescriptor = useStore((state) => state.updateDescriptor) + const tabLabels = ['Ckan', 'Github', 'Zenodo'] + return ( - - { - if (section === newSection) return - updateHelp(newSection) - // @ts-ignore - updateDescriptor({ type: newSection }) - }} - > - - - - - - + + Image Publish Dialog + + + + + + + + + + + + + + ) } diff --git a/client/components/Editors/Portal/Sections/Ckan.tsx b/client/components/Editors/Portal/Sections/Ckan.tsx index 5908fa43e..f450a63a2 100644 --- a/client/components/Editors/Portal/Sections/Ckan.tsx +++ b/client/components/Editors/Portal/Sections/Ckan.tsx @@ -2,15 +2,20 @@ import EditorSection from '../../Base/Section' import InputField from '../../../Parts/Fields/Input' import YesNoField from '../../../Parts/Fields/YesNo' import { useStore } from '../store' +import Box from '@mui/material/Box' export default function CkanSection() { const updateHelp = useStore((state) => state.updateHelp) return ( - updateHelp('ckan')}> - - - - + updateHelp('ckan')}> + + + + + + + + ) } diff --git a/client/components/Editors/Portal/Sections/Github.tsx b/client/components/Editors/Portal/Sections/Github.tsx index a3dadd40e..1785398c5 100644 --- a/client/components/Editors/Portal/Sections/Github.tsx +++ b/client/components/Editors/Portal/Sections/Github.tsx @@ -1,15 +1,21 @@ import EditorSection from '../../Base/Section' import InputField from '../../../Parts/Fields/Input' import { useStore } from '../store' +import Box from '@mui/material/Box' export default function GithubSection() { const updateHelp = useStore((state) => state.updateHelp) return ( - updateHelp('github')}> - - - - + updateHelp('github')}> + + {' '} + + + + + + {' '} + ) } diff --git a/client/components/Editors/Portal/Sections/Zenodo.tsx b/client/components/Editors/Portal/Sections/Zenodo.tsx index bfb1d6cc1..30b8ab2ce 100644 --- a/client/components/Editors/Portal/Sections/Zenodo.tsx +++ b/client/components/Editors/Portal/Sections/Zenodo.tsx @@ -2,15 +2,21 @@ import EditorSection from '../../Base/Section' import MultilineField from '../../../Parts/Fields/Multiline' import InputField from '../../../Parts/Fields/Input' import { useStore } from '../store' +import Box from '@mui/material/Box' export default function ZenodoSection() { const updateHelp = useStore((state) => state.updateHelp) return ( - updateHelp('zenodo')}> - - <Description /> - <Author /> - <Apikey /> + <EditorSection onHeadingClick={() => updateHelp('zenodo')}> + <Box sx={{ display: 'flex', justifyContent: 'space-around' }}> + {' '} + <Title /> + <Description /> + </Box> + <Box sx={{ display: 'flex', justifyContent: 'space-around' }}> + <Author /> + <Apikey /> + </Box> </EditorSection> ) } diff --git a/client/components/Parts/Cards/Help.tsx b/client/components/Parts/Cards/Help.tsx index 43df19bb1..aa9bfa1f6 100644 --- a/client/components/Parts/Cards/Help.tsx +++ b/client/components/Parts/Cards/Help.tsx @@ -1,9 +1,12 @@ import * as React from 'react' import Card from '@mui/material/Card' -import Button from '@mui/material/Button' +import Box from '@mui/material/Box' import Typography from '@mui/material/Typography' -import CardActions from '@mui/material/CardActions' +import Link from '@mui/material/Link' import CardContent from '@mui/material/CardContent' +import iconInfoImg from '../../../assets/icon_info.png' +import CardActions from '@mui/material/CardActions' +import Button from '@mui/material/Button' // TODO: review geometry porps/logic @@ -12,10 +15,41 @@ interface HelpCardProps { subtitle: string link: string height?: string + withIcon?: boolean } export default function HelpCard(props: React.PropsWithChildren<HelpCardProps>) { + if(props.withIcon) + return (<HelpCardWithIcon { ...props } />) + else return (<HelpCardNoIcon { ...props } />) + +} + +export function HelpCardWithIcon(props: React.PropsWithChildren<HelpCardProps>){ return ( + <Card + variant="outlined" + square={true} + sx={{ height: '100%', border: '1px solid #00D1FF', borderRadius: '8px', marginLeft: '12px', marginRight: '12px', marginBottom: '15px', backgroundColor: '#F5FDFE' }} + > + <CardContent sx={{ display: 'flex', + padding: '18px !important' + }}> + <Box> + <img src={iconInfoImg} alt="Image Folder Dialog" /> + </Box> + <Typography variant="body2" sx={{ paddingLeft: '12px', paddingRight: '80px' }}>{props.children} + <Link href={props.link} target="_blank" underline="none"> + {' '}Learn More + </Link> + </Typography> + </CardContent> + </Card> + ) +} + +export function HelpCardNoIcon(props: React.PropsWithChildren<HelpCardProps>){ + return ( <Card variant="outlined" square={true} @@ -28,20 +62,20 @@ export default function HelpCard(props: React.PropsWithChildren<HelpCardProps>) gutterBottom > Help - </Typography> - <Typography variant="h5" component="div"> + </Typography> + <Typography variant="h5" component="div"> {props.title} </Typography> <Typography sx={{ mb: 1.5, opacity: 0.5 }} color="text.primary"> {props.subtitle} </Typography> <Typography variant="body2">{props.children}</Typography> - </CardContent> - <CardActions sx={{ pt: 0 }}> + </CardContent> + <CardActions sx={{ pt: 0 }}> <Button size="small" component="a" target="_blank" href={props.link}> Learn More </Button> </CardActions> </Card> - ) -} + ) +} \ No newline at end of file diff --git a/client/components/Parts/Dialogs/Note.tsx b/client/components/Parts/Dialogs/Note.tsx index 4f235eaed..03124b24e 100644 --- a/client/components/Parts/Dialogs/Note.tsx +++ b/client/components/Parts/Dialogs/Note.tsx @@ -67,4 +67,4 @@ export default function NoteDialog(props: NoteDialogProps) { </Box> </Dialog> ) -} +} \ No newline at end of file diff --git a/client/components/Parts/Dialogs/OneButton.tsx b/client/components/Parts/Dialogs/OneButton.tsx index 2fb3c8c14..b5691aa50 100644 --- a/client/components/Parts/Dialogs/OneButton.tsx +++ b/client/components/Parts/Dialogs/OneButton.tsx @@ -82,7 +82,7 @@ export default function OneButtonDialog(props: OneButtonDialogProps) { </Box> )} </DialogContent> - <Box sx={{ paddingX: 3, paddingY: 2, display: 'flex', justifyContent: 'flex-end' }}> + <Box sx={{ marginX: props.title === 'Publish Dataset' ? '48px' : 'unset', paddingX: 3, paddingY: 2, display: 'flex', justifyContent: 'flex-end' }}> <SimpleButton fullWidth label={props.label || 'Confirm'} diff --git a/client/components/Parts/Dialogs/Pick.tsx b/client/components/Parts/Dialogs/Pick.tsx index ab75033a0..4ed2113c8 100644 --- a/client/components/Parts/Dialogs/Pick.tsx +++ b/client/components/Parts/Dialogs/Pick.tsx @@ -7,7 +7,7 @@ import ListItemText from '@mui/material/ListItemText' import ListItemIcon from '@mui/material/ListItemIcon' import Checkbox from '@mui/material/Checkbox' import Divider from '@mui/material/Divider' -import ConfirmDialog, { OneButtonDialogProps } from './OneButton' +import OneButtonDialog, { OneButtonDialogProps } from './OneButton' interface PickDialogProps extends Omit<OneButtonDialogProps, 'onConfirm'> { items: string[] @@ -106,14 +106,14 @@ export default function PickDialog(props: PickDialogProps) { ) return ( - <ConfirmDialog + <OneButtonDialog {...rest} onConfirm={() => onConfirm(propsItems.filter((_, index) => leftChecked.includes(index))) } > {customList('Items', available)} - </ConfirmDialog> + </OneButtonDialog> ) } diff --git a/client/components/Parts/Dialogs/TwoButton.tsx b/client/components/Parts/Dialogs/TwoButton.tsx index 901ffd0a9..7f34f8b27 100644 --- a/client/components/Parts/Dialogs/TwoButton.tsx +++ b/client/components/Parts/Dialogs/TwoButton.tsx @@ -25,7 +25,7 @@ export interface TwoButtonDialogProps { disableClosing?: boolean } -export default function ConfirmDialog(props: TwoButtonDialogProps) { +export default function TwoButtonDialog(props: TwoButtonDialogProps) { const handleCancel = () => props.onCancel && props.onCancel() const handleConfirm = () => props.onConfirm && props.onConfirm() diff --git a/client/components/Parts/Fields/Input.tsx b/client/components/Parts/Fields/Input.tsx index 65c2a1383..a45878b12 100644 --- a/client/components/Parts/Fields/Input.tsx +++ b/client/components/Parts/Fields/Input.tsx @@ -1,5 +1,6 @@ import noop from 'lodash/noop' import TextField from '@mui/material/TextField' +import { styled } from '@mui/material/styles' // TODO: rework all the fields wrappign props (see buttons) // TODO: fix it loosing focus @@ -30,14 +31,14 @@ export default function InputField(props: InputFieldProps) { const onFocus = props.onFocus || noop const onBlur = props.onBlur || noop return ( - <TextField - fullWidth + <StyledTextField name={props.name || props.label} type={props.type} margin="normal" label={props.label} value={props.value} size={props.size || 'small'} + style={{ maxWidth: '350px' }} disabled={props.disabled} inputProps={props.inputProps} onChange={(ev) => onChange(ev.target.value)} @@ -52,3 +53,25 @@ export default function InputField(props: InputFieldProps) { /> ) } + + +export const StyledTextField = styled(TextField)(() => ({ + width: '100%', + '& label.Mui-focused': { + color: '#00D1FF', + }, + '& .MuiInput-underline:after': { + borderBottomColor: '#00D1FF', + }, + '& .MuiOutlinedInput-root': { + '& fieldset': { + borderColor: 'gray', + }, + '&:hover fieldset': { + borderColor: '#00D1FF', + }, + '&.Mui-focused fieldset': { + borderColor: '#00D1FF', + }, + }, +})) \ No newline at end of file diff --git a/client/components/Parts/Fields/Multiline.tsx b/client/components/Parts/Fields/Multiline.tsx index 5b38191c6..d6ae45ca5 100644 --- a/client/components/Parts/Fields/Multiline.tsx +++ b/client/components/Parts/Fields/Multiline.tsx @@ -1,5 +1,5 @@ -import TextField from '@mui/material/TextField' import noop from 'lodash/noop' +import { StyledTextField } from './Input' // TODO: shall we open a modal editor for this field? @@ -20,7 +20,7 @@ interface MultilineFieldProps { export default function MultilineField(props: MultilineFieldProps) { const onFocus = props.onFocus || noop return ( - <TextField + <StyledTextField multiline fullWidth placeholder={props.placeholder} @@ -28,6 +28,7 @@ export default function MultilineField(props: MultilineFieldProps) { rows={props.rows} type={props.type} margin="normal" + style={{ maxWidth: '350px' }} label={props.label} value={props.value} size={props.size || 'small'} @@ -37,4 +38,4 @@ export default function MultilineField(props: MultilineFieldProps) { required={props.required} /> ) -} +} \ No newline at end of file diff --git a/client/components/Parts/Fields/Multiselect.tsx b/client/components/Parts/Fields/Multiselect.tsx index f351bc11d..a26ae6417 100644 --- a/client/components/Parts/Fields/Multiselect.tsx +++ b/client/components/Parts/Fields/Multiselect.tsx @@ -1,6 +1,6 @@ import noop from 'lodash/noop' import MenuItem from '@mui/material/MenuItem' -import TextField from '@mui/material/TextField' +import { StyledTextField } from './Input' // TODO: rework? merge with SelectField? // TODO: handle different value types properly (string/number/etc) @@ -22,7 +22,7 @@ export default function MultiselectField(props: MultiselectFieldProps) { ) return ( - <TextField + <StyledTextField select fullWidth label={props.label} @@ -39,6 +39,6 @@ export default function MultiselectField(props: MultiselectFieldProps) { {option.label} </MenuItem> ))} - </TextField> + </StyledTextField> ) } diff --git a/client/components/Parts/Fields/Select.tsx b/client/components/Parts/Fields/Select.tsx index 8112dd32b..5bacc78ef 100644 --- a/client/components/Parts/Fields/Select.tsx +++ b/client/components/Parts/Fields/Select.tsx @@ -1,6 +1,6 @@ import MenuItem from '@mui/material/MenuItem' -import TextField from '@mui/material/TextField' import { noop } from 'lodash' +import { StyledTextField } from './Input' // TODO: rework? merge with SelectField? // TODO: handle different value types properly (string/number/etc) @@ -27,7 +27,7 @@ export default function SelectField(props: SelectFieldProps) { typeof option === 'string' ? { label: option || 'select', value: option } : option ) return ( - <TextField + <StyledTextField select fullWidth label={props.label} @@ -46,6 +46,6 @@ export default function SelectField(props: SelectFieldProps) { {option.label} </MenuItem> ))} - </TextField> + </StyledTextField> ) } diff --git a/client/components/Parts/Fields/YesNo.tsx b/client/components/Parts/Fields/YesNo.tsx index e7cd5626a..5c251bb74 100644 --- a/client/components/Parts/Fields/YesNo.tsx +++ b/client/components/Parts/Fields/YesNo.tsx @@ -1,6 +1,6 @@ import noop from 'lodash/noop' -import TextField from '@mui/material/TextField' import MenuItem from '@mui/material/MenuItem' +import { StyledTextField } from './Input' interface YesNoFieldProps { label: string @@ -13,9 +13,9 @@ interface YesNoFieldProps { export default function YesNoField(props: YesNoFieldProps) { const onFocus = props.onFocus || noop return ( - <TextField + <StyledTextField select - fullWidth + style= {{ maxWidth: '350px' }} margin="normal" size={props.size || 'small'} label={props.label} @@ -25,6 +25,6 @@ export default function YesNoField(props: YesNoFieldProps) { > <MenuItem value={'yes'}>Yes</MenuItem> <MenuItem value={'no'}>No</MenuItem> - </TextField> + </StyledTextField> ) } diff --git a/client/components/Parts/Tabs/Dialog.tsx b/client/components/Parts/Tabs/Dialog.tsx new file mode 100644 index 000000000..e05d37e24 --- /dev/null +++ b/client/components/Parts/Tabs/Dialog.tsx @@ -0,0 +1,95 @@ +import * as React from 'react' +import Box from '@mui/material/Box' +import Tabs from '@mui/material/Tabs' +import Tab from '@mui/material/Tab' + +export default function DialogTabs(props: any) { + + const [currentTabIndex, setCurrentTabIndex] = React.useState(0) + + function a11yProps(index: number) { + return { + id: `simple-tab-${index}`, + 'aria-controls': `simple-tabpanel-${index}`, + } + } + + // by default the height of the tabs is set by its content, to avoid the height jump + // when changing the tabs we assign the second tab whatever is the height of the first one + const tabRefForHeight = React.useRef<HTMLDivElement>(null) + + // the event needs to be passed even if not used, disabling here so there's + // no unused variable error in the typescript check + // @ts-ignore + const handleChange = (event: React.SyntheticEvent, newValue: number) => { + setCurrentTabIndex(newValue) + } + + const isWebkitDirectorySupported = 'webkitdirectory' in document.createElement('input') + if (!isWebkitDirectorySupported) return null + + return ( + <Box> + <Box sx={{ borderBottom: 1, borderColor: 'divider' }}> + <Tabs + value={currentTabIndex} + onChange={handleChange} + aria-label="File Upload Tabs" + centered + sx={{ + '& .MuiTabs-indicator': { + backgroundColor: (theme) => theme.palette.OKFNBlue.main, + }, + '& .MuiButtonBase-root.MuiTab-root': { + color: (theme) => theme.palette.OKFNGray500.main, + transition: 'color 0.2s ease-in-out', + '&:hover': { + opacity: 0.7, + }, + '&.Mui-selected': { + color: (theme) => theme.palette.OKFNBlue.main, + }, + }, + }} + > + {props.labels.map((label: string, index: number) => ( + <Tab + key={label} + label={label} + sx={{ textTransform: 'capitalize' }} + {...a11yProps(index)} + /> + ))} + </Tabs> + </Box> + {React.Children.map(props.children, (child, index) => ( + <CustomTabPanel value={currentTabIndex} index={index}> + <Box ref={tabRefForHeight}> + {child} + </Box> + </CustomTabPanel> + ))} + + </Box> + ) +} + +function CustomTabPanel(props: { + children?: React.ReactNode + index: number + value: number +}) { + const { children, value, index, ...other } = props + + return ( + <div + role="tabpanel" + hidden={value !== index} + id={`simple-tabpanel-${index}`} + aria-labelledby={`simple-tab-${index}`} + {...other} + > + {value === index && <Box sx={{ p: 3 }}>{children}</Box>} + </div> + ) +} \ No newline at end of file