Skip to content

Commit

Permalink
Add FilePicker component
Browse files Browse the repository at this point in the history
  • Loading branch information
bytasv committed Jan 10, 2023
1 parent cc2fb60 commit 2020622
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import DateRangeIcon from '@mui/icons-material/DateRange';
import CheckBoxIcon from '@mui/icons-material/CheckBox';
import DashboardCustomizeSharpIcon from '@mui/icons-material/DashboardCustomizeSharp';
import AddIcon from '@mui/icons-material/Add';
import UploadFileIcon from '@mui/icons-material/UploadFile';
import NotesIcon from '@mui/icons-material/Notes';

import { SvgIconProps } from '@mui/material/SvgIcon';

const iconMap = new Map<string, React.ComponentType<SvgIconProps>>([
Expand All @@ -35,6 +37,7 @@ const iconMap = new Map<string, React.ComponentType<SvgIconProps>>([
['Switch', ToggleOnIcon],
['Radio', RadioButtonCheckedIcon],
['DatePicker', DateRangeIcon],
['FilePicker', UploadFileIcon],
['Checkbox', CheckBoxIcon],
['CodeComponent', DashboardCustomizeSharpIcon],
['CreateNew', AddIcon],
Expand Down
31 changes: 31 additions & 0 deletions packages/toolpad-app/src/toolpad/propertyControls/file.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Typography } from '@mui/material';
import * as React from 'react';
import type { EditorProps } from '../../types';

function FilePropEditor({ value = [] }: EditorProps<any[]>) {
const files = Array.from(value);
const hasSelectedFiles = files?.length > 0;

if (!hasSelectedFiles) {
return (
<Typography variant="body2" noWrap>
No files chosen
</Typography>
);
}

return (
<div style={{ width: '100%' }}>
<Typography variant="body2" paragraph sx={{ mb: 1, fontWeight: 700 }}>
Files:
</Typography>
{files.map(({ name }) => (
<Typography variant="body2" noWrap key={name} sx={{ mb: 0.5 }}>
{name}
</Typography>
))}
</div>
);
}

export default FilePropEditor;
4 changes: 4 additions & 0 deletions packages/toolpad-app/src/toolpad/propertyControls/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import SelectOptions from './SelectOptions';
import HorizontalAlign from './HorizontalAlign';
import VerticalAlign from './VerticalAlign';
import RowIdFieldSelect from './RowIdFieldSelect';
import file from './file';
import { EditorProps } from '../../types';

const propTypeControls: {
Expand All @@ -28,6 +29,7 @@ const propTypeControls: {
RowIdFieldSelect,
HorizontalAlign,
VerticalAlign,
file,
};

function getDefaultControlForType(propType: PropValueType): React.FC<EditorProps<any>> | null {
Expand All @@ -44,6 +46,8 @@ function getDefaultControlForType(propType: PropValueType): React.FC<EditorProps
return json;
case 'event':
return event;
case 'file':
return file;
default:
return null;
}
Expand Down
1 change: 1 addition & 0 deletions packages/toolpad-app/src/toolpadComponents/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const INTERNAL_COMPONENTS = new Map<string, ToolpadComponentDefinition>([
['DataGrid', { displayName: 'Data grid', builtIn: 'DataGrid' }],
['TextField', { displayName: 'Text field', builtIn: 'TextField' }],
['DatePicker', { displayName: 'Date picker', builtIn: 'DatePicker' }],
['FilePicker', { displayName: 'File picker', builtIn: 'FilePicker' }],
['Text', { displayName: 'Text', builtIn: 'Text' }],
['Select', { displayName: 'Select', builtIn: 'Select' }],
['Paper', { displayName: 'Paper', builtIn: 'Paper' }],
Expand Down
94 changes: 94 additions & 0 deletions packages/toolpad-components/src/FilePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import * as React from 'react';
import { TextField as MuiTextField, TextFieldProps as MuiTextFieldProps } from '@mui/material';
import { createComponent } from '@mui/toolpad-core';

interface FullFile {
name: string;
type: string;
size: number;
base64: null | string;
}

export type Props = MuiTextFieldProps & {
multiple: boolean;
onChange: (files: FullFile[]) => void;
};

const readFile = async (file: Blob): Promise<string> => {
return new Promise((resolve, reject) => {
const readerBase64 = new FileReader();

readerBase64.onload = (event) => {
if (!event.target) {
reject();
return;
}

resolve(event.target.result as string);
};

readerBase64.readAsDataURL(file);
});
};

function FilePicker({ multiple, onChange, ...props }: Props) {
const handleChange = async (changeEvent: React.ChangeEvent<HTMLInputElement>) => {
const filesPromises = Array.from(changeEvent.target.files || []).map(async (file) => {
const fullFile: FullFile = {
name: file.name,
type: file.type,
size: file.size,
base64: await readFile(file),
};

return fullFile;
});

const files = await Promise.all(filesPromises);

onChange(files);
};

return (
<MuiTextField
{...props}
type="file"
value={undefined}
inputProps={{ multiple }}
onChange={handleChange}
/>
);
}

export default createComponent(FilePicker, {
argTypes: {
value: {
typeDef: { type: 'file' },
onChangeProp: 'onChange',
},
label: {
typeDef: { type: 'string' },
},
variant: {
typeDef: { type: 'string', enum: ['outlined', 'filled', 'standard'] },
defaultValue: 'outlined',
},
size: {
typeDef: { type: 'string', enum: ['small', 'normal'] },
defaultValue: 'small',
},
multiple: {
typeDef: { type: 'boolean' },
defaultValue: true,
},
fullWidth: {
typeDef: { type: 'boolean' },
},
disabled: {
typeDef: { type: 'boolean' },
},
sx: {
typeDef: { type: 'object' },
},
},
});
2 changes: 2 additions & 0 deletions packages/toolpad-components/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@ export { default as Image } from './Image.js';

export { default as DatePicker } from './DatePicker.js';

export { default as FilePicker } from './FilePicker.js';

export { CUSTOM_COLUMN_TYPES, NUMBER_FORMAT_PRESETS, inferColumns, parseColumns } from './DataGrid';
export type { SerializableGridColumn, SerializableGridColumns, NumberFormat } from './DataGrid';
12 changes: 9 additions & 3 deletions packages/toolpad-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export type BindableAttrEntries = [string, BindableAttrValue<any>][];
export type SlotType = 'single' | 'multiple' | 'layout';

export interface ValueTypeBase {
type: 'string' | 'boolean' | 'number' | 'object' | 'array' | 'element' | 'event';
type: 'string' | 'boolean' | 'number' | 'object' | 'array' | 'element' | 'event' | 'file';
}

export interface StringValueType extends ValueTypeBase {
Expand Down Expand Up @@ -103,6 +103,10 @@ export interface ArrayValueType extends ValueTypeBase {
schema?: string;
}

export interface FileValueType extends ValueTypeBase {
type: 'file';
}

export interface ElementValueType extends ValueTypeBase {
type: 'element';
}
Expand Down Expand Up @@ -138,15 +142,17 @@ export interface ArgControlSpec {
| 'HorizontalAlign'
| 'VerticalAlign'
| 'event'
| 'RowIdFieldSelect'; // Row id field specialized select
| 'RowIdFieldSelect' // Row id field specialized select
| 'file';
}

type PrimitiveValueType =
| StringValueType
| NumberValueType
| BooleanValueType
| ObjectValueType
| ArrayValueType;
| ArrayValueType
| FileValueType;

export type PropValueType = PrimitiveValueType | ElementValueType | EventValueType;

Expand Down

0 comments on commit 2020622

Please sign in to comment.