Skip to content

Commit 449e358

Browse files
authored
Merge pull request #269 from marcosmoura/components/mdFile
Components/md file
2 parents 28e47c7 + a359301 commit 449e358

File tree

11 files changed

+338
-10
lines changed

11 files changed

+338
-10
lines changed

docs/src/App.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@
7171
<router-link exact to="/components/dialog">Dialog</router-link>
7272
</md-list-item>
7373

74+
<md-list-item class="md-inset">
75+
<router-link exact to="/components/file">File</router-link>
76+
</md-list-item>
77+
7478
<md-list-item class="md-inset">
7579
<router-link exact to="/components/icon">Icon</router-link>
7680
</md-list-item>

docs/src/pages/components/File.vue

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<template>
2+
<page-content page-title="Components - File">
3+
<docs-component>
4+
<div slot="description">
5+
<p>The file picker aim to select files like images, videos and other formats. They can have multiselection and use the devide file system to pick the file.</p>
6+
</div>
7+
8+
<div slot="api">
9+
<api-table name="md-file">
10+
<md-table slot="properties">
11+
<md-table-header>
12+
<md-table-row>
13+
<md-table-head>Name</md-table-head>
14+
<md-table-head>Type</md-table-head>
15+
<md-table-head>Description</md-table-head>
16+
</md-table-row>
17+
</md-table-header>
18+
19+
<md-table-body>
20+
<md-table-row>
21+
<md-table-cell>v-model</md-table-cell>
22+
<md-table-cell><code>String</code></md-table-cell>
23+
<md-table-cell>A required model object to bind the value.</md-table-cell>
24+
</md-table-row>
25+
26+
<md-table-row>
27+
<md-table-cell>id</md-table-cell>
28+
<md-table-cell><code>String</code></md-table-cell>
29+
<md-table-cell>Sets the input id.</md-table-cell>
30+
</md-table-row>
31+
32+
<md-table-row>
33+
<md-table-cell>name</md-table-cell>
34+
<md-table-cell><code>String</code></md-table-cell>
35+
<md-table-cell>Sets the input name.</md-table-cell>
36+
</md-table-row>
37+
38+
<md-table-row>
39+
<md-table-cell>disabled</md-table-cell>
40+
<md-table-cell><code>Boolean</code></md-table-cell>
41+
<md-table-cell>Disable the input and prevent his actions. Default <code>false</code></md-table-cell>
42+
</md-table-row>
43+
44+
<md-table-row>
45+
<md-table-cell>required</md-table-cell>
46+
<md-table-cell><code>Boolean</code></md-table-cell>
47+
<md-table-cell>Apply the required rule to style the label with an "*". Default <code>false</code></md-table-cell>
48+
</md-table-row>
49+
50+
<md-table-row>
51+
<md-table-cell>accept</md-table-cell>
52+
<md-table-cell><code>String</code></md-table-cell>
53+
<md-table-cell>Filter files that can be selected by mimetype pattern.</md-table-cell>
54+
</md-table-row>
55+
56+
<md-table-row>
57+
<md-table-cell>multiple</md-table-cell>
58+
<md-table-cell><code>Boolean</code></md-table-cell>
59+
<md-table-cell>Enable multiple selection.</md-table-cell>
60+
</md-table-row>
61+
</md-table-body>
62+
</md-table>
63+
</api-table>
64+
</div>
65+
66+
<div slot="example">
67+
<example-box card-title="Single File">
68+
<div slot="demo">
69+
<md-input-container>
70+
<label>Single</label>
71+
<md-file v-model="single"></md-file>
72+
</md-input-container>
73+
74+
<md-input-container>
75+
<md-file v-model="placeholder" placeholder="A nice input placeholder"></md-file>
76+
</md-input-container>
77+
78+
<md-input-container>
79+
<md-file placeholder="Disabled" disabled></md-file>
80+
</md-input-container>
81+
82+
<md-input-container>
83+
<label>Initial Value</label>
84+
<md-file v-model="initialValue"></md-file>
85+
</md-input-container>
86+
87+
<md-input-container>
88+
<label>Multiple</label>
89+
<md-file v-model="multiple" multiple></md-file>
90+
</md-input-container>
91+
92+
<md-input-container>
93+
<label>Only Images</label>
94+
<md-file v-model="onlyImages" accept="image/*"></md-file>
95+
</md-input-container>
96+
</div>
97+
98+
<div slot="code">
99+
<code-block lang="xml">
100+
101+
</code-block>
102+
</div>
103+
</example-box>
104+
</div>
105+
</docs-component>
106+
</page-content>
107+
</template>
108+
109+
<script>
110+
export default {
111+
data: () => ({
112+
single: null,
113+
placeholder: null,
114+
initialValue: 'my-profile-picture.jpg',
115+
multiple: null,
116+
onlyImages: null
117+
}),
118+
methods: {
119+
120+
}
121+
};
122+
</script>

