mirror of
https://github.com/Hopiu/vue-material.git
synced 2026-05-16 19:21:07 +00:00
Create select menu and add core directive to handle outside clicks
This commit is contained in:
parent
5e00d77482
commit
cd77016eaf
33 changed files with 301 additions and 76 deletions
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
$avatar-size: 40px;
|
||||
$avatar-large-size: 64px;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
.md-bottom-bar {
|
||||
width: 100%;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
$button-width: 88px;
|
||||
$button-height: 36px;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
.md-button-toggle {
|
||||
.md-button:not([disabled]) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
$checkbox-size: 20px;
|
||||
$checkbox-ripple-size: 48px;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
.md-divider {
|
||||
height: 1px;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
$icon-size: 24px;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
.md-ink-ripple {
|
||||
pointer-events: none;
|
||||
|
|
|
|||
|
|
@ -115,9 +115,6 @@
|
|||
managePlaceholderClass(this.placeholder, this.parentClasses);
|
||||
manageHasValueClass(this.$el.value, this.parentClasses);
|
||||
manageMaxlength(this.maxlength, this.$parent);
|
||||
},
|
||||
beforeDestroy() {
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
$input-size: 32px;
|
||||
|
||||
|
|
@ -192,11 +192,9 @@ $input-size: 32px;
|
|||
}
|
||||
}
|
||||
|
||||
&.md-has-select {
|
||||
&:hover {
|
||||
label {
|
||||
color: rgba(#000, .87);
|
||||
}
|
||||
&.md-has-select:hover {
|
||||
.md-select:after {
|
||||
color: rgba(#000, .87);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
.md-list {
|
||||
margin: 0;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
$radio-size: 20px;
|
||||
$radio-ripple-size: 48px;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import mdSelect from './mdSelect.vue';
|
||||
import mdOption from './mdOption.vue';
|
||||
import mdSelectTheme from './mdSelect.theme';
|
||||
|
||||
export default function install(Vue) {
|
||||
Vue.component('md-select', Vue.extend(mdSelect));
|
||||
Vue.component('md-option', Vue.extend(mdOption));
|
||||
|
||||
window.VueMaterial.styles.push(mdSelectTheme);
|
||||
}
|
||||
|
|
|
|||
28
src/components/mdSelect/mdOption.vue
Normal file
28
src/components/mdSelect/mdOption.vue
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<template>
|
||||
<li class="md-option" @click="selectOption" v-md-ink-ripple>
|
||||
<slot></slot>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Boolean],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectOption() {
|
||||
this.$parent.selectOption(this.value);
|
||||
}
|
||||
},
|
||||
ready() {
|
||||
if (!this.$parent.$el.classList.contains('md-select')) {
|
||||
this.$destroy();
|
||||
|
||||
throw new Error('You should wrap the md-option in a md-select');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,16 +1,13 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
.md-select {
|
||||
width: 100%;
|
||||
min-width: 128px;
|
||||
|
||||
&:hover {
|
||||
&:after {
|
||||
color: rgba(#000, .87);
|
||||
}
|
||||
}
|
||||
height: 32px;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
margin-top: 9px;
|
||||
margin-top: 2px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
|
|
@ -20,12 +17,104 @@
|
|||
content: "\25BC";
|
||||
}
|
||||
|
||||
&.md-active {
|
||||
.md-select-menu {
|
||||
top: -8px;
|
||||
pointer-events: auto;
|
||||
opacity: 1;
|
||||
transform: translateY(-8px) scale3D(1, 1, 1);
|
||||
transform-origin: center top;
|
||||
transition: $swift-ease-out;
|
||||
transition-duration: .25s;
|
||||
transition-property: opacity, transform, top;
|
||||
|
||||
> * {
|
||||
opacity: 1;
|
||||
transition: $swift-ease-in;
|
||||
transition-duration: .15s;
|
||||
transition-delay: .1s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
position: absolute;
|
||||
left: -999em;
|
||||
}
|
||||
|
||||
.md-select-value {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
font-size: 16px;
|
||||
line-height: 1.2em;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.md-select-menu {
|
||||
display: none;
|
||||
min-width: 156px;
|
||||
max-width: 100%;
|
||||
min-height: 48px;
|
||||
max-height: 256px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: stretch;
|
||||
align-content: stretch;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: -16px;
|
||||
left: -16px;
|
||||
z-index: 7;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
box-shadow: $material-shadow-2dp;
|
||||
opacity: 0;
|
||||
transform: scale3D(.85, .7, 1);
|
||||
transition: opacity .25s $swift-ease-in-timing-function,
|
||||
top .25s $swift-ease-in-timing-function,
|
||||
transform 0s .25s $swift-ease-in-timing-function;
|
||||
|
||||
> * {
|
||||
opacity: 0;
|
||||
transition: $swift-ease-out;
|
||||
transition-duration: .25s;
|
||||
}
|
||||
}
|
||||
|
||||
.md-select-menu-container {
|
||||
margin: 0;
|
||||
padding: 8px 0;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: stretch;
|
||||
align-content: stretch;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.md-option {
|
||||
height: 48px;
|
||||
min-height: 48px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: $swift-ease-out;
|
||||
font-size: 16px;
|
||||
line-height: 1.2em;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(#000, .12);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,33 @@
|
|||
<template>
|
||||
<div class="md-select" :class="classes">
|
||||
<div class="md-select-value">
|
||||
<select v-model="model" :name="name" :id="id" :disabled="disabled" :value="value">
|
||||
<option v-for="option in selectedOptions" :value="option.value">{{ options.text }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="md-select" :class="classes" v-on-clickaway="hideMenu" @invalid="onInvalid" @valid="onValid">
|
||||
<span class="md-select-value" @click="showMenu">{{ model }}</span>
|
||||
|
||||
<div class="md-select-menu">
|
||||
<slot></slot>
|
||||
<ul class="md-select-menu-container">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<select v-model.sync="model" :name="name" :id="id" :required="required">
|
||||
<option selected="true" :value="model">{{ model }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" src="./mdSelect.scss"></style>
|
||||
|
||||
<script>
|
||||
let hasValueClass = 'md-has-value';
|
||||
let invalidClass = 'md-input-invalid';
|
||||
|
||||
let handleModelValue = (target, value) => {
|
||||
if (value) {
|
||||
target.add(hasValueClass);
|
||||
} else {
|
||||
target.remove(hasValueClass);
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
props: {
|
||||
model: {
|
||||
|
|
@ -22,25 +35,59 @@
|
|||
required: true,
|
||||
twoWay: true
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
required: Boolean,
|
||||
value: String,
|
||||
name: String,
|
||||
id: String,
|
||||
disabled: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedOptions: []
|
||||
active: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
classes() {
|
||||
return {
|
||||
'md-disabled': this.disabled
|
||||
'md-disabled': this.disabled,
|
||||
'md-active': this.active
|
||||
};
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
model(value) {
|
||||
handleModelValue(this.$parent.$el.classList, value);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onInvalid() {
|
||||
this.$parent.$el.classList.add(invalidClass);
|
||||
},
|
||||
onValid() {
|
||||
this.$parent.$el.classList.remove(invalidClass);
|
||||
},
|
||||
showMenu() {
|
||||
this.active = true;
|
||||
},
|
||||
hideMenu() {
|
||||
this.active = false;
|
||||
},
|
||||
selectOption(option) {
|
||||
this.model = option;
|
||||
this.hideMenu();
|
||||
}
|
||||
},
|
||||
ready() {
|
||||
if (!this.$parent.$el.classList.contains('md-input-container')) {
|
||||
this.$destroy();
|
||||
|
||||
throw new Error('You should wrap the md-select in a md-input-container');
|
||||
}
|
||||
|
||||
handleModelValue(this.$parent.$el.classList, this.model);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
.md-sidenav {
|
||||
&.md-left .md-sidenav-content {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
.md-subheader {
|
||||
min-height: 48px;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
$switch-width: 34px;
|
||||
$switch-height: 14px;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
.md-toolbar {
|
||||
min-height: 64px;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
$tooltip-height: 20px;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
.md-whiteframe {
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -1,25 +1,29 @@
|
|||
<script>
|
||||
import CoreTheme from './core.theme';
|
||||
import Vue from 'vue';
|
||||
import CoreTheme from './stylesheets/core.theme';
|
||||
import clickaway from './directives/clickaway';
|
||||
|
||||
window.VueMaterial = {
|
||||
styles: [CoreTheme]
|
||||
};
|
||||
|
||||
Vue.directive('onClickaway', clickaway);
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
/* Common mixins */
|
||||
@import 'utils/mixins';
|
||||
@import './stylesheets/utils/mixins';
|
||||
|
||||
|
||||
/* Commons */
|
||||
@import 'utils/commons';
|
||||
@import './stylesheets/utils/commons';
|
||||
|
||||
|
||||
/* Variables */
|
||||
@import 'variables';
|
||||
@import './stylesheets/variables';
|
||||
|
||||
|
||||
/* Core Styles */
|
||||
@import 'structure';
|
||||
@import 'type';
|
||||
@import './stylesheets/structure';
|
||||
@import './stylesheets/type';
|
||||
</style>
|
||||
|
|
|
|||
26
src/core/directives/clickaway.js
Normal file
26
src/core/directives/clickaway.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import Vue from 'vue';
|
||||
|
||||
export default {
|
||||
acceptStatement: true,
|
||||
priority: 700,
|
||||
update(handler) {
|
||||
var self = this;
|
||||
|
||||
this.handler = function(ev) {
|
||||
if (!self.el.contains(ev.target)) {
|
||||
let res = handler(ev);
|
||||
|
||||
ev.targetVM = self.vm;
|
||||
self.vm.$event = ev;
|
||||
|
||||
self.vm.$event = null;
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
Vue.util.on(document.documentElement, 'click', this.handler);
|
||||
},
|
||||
unbind() {
|
||||
Vue.util.off(document.documentElement, 'click', this.handler);
|
||||
}
|
||||
};
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
body {
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
color: rgba(#000, .87);
|
||||
font-family: $font-roboto;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
letter-spacing: .010em;
|
||||
line-height: 20px;
|
||||
}
|
||||
46
src/core/stylesheets/structure.scss
Normal file
46
src/core/stylesheets/structure.scss
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
body {
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
color: rgba(#000, .87);
|
||||
font-family: $font-roboto;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
letter-spacing: .010em;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
box-shadow: inset 1px 1px 0 rgba(0, 0, 0, .1);
|
||||
transition: $swift-ease-in-out;
|
||||
background-color: rgba(0, 0, 0, .05);
|
||||
|
||||
&:hover {
|
||||
box-shadow: inset 1px 1px 0 rgba(0, 0, 0, .05),
|
||||
inset 0 -1px 0 rgba(0, 0, 0, .03);
|
||||
background-color: rgba(0, 0, 0, .08);
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, .2);
|
||||
box-shadow: inset 1px 1px 0 rgba(0, 0, 0, .05),
|
||||
inset 0 -1px 0 rgba(0, 0, 0, .07);
|
||||
transition: $swift-ease-in-out;
|
||||
}
|
||||
|
|
@ -580,7 +580,7 @@
|
|||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../core/variables.scss';
|
||||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
.custom-list {
|
||||
.md-list-action {
|
||||
|
|
|
|||
|
|
@ -2,24 +2,29 @@
|
|||
<section>
|
||||
<h2 class="title">Select</h2>
|
||||
|
||||
<div>
|
||||
<div class="field-group">
|
||||
<md-input-container>
|
||||
<label for="movies">Movies</label>
|
||||
<md-select :model.sync="movies" name="movies" id="movies">
|
||||
<label for="movie">Movie</label>
|
||||
<md-select :model.sync="movie" name="movie" id="movie">
|
||||
<md-option value="Fight Club">Fight Club</md-option>
|
||||
<md-option value="Godfather II">Godfather II</md-option>
|
||||
<md-option value="Godfather III">Godfather III</md-option>
|
||||
<md-option value="Godfather">Godfather</md-option>
|
||||
<md-option value="Godfellas">Godfellas</md-option>
|
||||
<md-option value="Pulp Fiction">Pulp Fiction</md-option>
|
||||
<md-option value="Scarface">Scarface</md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container>
|
||||
<label for="countries">Countries</label>
|
||||
<md-select :model.sync="countries" name="countries" id="countries">
|
||||
<label for="country">Country</label>
|
||||
<md-select :model.sync="country" name="country" id="country">
|
||||
<md-option value="Australia">Australia</md-option>
|
||||
<md-option value="Brazil">Brazil</md-option>
|
||||
<md-option value="Japan">Japan</md-option>
|
||||
<md-option value="United States">United States</md-option>
|
||||
|
||||
<span class="md-error">Validation message</span>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
</div>
|
||||
|
|
@ -31,7 +36,7 @@
|
|||
margin: 24px;
|
||||
}
|
||||
|
||||
div {
|
||||
.field-group {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
|
|
@ -44,8 +49,8 @@
|
|||
export default {
|
||||
data() {
|
||||
return {
|
||||
movies: 'Pulp Fiction',
|
||||
countries: null
|
||||
movie: 'Godfather',
|
||||
country: ''
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue