Skip to content

Commit 1cb0615

Browse files
committed
Add support for file input (data-url)
1 parent dff47e7 commit 1cb0615

File tree

3 files changed

+156
-8
lines changed

3 files changed

+156
-8
lines changed

src/components/form.js

Lines changed: 142 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
export function FormInput({label, help_text, error, ...props}) {
1+
import Button from './buttons';
2+
3+
4+
export function FormInput({label, help_text, error, inputRef, ...props}) {
25

36
if (props.type === 'string')
47
props.type = 'text'
58

9+
if (inputRef)
10+
props.ref = inputRef;
11+
612
return (
713
<div>
814
{label && <label>{label}</label>}
@@ -86,7 +92,140 @@ export function FormSelectInput({label, help_text, error, value, options, ...pro
8692
);
8793
}
8894

95+
export function dataURItoBlob(dataURI) {
96+
// Split metadata from data
97+
const splitted = dataURI.split(",");
98+
// Split params
99+
const params = splitted[0].split(";");
100+
// Get mime-type from params
101+
const type = params[0].replace("data:", "");
102+
// Filter the name property from params
103+
const properties = params.filter(param => {
104+
return param.split("=")[0] === "name";
105+
});
106+
// Look for the name and use unknown if no name property.
107+
let name;
108+
if (properties.length !== 1) {
109+
name = "unknown";
110+
} else {
111+
// Because we filtered out the other property,
112+
// we only have the name case here.
113+
name = properties[0].split("=")[1];
114+
}
115+
116+
// Built the Uint8Array Blob parameter from the base64 string.
117+
const binary = atob(splitted[1]);
118+
const array = [];
119+
for (let i = 0; i < binary.length; i++) {
120+
array.push(binary.charCodeAt(i));
121+
}
122+
// Create the blob object
123+
const blob = new window.Blob([new Uint8Array(array)], { type });
124+
125+
return {blob, name};
126+
}
127+
128+
129+
130+
export class FormFileInput extends React.Component {
131+
constructor(props) {
132+
super(props);
133+
134+
this.state = {
135+
value: props.value,
136+
fileName: this.getFileName()
137+
};
138+
139+
this.inputRef = React.createRef();
140+
}
141+
142+
componentDidUpdate(prevProps, prevState) {
143+
if (this.props.value !== prevProps.value) {
144+
this.setState({
145+
value: this.props.value,
146+
fileName: this.getFileName()
147+
});
148+
}
149+
}
150+
151+
getFileName = () => {
152+
if (!this.props.value)
153+
return '';
154+
155+
if (this.props.type === 'data-url') {
156+
return this.extractFileInfo(this.props.value).name;
157+
} else if (this.props.type === 'file-url') {
158+
return this.props.value;
159+
} else {
160+
return 'Unknown file';
161+
}
162+
}
163+
164+
extractFileInfo = (dataURL) => {
165+
const {blob, name} = dataURItoBlob(dataURL);
166+
return {
167+
name: name,
168+
size: blob.size,
169+
type: blob.type
170+
}
171+
}
172+
173+
addNameToDataURL = (dataURL, name) => {
174+
return dataURL.replace(';base64', ';name=' + encodeURIComponent(name) + ';base64');
175+
}
89176

90-
export function FileInput(props) {
91-
return <FormInput {...props} />
177+
handleChange = (e) => {
178+
if (this.props.type === 'data-url') {
179+
180+
}
181+
182+
let file = e.target.files[0];
183+
let fileName = file.name
184+
185+
let reader = new FileReader();
186+
187+
reader.onload = () => {
188+
189+
// this.setState({src: reader.result});
190+
191+
// we create a fake event object
192+
let event = {
193+
target: {
194+
type: 'text',
195+
value: this.addNameToDataURL(reader.result, fileName),
196+
name: this.props.name
197+
}
198+
};
199+
200+
this.props.onChange(event);
201+
202+
}
203+
reader.readAsDataURL(file);
204+
205+
}
206+
207+
showFileBrowser = () => {
208+
this.inputRef.current.click();
209+
}
210+
211+
render() {
212+
let {label, value, ...props} = {value, ...this.props};
213+
props.type = 'file';
214+
props.onChange = this.handleChange;
215+
216+
return (
217+
<div>
218+
{label && <label>{label}</label>}
219+
<div className="rjf-file-field">
220+
{this.state.value &&
221+
<div className="rjf-current-file-name">Current file: <span>{this.state.fileName}</span></div>
222+
}
223+
{this.state.value && 'Change:'}
224+
<div className="rjf-file-field-input">
225+
<FormInput {...props} inputRef={this.inputRef} />
226+
</div>
227+
</div>
228+
</div>
229+
);
230+
}
92231
}

src/components/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import Button from './buttons';
2-
import {FormInput, FormCheckInput, FormRadioInput, FormSelectInput, FileInput} from './form';
2+
import {FormInput, FormCheckInput, FormRadioInput, FormSelectInput, FormFileInput} from './form';
33
import {FormRow, FormGroup} from './containers';
44

55
export {
66
Button,
7-
FormInput, FormCheckInput, FormRadioInput, FormSelectInput, FileInput,
7+
FormInput, FormCheckInput, FormRadioInput, FormSelectInput, FormFileInput,
88
FormRow, FormGroup
99
};

src/ui.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {getBlankData} from './data';
22
import {Button, FormInput, FormCheckInput, FormRadioInput, FormSelectInput,
3-
FormRow, FormGroup} from './components';
3+
FormFileInput, FormRow, FormGroup} from './components';
44
import {getVerboseName} from './util';
55

66

@@ -42,8 +42,17 @@ function FormField(props) {
4242

4343
switch (type) {
4444
case 'string':
45-
inputProps.type = 'text';
4645
InputField = FormInput;
46+
47+
if (props.schema.format) {
48+
if (props.schema.format === 'data-url' || props.schema.format === 'file-url') {
49+
InputField = FormFileInput;
50+
}
51+
inputProps.type = props.schema.format;
52+
}
53+
else {
54+
inputProps.type = 'text';
55+
}
4756
break;
4857
case 'number':
4958
inputProps.type = 'number';
@@ -77,7 +86,7 @@ function FormField(props) {
7786
<InputField
7887
{...inputProps}
7988
label={
80-
props.editable ? <span>{porps.schema.title} <Button className="edit" onClick={props.onEdit} title="Edit">Edit</Button></span>
89+
props.editable ? <span>{props.schema.title} <Button className="edit" onClick={props.onEdit} title="Edit">Edit</Button></span>
8190
:
8291
props.schema.title
8392
}

0 commit comments

Comments
 (0)