docs/src/routes.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const Card = (resolve) => require(['./pages/components/Card'], resolve);
1414
const Checkbox = (resolve) => require(['./pages/components/Checkbox'], resolve);
1515
const Chips = (resolve) => require(['./pages/components/Chips'], resolve);
1616
const Dialog = (resolve) => require(['./pages/components/Dialog'], resolve);
17+
const File = (resolve) => require(['./pages/components/File'], resolve);
1718
const Icon = (resolve) => require(['./pages/components/Icon'], resolve);
1819
const ImageLoader = (resolve) => require(['./pages/components/ImageLoader'], resolve);
1920
const InkRipple = (resolve) => require(['./pages/components/InkRipple'], resolve);
@@ -110,6 +111,11 @@ const components = [
110111
name: 'components:dialog',
111112
component: Dialog
112113
},
114+
{
115+
path: '/components/file',
116+
name: 'components:file',
117+
component: File
118+
},
113119
{
114120
path: '/components/icon',
115121
name: 'components:icon',

src/components/mdFile/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import mdFile from './mdFile.vue';
2+
import mdFileTheme from './mdFile.theme';
3+
4+
export default function install(Vue) {
5+
Vue.component('md-file', Vue.extend(mdFile));
6+
7+
Vue.material.styles.push(mdFileTheme);
8+
}

src/components/mdFile/mdFile.scss

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
@import '../../core/stylesheets/variables.scss';
2+
3+
.md-file {
4+
display: flex;
5+
flex: 1;
6+
7+
input[type="file"] {
8+
width: 1px;
9+
height: 1px;
10+
margin: -1px;
11+
padding: 0;
12+
overflow: hidden;
13+
position: absolute;
14+
clip: rect(0 0 0 0);
15+
border: 0;
16+
}
17+
18+
.md-icon {
19+
cursor: pointer;
20+
}
21+
}

src/components/mdFile/mdFile.theme

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.THEME_NAME {
2+
&.md-file {
3+
4+
}
5+
}

src/components/mdFile/mdFile.vue

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<template>
2+
<div class="md-file" @click="openPicker">
3+
<md-input
4+
readonly
5+
v-model="filename"
6+
:required="required"
7+
:placeholder="placeholder"
8+
:disabled="disabled"
9+
ref="textInput">
10+
</md-input>
11+
12+
<md-icon>attach_file</md-icon>
13+
14+
<input
15+
type="file"
16+
:id="id"
17+
:name="name"
18+
:disabled="disabled"
19+
:multiple="multiple"
20+
:accept="accept"
21+
@change="onFileSelected"
22+
ref="fileInput">
23+
</div>
24+
</template>
25+
26+
<style lang="scss" src="./mdFile.scss"></style>
27+
28+
<script>
29+
import getClosestVueParent from '../../core/utils/getClosestVueParent';
30+
31+
export default {
32+
props: {
33+
value: String,
34+
id: String,
35+
name: String,
36+
disabled: Boolean,
37+
required: Boolean,
38+
placeholder: String,
39+
accept: String,
40+
multiple: Boolean
41+
},
42+
data() {
43+
return {
44+
filename: this.value
45+
};
46+
},
47+
watch: {
48+
value() {
49+
this.filename = this.value;
50+
}
51+
},
52+
methods: {
53+
getMultipleName(files) {
54+
let names = [];
55+
56+
[...files].forEach((file) => {
57+
names.push(file.name);
58+
});
59+
60+
return names.join(', ');
61+
},
62+
openPicker() {
63+
if (!this.disabled) {
64+
this.$refs.fileInput.click();
65+
this.$refs.textInput.$el.focus();
66+
}
67+
},
68+
onFileSelected($event) {
69+
const files = $event.target.files || $event.dataTransfer.files;
70+
71+
if (files) {
72+
if (files.length > 1) {
73+
this.filename = this.getMultipleName(files);
74+
} else if (files.length === 1) {
75+
this.filename = files[0].name;
76+
}
77+
} else {
78+
this.filename = $event.target.value.split('\\').pop();
79+
}
80+
81+
this.$emit('selected', files || $event.target.value);
82+
this.$emit('input', this.filename);
83+
}
84+
},
85+
mounted() {
86+
this.parentContainer = getClosestVueParent(this.$parent, 'md-input-container');
87+
88+
if (!this.parentContainer) {
89+
this.$destroy();
90+
91+
throw new Error('You should wrap the md-file in a md-input-container');
92+
}
93+
94+
this.parentContainer.hasFile = true;
95+
},
96+
beforeDestroy() {
97+
this.parentContainer.hasFile = false;
98+
}
99+
};
100+
</script>

src/components/mdInputContainer/mdInputContainer.scss

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ $input-size: 32px;
77
min-height: 48px;
88
margin: 4px 0 24px;
99
padding-top: 16px;
10+
display: flex;
1011
position: relative;
1112

1213
&:after {
@@ -38,6 +39,7 @@ $input-size: 32px;
3839
height: $input-size;
3940
padding: 0;
4041
display: block;
42+
flex: 1;
4143
border: none;
4244
background: none;
4345
transition: $swift-ease-out;
@@ -57,6 +59,15 @@ $input-size: 32px;
5759
text-shadow: none;
5860
-webkit-text-fill-color: initial;
5961
}
62+
63+
~ .md-icon {
64+
margin-left: 12px;
65+
66+
&:after {
67+
right: 0;
68+
left: auto;
69+
}
70+
}
6071
}
6172

6273
textarea {
@@ -67,21 +78,50 @@ $input-size: 32px;
6778
line-height: 1.3em;
6879
}
6980

70-
.md-error {
81+
.md-error,
82+
.md-count {
7183
height: 20px;
72-
display: block !important;
7384
position: absolute;
85+
top: 50px;
86+
font-size: 12px;
87+
}
88+
89+
.md-error {
90+
display: block !important;
7491
opacity: 0;
7592
transform: translate3d(0, -8px, 0);
7693
transition: $swift-ease-in;
77-
font-size: 12px;
7894
}
7995

8096
.md-count {
81-
height: 20px;
82-
position: absolute;
8397
right: 0;
84-
font-size: 12px;
98+
}
99+
100+
.md-icon {
101+
color: rgba(#000, .54);
102+
transition: $swift-ease-out;
103+
104+
&:after {
105+
width: 36px;
106+
height: 2px;
107+
position: absolute;
108+
left: 0;
109+
bottom: 0;
110+
z-index: 2;
111+
content: "";
112+
}
113+
114+
~ {
115+
label {
116+
left: 36px;
117+
}
118+
119+
.md-input,
120+
.md-textarea,
121+
.md-file {
122+
margin-left: 12px;
123+
}
124+
}
85125
}
86126
}
87127

0 commit comments

Comments
 (0)