mirror of
https://gitlab.com/CIEFWorldwideSdnBhd/unity.git
synced 2026-08-19 04:13:58 +00:00
user UI setup
user login UI
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<component :is="layout">
|
||||
<transition name="fade" mode="out-in">
|
||||
<router-view></router-view>
|
||||
</transition>
|
||||
</component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const default_layout = "default";
|
||||
export default {
|
||||
computed: {
|
||||
layout() {
|
||||
let vm = this;
|
||||
return (vm.$route.meta.layout || default_layout) + '-layout';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Vendored
+112
@@ -0,0 +1,112 @@
|
||||
window._ = require('lodash');
|
||||
|
||||
// Axios
|
||||
window.axios = require('axios');
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
|
||||
// Token
|
||||
let token = document.head.querySelector('meta[name="csrf-token"]');
|
||||
if (token) {
|
||||
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
|
||||
} else {
|
||||
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
|
||||
}
|
||||
|
||||
// Vuetify
|
||||
import Vuetify, {
|
||||
VApp,
|
||||
VTextField,
|
||||
VBtn,
|
||||
VBreadcrumbs,
|
||||
VSwitch,
|
||||
VRadioGroup,
|
||||
VRadio
|
||||
} from 'vuetify/lib';
|
||||
// import 'material-design-icons-iconfont/dist/material-design-icons.css';
|
||||
import 'vuetify/src/stylus/app.styl';
|
||||
|
||||
Vue.use(Vuetify, {
|
||||
components: {
|
||||
VApp,
|
||||
VTextField,
|
||||
VBtn,
|
||||
VBreadcrumbs,
|
||||
VSwitch,
|
||||
VRadioGroup,
|
||||
VRadio
|
||||
},
|
||||
options: {
|
||||
customProperties: true
|
||||
},
|
||||
iconfont: 'md' || 'mdi',
|
||||
});
|
||||
|
||||
// Vue Meta
|
||||
import VueMeta from 'vue-meta'
|
||||
Vue.use(VueMeta)
|
||||
|
||||
// Boostrap
|
||||
import BootstrapVue from 'bootstrap-vue'
|
||||
Vue.use(BootstrapVue);
|
||||
|
||||
// Form Wiziard
|
||||
import VueFormWizard from 'vue-form-wizard'
|
||||
import 'vue-form-wizard/dist/vue-form-wizard.min.css'
|
||||
Vue.use(VueFormWizard);
|
||||
|
||||
// Tags Input
|
||||
import VueTagsInput from '@johmun/vue-tags-input';
|
||||
Vue.component('vue-tags-input', VueTagsInput);
|
||||
|
||||
// Toasted
|
||||
import Toasted from 'vue-toasted';
|
||||
let Options = {
|
||||
// iconPack : 'fontawesome'
|
||||
}
|
||||
Vue.use(Toasted, Options);
|
||||
|
||||
// Sprintf
|
||||
window.Sprintf = require('sprintf-js').sprintf;
|
||||
|
||||
// Youtube
|
||||
import VueYouTubeEmbed from 'vue-youtube-embed';
|
||||
Vue.use(VueYouTubeEmbed);
|
||||
|
||||
// VueElementLoading
|
||||
import VueElementLoading from 'vue-element-loading';
|
||||
Vue.component('vue-element-loading', VueElementLoading);
|
||||
|
||||
//Utilities
|
||||
import Form from './utilities/form';
|
||||
window.Form = Form;
|
||||
|
||||
import Constants from './utilities/constants';
|
||||
window.Constants = Constants;
|
||||
|
||||
window.General = require('./utilities/general');
|
||||
|
||||
window.NavigationAccess = require('./utilities/navigationAccess');
|
||||
|
||||
import En from './langs/en';
|
||||
window.En = En;
|
||||
|
||||
import ENV from './utilities/env';
|
||||
window.ENV = ENV;
|
||||
|
||||
/**
|
||||
* Echo exposes an expressive API for subscribing to channels and listening
|
||||
* for events that are broadcast by Laravel. Echo and event broadcasting
|
||||
* allows your team to easily build robust real-time web applications.
|
||||
*/
|
||||
|
||||
// import Echo from 'laravel-echo';
|
||||
|
||||
// window.Pusher = require('pusher-js');
|
||||
|
||||
// window.Echo = new Echo({
|
||||
// broadcaster: 'pusher',
|
||||
// key: process.env.MIX_PUSHER_APP_KEY,
|
||||
// cluster: process.env.MIX_PUSHER_APP_CLUSTER,
|
||||
// forceTLS: true
|
||||
// });
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div >
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name)}">
|
||||
<datepicker
|
||||
:name="input_name"
|
||||
input-class="form-control"
|
||||
:placeholder="placeholder"
|
||||
v-model="form[input_name]"
|
||||
@input="form.errors.clear(input_name)"
|
||||
format="dd-MM-yy"
|
||||
:disabled-dates="state.disabledDates"
|
||||
></datepicker>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import Datepicker from 'vuejs-datepicker';
|
||||
import moment from 'moment';
|
||||
|
||||
export default {
|
||||
props: ['input_name', 'form', 'placeholder', 'label', 'disable_pass_date'],
|
||||
components: {
|
||||
Datepicker
|
||||
},
|
||||
data() {
|
||||
let vm = this;
|
||||
return {
|
||||
state : {
|
||||
disabledDates: {
|
||||
customPredictor: function(date) {
|
||||
if (!vm.disable_pass_date) {
|
||||
if(date < moment().startOf('day') ){
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div >
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name_1)}">
|
||||
<datepicker
|
||||
:name="input_name_1"
|
||||
input-class="form-control"
|
||||
:placeholder="placeholder"
|
||||
v-model="form[input_name_1]"
|
||||
@input="form.errors.clear(input_name_1)"
|
||||
format="dd-MM-yy"
|
||||
:disabled-dates="state.disabledDates"
|
||||
></datepicker>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name_1)" v-text="form.errors.get(input_name_1)"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name_2)}">
|
||||
<datepicker
|
||||
:name="input_name_2"
|
||||
input-class="form-control"
|
||||
:placeholder="placeholder"
|
||||
v-model="form[input_name_2]"
|
||||
@input="form.errors.clear(input_name_2)"
|
||||
format="dd-MM-yy"
|
||||
:disabled-dates="state.disabledDates"
|
||||
></datepicker>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name_2)" v-text="form.errors.get(input_name_2)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import Datepicker from 'vuejs-datepicker';
|
||||
import moment from 'moment';
|
||||
|
||||
export default {
|
||||
props: ['input_name_1', 'input_name_2', 'form', 'placeholder', 'label', 'disable_pass_date'],
|
||||
components: {
|
||||
Datepicker
|
||||
},
|
||||
data() {
|
||||
let vm = this;
|
||||
return {
|
||||
state : {
|
||||
disabledDates: {
|
||||
customPredictor: function(date) {
|
||||
if (!vm.disable_pass_date) {
|
||||
if(date < moment().startOf('day') ){
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="form-group">
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
v-model="form[input_name_1]"
|
||||
:readonly="true"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="form-group">
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
v-model="form[input_name_2]"
|
||||
:readonly="true"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
export default {
|
||||
props: ['input_name_1', 'input_name_2', 'form', 'label', 'disable_pass_date'],
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div >
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name_1)}">
|
||||
<datetime
|
||||
type="datetime"
|
||||
class="form-control"
|
||||
v-model="form[input_name_1]"
|
||||
value-zone="Asia/Singapore"
|
||||
zone="Asia/Singapore"
|
||||
@input="form.errors.clear(input_name_1)"
|
||||
></datetime>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name_1)" v-text="form.errors.get(input_name_1)"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name_2)}">
|
||||
<datetime
|
||||
type="datetime"
|
||||
class="form-control"
|
||||
v-model="form[input_name_2]"
|
||||
value-zone="Asia/Singapore"
|
||||
zone="Asia/Singapore"
|
||||
@input="form.errors.clear(input_name_2)"
|
||||
></datetime>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name_2)" v-text="form.errors.get(input_name_2)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import { Datetime } from 'vue-datetime';
|
||||
import 'vue-datetime/dist/vue-datetime.css';
|
||||
import moment from 'moment';
|
||||
|
||||
export default {
|
||||
props: ['input_name_1', 'input_name_2', 'form', 'placeholder', 'label', 'disable_pass_date'],
|
||||
components: {
|
||||
Datetime
|
||||
},
|
||||
data() {
|
||||
let vm = this;
|
||||
return {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name)}">
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<div class="upload-box">
|
||||
<div class="file-container mb-1">
|
||||
<i class="fa fa-download"></i>
|
||||
<input type='file'
|
||||
:name="input_name"
|
||||
@change="onChange"
|
||||
/>
|
||||
<div>{{ file_name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: ['input_name', 'form', 'placeholder', 'label'],
|
||||
components: {
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
file_name: '',
|
||||
file: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(e) {
|
||||
let vm = this;
|
||||
let files = e.target.files;
|
||||
General.imageBase64(e.target.files).then(data => {
|
||||
vm.file = data[0];
|
||||
vm.file_name = e.target.files[0].name;
|
||||
vm.form[vm.input_name] = [vm.file];
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name)}">
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<div class="upload-box">
|
||||
<div class="file-container mb-1">
|
||||
<i class="fa fa-download"></i>
|
||||
<input type='file'
|
||||
:name="input_name"
|
||||
@change="onChange"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="preview" v-for="(img, $index) in preview" :key="$index">
|
||||
<a v-if="mime_type == 'application/pdf'" class="btn btn-xs btn-light" :href="img" target="_blank">
|
||||
<i class="fa fa-file-pdf"></i>
|
||||
</a>
|
||||
<div v-if="mime_type != 'application/pdf'">
|
||||
<img class="img-fluid" alt='' :src="img"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
<cropper-modal
|
||||
:img="img"
|
||||
@clicked="getCropImage"
|
||||
>
|
||||
</cropper-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CropperModal from "../modals/CropperModal";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
input_name: String,
|
||||
form: Object,
|
||||
placeholder: String,
|
||||
label: String,
|
||||
exist_img: Array,
|
||||
},
|
||||
components: {
|
||||
CropperModal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
preview: [],
|
||||
img: [],
|
||||
mime_type: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(e) {
|
||||
let vm = this;
|
||||
let files = e.target.files;
|
||||
vm.mime_type = files[0].type;
|
||||
|
||||
General.imageBase64(e.target.files).then(data => {
|
||||
vm.img = data[0];
|
||||
vm.cropperModalToggle();
|
||||
});
|
||||
},
|
||||
cropperModalToggle() {
|
||||
let vm = this;
|
||||
vm.$bvModal.show('cropper-modal');
|
||||
},
|
||||
getCropImage(img) {
|
||||
let vm = this;
|
||||
vm.form[vm.input_name] = [img];
|
||||
vm.preview = [img];
|
||||
},
|
||||
loadExistingImg() {
|
||||
let vm = this;
|
||||
if (vm.exist_img[0]) {
|
||||
vm.mime_type = vm.exist_img[0].mime_type;
|
||||
Array.from(vm.exist_img).forEach(file => {
|
||||
vm.preview.push(file.file_info.original.file);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'exist_img': 'loadExistingImg',
|
||||
},
|
||||
created() {
|
||||
let vm = this;
|
||||
vm.loadExistingImg();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<div class="upload-box">
|
||||
<div v-if="preview" v-for="(img, $index) in preview" :key="$index">
|
||||
<div>
|
||||
<img class="img-fluid" alt='' :src="img"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
label: String,
|
||||
exist_img: Array,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
preview: [],
|
||||
img: [],
|
||||
mime_type: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadExistingImg() {
|
||||
let vm = this;
|
||||
if (vm.exist_img[0]) {
|
||||
vm.mime_type = vm.exist_img[0].mime_type;
|
||||
Array.from(vm.exist_img).forEach(file => {
|
||||
vm.preview.push(file.file_info.original.file);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'exist_img': 'loadExistingImg',
|
||||
},
|
||||
created() {
|
||||
let vm = this;
|
||||
vm.loadExistingImg();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name)}">
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">{{ icon }}</span>
|
||||
</div>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
:placeholder="placeholder"
|
||||
v-model="form[input_name]"
|
||||
@keydown="form.errors.clear(input_name)"
|
||||
@keypress="isNumber($event)"
|
||||
@input="$emit('clicked')"
|
||||
:readonly="readonly"
|
||||
>
|
||||
</div>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: ['input_name', 'form', 'icon', 'placeholder', 'label', 'readonly'],
|
||||
methods: {
|
||||
isNumber($event) {
|
||||
let keyCode = ($event.keyCode ? $event.keyCode : $event.which);
|
||||
if ((keyCode < 48 || keyCode > 57) && keyCode !== 46) { // 46 is dot
|
||||
$event.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name)}">
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<input type="text"
|
||||
:name="input_name"
|
||||
class="form-control"
|
||||
:placeholder="placeholder"
|
||||
v-model="form[input_name]"
|
||||
@keydown="form.errors.clear(input_name)"
|
||||
@keypress="isNumber($event)"
|
||||
@keyup="$emit('clicked')"
|
||||
:readonly="readonly"
|
||||
>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: {
|
||||
input_name: String,
|
||||
form: Object,
|
||||
placeholder: String,
|
||||
label: String,
|
||||
readonly: Boolean,
|
||||
},
|
||||
methods: {
|
||||
isNumber($event) {
|
||||
let keyCode = ($event.keyCode ? $event.keyCode : $event.which);
|
||||
if ((keyCode < 48 || keyCode > 57) && keyCode !== 46) { // 46 is dot
|
||||
$event.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name)}">
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<div class="input-group">
|
||||
<input :type="password_type ? 'password' : 'text'"
|
||||
class="form-control"
|
||||
:placeholder="placeholder"
|
||||
v-model="form[input_name]"
|
||||
@keydown="form.errors.clear(input_name)"
|
||||
>
|
||||
<div class="input-group-prepend pointer-cursor" @click="passwordToggle">
|
||||
<span class="input-group-text">
|
||||
<i :class="password_type ? 'fa fa-eye-slash' : 'fa fa-eye'" aria-hidden="true"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: {
|
||||
input_name: String,
|
||||
form: Object,
|
||||
placeholder: String,
|
||||
label: String
|
||||
},
|
||||
data() {
|
||||
let vm = this;
|
||||
return {
|
||||
password_type: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
passwordToggle() {
|
||||
let vm = this;
|
||||
vm.password_type = !vm.password_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name)}">
|
||||
<div>
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
</div>
|
||||
<v-radio-group v-model="form[input_name]" :mandatory="false">
|
||||
<v-radio
|
||||
v-if="list" v-for="(item, $index) in list" :key="$index"
|
||||
color="primary"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
></v-radio>
|
||||
</v-radio-group>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: {
|
||||
input_name: String,
|
||||
form: Object,
|
||||
list: Array,
|
||||
label: String,
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name)}">
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<vue-tags-input
|
||||
ref="tags_input"
|
||||
v-model="tag"
|
||||
:tags="tags"
|
||||
:autocomplete-min-length="0"
|
||||
:add-on-blur="false"
|
||||
:autocomplete-items="autocompleteItems"
|
||||
:before-deleting-tag="deleteTag"
|
||||
:max-tags="limit_tag"
|
||||
:add-only-from-autocomplete="!can_add_new_tag ? true : false"
|
||||
:placeholder="''"
|
||||
@tags-changed="update"
|
||||
@before-adding-tag="checkTag"
|
||||
@focus="focus()"
|
||||
>
|
||||
</vue-tags-input>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: {
|
||||
input_name: String,
|
||||
form: Object,
|
||||
exist_data: Array,
|
||||
data_set: Array,
|
||||
placeholder: String,
|
||||
label: String,
|
||||
endpoint: String,
|
||||
params: Object,
|
||||
search_field: String,
|
||||
is_array: Boolean,
|
||||
limit_tag: Number,
|
||||
return_key: String,
|
||||
can_add_new_tag: Boolean
|
||||
},
|
||||
data() {
|
||||
let vm = this;
|
||||
return {
|
||||
tag: '',
|
||||
original_tag: [],
|
||||
tags: vm.exist_data ? vm.exist_data : [],
|
||||
autocompleteItems: [],
|
||||
pervious_item: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
focus() {
|
||||
let vm = this;
|
||||
if (vm.pervious_item.length > 0) {
|
||||
vm.pervious_item = [];
|
||||
vm.initItems();
|
||||
}
|
||||
},
|
||||
checkTag(obj) {
|
||||
let vm = this;
|
||||
if (vm.limit_tag >= vm.tags.length) {
|
||||
vm.tags = [];
|
||||
}
|
||||
obj.addTag();
|
||||
},
|
||||
update(newTags = '') {
|
||||
let vm = this;
|
||||
vm.tags = newTags;
|
||||
vm.pervious_item = vm.autocompleteItems;
|
||||
vm.autocompleteItems = [];
|
||||
vm.keyConvert();
|
||||
vm.$emit('clicked', vm.extractRow(vm.tags));
|
||||
vm.form.errors.clear(vm.input_name);
|
||||
},
|
||||
deleteTag() {
|
||||
vm.$emit('clicked', vm.extractRow(vm.tags));
|
||||
},
|
||||
keyConvert() {
|
||||
let vm = this;
|
||||
if (!vm.original) {
|
||||
vm.form[vm.input_name] = General.getKey(vm.tags, (!vm.return_key ? 'id' : vm.return_key));
|
||||
}
|
||||
else {
|
||||
vm.form[vm.input_name] = vm.original_tag;
|
||||
}
|
||||
},
|
||||
initItems() {
|
||||
let vm = this;
|
||||
|
||||
|
||||
if (vm.pervious_item.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!vm.data_set) {
|
||||
let search_field = {};
|
||||
search_field[vm.search_field] = vm.array ? [vm.tag] : vm.tag;
|
||||
|
||||
let form = new Form({...vm.params, ...search_field});
|
||||
General.getCall(vm.endpoint, form).then(data => {
|
||||
vm.autocompleteItems = data.data.map(item => {
|
||||
vm.original_tag.push(vm.tag);
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
hash_id: (item.hash_id ? item.hash_id : ''),
|
||||
row: item,
|
||||
text: (item.name ? item.name : item.hash_id )
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
vm.autocompleteItems = vm.data_set;
|
||||
}
|
||||
},
|
||||
extractRow() {
|
||||
let vm = this;
|
||||
let return_row = [];
|
||||
vm.tags.forEach(function(element, index) {
|
||||
return_row.push(element.row);
|
||||
});
|
||||
return return_row;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
let vm = this;
|
||||
setTimeout(function(){
|
||||
vm.tags = vm.exist_data ? vm.exist_data : [];
|
||||
vm.$emit('clicked', vm.extractRow(vm.tags));
|
||||
}, 1000);
|
||||
|
||||
vm.initItems();
|
||||
|
||||
},
|
||||
watch: {
|
||||
'tag': 'initItems',
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name)}">
|
||||
<label v-if="label">{{ label }}</label>
|
||||
<select
|
||||
class="form-control"
|
||||
v-model="form[input_name]"
|
||||
@change="$emit('clicked')"
|
||||
:disabled="readonly"
|
||||
>
|
||||
<option disabled value="">Select Option</option>
|
||||
<option :value="option.id" v-if="list" v-for="(option, $index) in list" :key="$index">
|
||||
{{ option.name }}
|
||||
</option>
|
||||
</select>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: ['input_name', 'form', 'label', 'list', 'readonly']
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="mb-2">
|
||||
<label v-if="label">{{ label }}</label>
|
||||
<v-switch
|
||||
v-model="form[input_name]"
|
||||
color="primary"
|
||||
:value="1"
|
||||
hide-details
|
||||
></v-switch>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import {ToggleButton} from 'vue-js-toggle-button'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
input_name: String,
|
||||
form: Object,
|
||||
label: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name)}">
|
||||
<div>
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div :class="full == true ? 'col-12' : 'col-3'" class="p-0" v-if="list.data" v-for="(item, $index) in list.data" :key="$index">
|
||||
<div class="float-left mr-3">{{ item.value }}</div>
|
||||
<div class="float-right">
|
||||
<v-switch
|
||||
:name="input_name"
|
||||
v-model="form[input_name]"
|
||||
color="primary"
|
||||
:value="item.key"
|
||||
hide-details
|
||||
@keydown="form.errors.clear(input_name)"
|
||||
></v-switch>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: ['input_name', 'form', 'list', 'label', 'full']
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="mb-2">
|
||||
<label v-if="label">{{ label }}</label>
|
||||
<v-switch
|
||||
v-model="form[input_name]"
|
||||
color="primary"
|
||||
:value="value"
|
||||
hide-details
|
||||
></v-switch>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import {ToggleButton} from 'vue-js-toggle-button'
|
||||
|
||||
export default {
|
||||
props: ['input_name', 'value', 'form', 'label', 'value']
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name)}">
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<textarea
|
||||
:name="input_name"
|
||||
class="form-control"
|
||||
:placeholder="placeholder"
|
||||
v-model="form[input_name]"
|
||||
@keydown="form.errors.clear(input_name)"
|
||||
:readonly="readonly"
|
||||
>
|
||||
</textarea>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: {
|
||||
input_name: String,
|
||||
form: Object,
|
||||
placeholder: String,
|
||||
label: String,
|
||||
readonly: Boolean
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<textarea type="text"
|
||||
rows="8" cols="50"
|
||||
class="form-control"
|
||||
:value="value"
|
||||
:readonly="true"
|
||||
>
|
||||
</textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: ['value', 'label']
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name)}">
|
||||
<label v-if="label">{{ label }}</label>
|
||||
<input type="text"
|
||||
:name="input_name"
|
||||
class="form-control"
|
||||
:placeholder="placeholder"
|
||||
v-model="form[input_name]"
|
||||
@keydown="form.errors.clear(input_name)"
|
||||
:readonly="readonly"
|
||||
>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: {
|
||||
input_name: String,
|
||||
form: Object,
|
||||
placeholder: String,
|
||||
label: String,
|
||||
readonly: Boolean
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div class="form-group" :class="{'has-error': form.errors.has(input_name)}">
|
||||
<div class="mb-2">
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
</div>
|
||||
|
||||
<b-row class="mb-2"
|
||||
v-for="(input, $index) in form[input_name]" :key="$index"
|
||||
>
|
||||
<b-col md="10">
|
||||
<input type="text"
|
||||
class="form-control mb-1"
|
||||
v-model="form[input_name][$index]"
|
||||
@keydown="form.errors.clear(input_name)"
|
||||
>
|
||||
</b-col>
|
||||
<b-col md="2" class="text-right">
|
||||
<button type="button" class="btn btn-xs btn-danger" @click="removeField($index)">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-xs btn-light"
|
||||
@click="addField"
|
||||
>
|
||||
<i class="fa fa-plus"></i> Add Option
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-danger text-left" v-if="form.errors.has(input_name)" v-text="form.errors.get(input_name)"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: ['input_name', 'form', 'placeholder', 'label', 'readonly'],
|
||||
methods: {
|
||||
addField() {
|
||||
let vm = this;
|
||||
vm.form[vm.input_name].push([]);
|
||||
},
|
||||
removeField(index) {
|
||||
let vm = this;
|
||||
vm.form[vm.input_name].splice(index, 1);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
let vm = this;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<b-row class="mb-2"
|
||||
v-for="(input, $index) in form[input_name]" :key="$index"
|
||||
>
|
||||
<b-col md="12">
|
||||
<input type="text"
|
||||
class="form-control mb-1"
|
||||
v-model="form[input_name][$index]"
|
||||
:readonly="true"
|
||||
>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: ['input_name', 'form', 'label'],
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<label v-if="label" for="name">{{ label }}</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
:value="value"
|
||||
:readonly="true"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: {
|
||||
label: String,
|
||||
value: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-modal :id="'cropper-modal'" size="lg" scrollable>
|
||||
<template v-slot:modal-title>
|
||||
<h4 class="modal-title">Crop Image</h4>
|
||||
</template>
|
||||
<vue-cropper
|
||||
ref='cropper'
|
||||
:guides="true"
|
||||
:view-mode="2"
|
||||
drag-mode="crop"
|
||||
:auto-crop-area="0.5"
|
||||
:min-container-width="250"
|
||||
:min-container-height="180"
|
||||
:background="true"
|
||||
:rotatable="true"
|
||||
:src="img"
|
||||
alt="Source Image"
|
||||
:img-style="{ 'width': '400px', 'height': '300px' }"
|
||||
>
|
||||
</vue-cropper>
|
||||
<template v-slot:modal-footer>
|
||||
<form>
|
||||
<v-btn
|
||||
:block="true"
|
||||
type="button"
|
||||
color="primary"
|
||||
class="white--text"
|
||||
@click="cropImage"
|
||||
>
|
||||
Crop
|
||||
</v-btn>
|
||||
</form>
|
||||
</template>
|
||||
</b-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VueCropper from 'vue-cropperjs';
|
||||
export default {
|
||||
props: ['img'],
|
||||
components: {
|
||||
VueCropper
|
||||
},
|
||||
methods: {
|
||||
cropImage() {
|
||||
let vm = this;
|
||||
let cropImg = '';
|
||||
cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL();
|
||||
vm.$bvModal.hide('cropper-modal');
|
||||
vm.$emit('clicked', cropImg);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-modal :id="'img-pop-modal'" size="lg" scrollable>
|
||||
<template v-slot:modal-title>
|
||||
<h4 class="modal-title">{{ label }}</h4>
|
||||
</template>
|
||||
<div v-if="img[0]" class="text-center">
|
||||
<img class="img-fluid"
|
||||
:src="img[0].file_info.original.file"
|
||||
width="300px" height="300px"
|
||||
>
|
||||
</div>
|
||||
<template v-slot:modal-footer>
|
||||
<form>
|
||||
<v-btn
|
||||
depressed
|
||||
small
|
||||
type="button"
|
||||
color="warning"
|
||||
class="m-0"
|
||||
@click="$bvModal.hide('img-pop-modal')"
|
||||
>
|
||||
Close
|
||||
</v-btn>
|
||||
</form>
|
||||
</template>
|
||||
</b-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VueCropper from 'vue-cropperjs';
|
||||
export default {
|
||||
props: ['label', 'img'],
|
||||
components: {
|
||||
VueCropper
|
||||
},
|
||||
methods: {
|
||||
cropImage() {
|
||||
let vm = this;
|
||||
let cropImg = '';
|
||||
cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL();
|
||||
vm.$bvModal.hide('cropper-modal');
|
||||
vm.$emit('clicked', cropImg);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<tr>
|
||||
<td class="pl-0 pr-0" :colspan="colspan">
|
||||
<ul class="pagination pull-right">
|
||||
|
||||
<li v-if="pervious_page" class="page-item">
|
||||
<span
|
||||
@click="$emit('clicked', pervious_page)"
|
||||
class="page-link"
|
||||
title="left" value="">
|
||||
«
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="next_page" class="page-item">
|
||||
<span
|
||||
@click="$emit('clicked', next_page)"
|
||||
class="page-link"
|
||||
title="right" value="">
|
||||
»
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: ['pervious_page', 'next_page', 'colspan']
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="app-page-title">
|
||||
<div class="page-title-wrapper">
|
||||
<div class="page-title-heading">
|
||||
<div class="page-title-icon">
|
||||
<i :class="icon"></i>
|
||||
</div>
|
||||
<div>
|
||||
{{heading}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['icon', 'heading'],
|
||||
}
|
||||
</script>
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
window.Vue = require('vue');
|
||||
require('./bootstrap');
|
||||
|
||||
import router from './router'
|
||||
|
||||
import App from './App'
|
||||
import DefaultLayout from './layouts/DefaultLayout.vue';
|
||||
import PageLayout from './layouts/PageLayout.vue';
|
||||
|
||||
Vue.component('default-layout', DefaultLayout);
|
||||
Vue.component('userpages-layout', PageLayout);
|
||||
|
||||
const app = new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
template: '<App/>',
|
||||
components: { App }
|
||||
});
|
||||
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
const En = {
|
||||
// Input
|
||||
'name': 'name',
|
||||
'username': 'username',
|
||||
'email': 'email',
|
||||
'password': 'password',
|
||||
'role_id': 'role',
|
||||
'permission_id': 'permission',
|
||||
'phone_code': 'phone code',
|
||||
'code': 'code',
|
||||
'currency_id': 'currency',
|
||||
'language_id': 'language',
|
||||
'symbol': 'symbol',
|
||||
'sell': 'sell',
|
||||
'buy': 'buy',
|
||||
'description': 'description',
|
||||
'min_file_size': 'min file size',
|
||||
'max_file_size': 'max file size',
|
||||
'credit_amount': 'credit amount',
|
||||
'media_property_type_id': 'media property type',
|
||||
'type': 'type',
|
||||
'mime_type_id': 'mime type',
|
||||
'trending_count': 'trending count',
|
||||
'bank_name': 'bank name',
|
||||
'holder_name': 'holder name',
|
||||
'holder_no': 'holder no',
|
||||
'country_id': 'country',
|
||||
|
||||
|
||||
|
||||
// Error
|
||||
'incorrect_account_or_password': 'Incorrect account or password',
|
||||
'this_field_required': 'This %(att)s field required',
|
||||
'incorrect_email_format': 'Incorrect email format',
|
||||
'the_content_has_already_been_taken': 'The %(att)s has already been taken',
|
||||
'the_content_confirmation_does_not_match': 'The %(att)s confirmation does not match',
|
||||
'incorrect_image_format': 'Incorrect image format',
|
||||
'incorrect_excel_format': 'Incorrect excel format',
|
||||
'the_content_must_be_a_number': 'The %(att)s must be a number',
|
||||
'the_content_must_be_greater_than_0': 'The %(att)s must be greater than 0',
|
||||
'the_content_not_exist': 'The %(att)s not exist',
|
||||
'the_content_must_be_at_least_10_characters': 'The %(att)s must be at least 10 characters',
|
||||
'the_content_must_be_less_than_100': 'The %(att)s must be less than 100',
|
||||
'status_error': 'status not allow update',
|
||||
|
||||
// toasted
|
||||
'have_been_successfully_created': '%(value[0].att)s ( %(value[1].att)s ) have been successfully created',
|
||||
'have_been_successfully_updated': '%(value[0].att)s ( %(value[1].att)s ) have been successfully updated',
|
||||
'have_been_successfully_imported': '%(value[0].att)s have been successfully imported',
|
||||
'error_mass_import': '%(value[0].att)s excel file have error when import',
|
||||
};
|
||||
|
||||
export default En;
|
||||
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<v-app id="benesApp">
|
||||
<div v-if="!loading" class="app-container app-theme-white">
|
||||
<transition name="fade" mode="out-in" appear>
|
||||
<app-header
|
||||
:user_detail="user_detail"
|
||||
>
|
||||
</app-header>
|
||||
</transition>
|
||||
<transition name="fade" mode="out-in" appear>
|
||||
<app-sidebar
|
||||
:permission_list="permission_list"
|
||||
>
|
||||
</app-sidebar>
|
||||
</transition>
|
||||
<div class="app-main__outer">
|
||||
<div class="app-main__inner">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<transition name="fade" mode="out-in" appear>
|
||||
<app-footer></app-footer>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="loading" class="main-loading-box">
|
||||
<vue-element-loading :active="true" spinner="line-scale" color="var(--primary)"></vue-element-loading>
|
||||
</div>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AppHeader from "./parts/Header";
|
||||
import AppSidebar from "./parts/Sidebar";
|
||||
import AppFooter from "./parts/Footer";
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: {
|
||||
AppHeader,
|
||||
AppSidebar,
|
||||
AppFooter
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: new Form({}),
|
||||
isConfigOpen: false,
|
||||
user_detail: {},
|
||||
permission_list: [],
|
||||
loading: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getUserDetail() {
|
||||
let vm = this;
|
||||
let url = 'user/self';
|
||||
|
||||
General.getCall(url, vm.form).then(response => {
|
||||
if (response.data) {
|
||||
vm.user_detail = response.data;
|
||||
vm.permission_list = response.data.permission_list;
|
||||
vm.loading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
let vm = this;
|
||||
vm.getUserDetail();
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<v-app id="benesApp">
|
||||
<div class="app-container app-theme-white">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div v-if="menu_item_list" class="mb-3">
|
||||
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item" :class="menu.active ? 'active' : ''" v-for="(menu, $index) in menu_item_list" :key="$index">
|
||||
<router-link :to="menu.path" tag="button">
|
||||
{{ menu.title }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['menu_item_list'],
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="app-footer">
|
||||
<div class="app-footer__inner">
|
||||
<div class="app-footer-left">
|
||||
Copyright © {{ site_title }} {{ year }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
let vm = this;
|
||||
return {
|
||||
site_title: Constants.site.title,
|
||||
year: General.getYear()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div :class="headerbg" class="app-header header-shadow bg-plum-plate text-lighter">
|
||||
<div class="app-header__content">
|
||||
<div class="app-header-right">
|
||||
<HeaderUserArea
|
||||
:user_detail="user_detail"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="app-header__mobile-menu">
|
||||
<div>
|
||||
<button type="button" class="hamburger close-sidebar-btn hamburger--elastic" :class="{ 'is-active' : isOpen }" @click="toggleMobile('closed-sidebar-open')">
|
||||
<span class="hamburger-box">
|
||||
<span class="hamburger-inner"></span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="app-header__menu">
|
||||
<span>
|
||||
<b-button class="btn-icon btn-icon-only" variant="primary" size="sm" :class="{ 'active' : isOpenMobileMenu }" @click="toggleMobile2('header-menu-open')">
|
||||
<div class="btn-icon-wrapper">
|
||||
<i class="fa fa-ellipsis-v"></i>
|
||||
</div>
|
||||
</b-button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HeaderUserArea from './HeaderUserArea';
|
||||
|
||||
export default {
|
||||
name: "Header",
|
||||
components: {
|
||||
HeaderUserArea,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isOpen: false,
|
||||
isOpenMobileMenu: false,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
headerbg: String,
|
||||
user_detail: Object
|
||||
},
|
||||
methods: {
|
||||
toggleMobile(className) {
|
||||
let vm = this;
|
||||
|
||||
const bu = document.getElementsByClassName('close-sidebar-btn')[0];
|
||||
const el = document.body;
|
||||
|
||||
if (bu.classList.contains('is-active')) {
|
||||
vm.isOpen = false;
|
||||
bu.classList.remove('is-active');
|
||||
}
|
||||
else {
|
||||
vm.isOpen = true;
|
||||
bu.classList.add('is-active');
|
||||
}
|
||||
|
||||
if (vm.isOpen) {
|
||||
el.classList.add(className);
|
||||
} else {
|
||||
el.classList.remove(className);
|
||||
}
|
||||
},
|
||||
|
||||
toggleMobile2(className) {
|
||||
let vm = this;
|
||||
const el = document.body;
|
||||
vm.isOpenMobileMenu = !vm.isOpenMobileMenu;
|
||||
|
||||
if (vm.isOpenMobileMenu) {
|
||||
el.classList.add(className);
|
||||
} else {
|
||||
el.classList.remove(className);
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div v-if="user_detail.username" class="d-flex">
|
||||
<div class="header-btn-lg pr-0">
|
||||
<div class="widget-content p-0">
|
||||
<div class="widget-content-wrapper">
|
||||
<div class="widget-content-left">
|
||||
<b-dropdown toggle-class="p-0 mr-2" menu-class="dropdown-menu-lg" variant="link" no-caret right>
|
||||
<span slot="button-content">
|
||||
<div class="icon-wrapper icon-wrapper-alt rounded-circle">
|
||||
<img width="50" height="50" class="rounded-circle" :src="user_detail.img[0].file_info.small.file" alt="">
|
||||
</div>
|
||||
</span>
|
||||
<div class="dropdown-menu-header">
|
||||
<div class="dropdown-menu-header-inner bg-info">
|
||||
<div class="menu-header-image opacity-2 dd-header-bg-6"></div>
|
||||
<div class="menu-header-content text-left">
|
||||
<div class="widget-content p-0">
|
||||
<div class="widget-content-wrapper">
|
||||
<div class="widget-content-left mr-3">
|
||||
<img width="42" class="rounded-circle" :src="user_detail.img[0].file_info.small.file" alt="">
|
||||
</div>
|
||||
<div class="widget-content-left">
|
||||
<div class="widget-heading">{{ user_detail.username}}</div>
|
||||
<div class="widget-subheading opacity-8">{{ user_detail.role_list[0].username }}</div>
|
||||
</div>
|
||||
<div class="widget-content-right mr-2">
|
||||
<button
|
||||
@click="logout"
|
||||
class="btn-shadow btn-shine btn btn-focus"
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-area-xs" style="height: 80px;">
|
||||
<vue-perfect-scrollbar class="scrollbar-container" v-once>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item-header nav-item">My Account</li>
|
||||
<li class="nav-item">
|
||||
<router-link
|
||||
:to="'/admin/profile/update'"
|
||||
class="nav-link"
|
||||
title="Profile"
|
||||
>
|
||||
My Profile
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</vue-perfect-scrollbar>
|
||||
</div>
|
||||
</b-dropdown>
|
||||
</div>
|
||||
<div class="widget-content-left ml-3 header-user-info">
|
||||
<div class="widget-heading">{{ user_detail.username }}</div>
|
||||
<div class="widget-subheading">{{ user_detail.role_list[0].name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VuePerfectScrollbar from 'vue-perfect-scrollbar'
|
||||
import VueCircle from 'vue2-circle-progress'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VuePerfectScrollbar,
|
||||
VueCircle,
|
||||
},
|
||||
props: {
|
||||
user_detail: Object
|
||||
},
|
||||
methods: {
|
||||
logout() {
|
||||
General.logout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div class="app-sidebar sidebar-shadow bg-plum-plate text-lighter" @mouseover="toggleSidebarHover('add','closed-sidebar-open')" @mouseleave="toggleSidebarHover('remove','closed-sidebar-open')">
|
||||
<div class="app-header__logo">
|
||||
<div class="text-white m-2">
|
||||
<router-link
|
||||
:to="'/'"
|
||||
class="btn-link"
|
||||
>
|
||||
<div class="logo-src" :style='{ background: "url(" + share_url + "logo-white.png" + ")" }'></div>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="header__pane ml-auto">
|
||||
<button type="button" class="hamburger close-sidebar-btn hamburger--elastic" v-bind:class="{ 'is-active' : is_open }" @click="toggleBodyClass('closed-sidebar')">
|
||||
<span class="hamburger-box">
|
||||
<span class="hamburger-inner"></span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="app-sidebar-content">
|
||||
<vue-perfect-scrollbar v-if="menu[0]" class="app-sidebar-scroll" v-once>
|
||||
<sidebar-menu
|
||||
showOneChild
|
||||
:menu="menu"
|
||||
@item-click="onItemClick"
|
||||
/>
|
||||
</vue-perfect-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import { SidebarMenu } from 'vue-sidebar-menu'
|
||||
import VuePerfectScrollbar from 'vue-perfect-scrollbar'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
permission_list: Array
|
||||
|
||||
},
|
||||
components: {
|
||||
SidebarMenu,
|
||||
VuePerfectScrollbar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
is_open: false,
|
||||
sidebar_active: false,
|
||||
menu: [],
|
||||
collapsed: true,
|
||||
window_width: 0,
|
||||
share_url: ENV.SHARE_URL,
|
||||
site_short_title: Constants.site.short_title,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onItemClick(event, item) {
|
||||
let vm = this;
|
||||
if (!item.child) {
|
||||
const el = document.body;
|
||||
el.classList.remove('closed-sidebar-open');
|
||||
|
||||
const bu = document.getElementsByClassName('close-sidebar-btn')[0];
|
||||
bu.classList.remove('is-active');
|
||||
}
|
||||
},
|
||||
toggleBodyClass(className) {
|
||||
let vm = this;
|
||||
const el = document.body;
|
||||
vm.is_open = !vm.is_open;
|
||||
|
||||
if (vm.is_open) {
|
||||
el.classList.add(className);
|
||||
} else {
|
||||
el.classList.remove(className);
|
||||
}
|
||||
},
|
||||
toggleSidebarHover(add, className) {
|
||||
let vm = this;
|
||||
const el = document.body;
|
||||
vm.sidebar_active = !vm.sidebar_active;
|
||||
vm.window_width = document.documentElement.clientWidth;
|
||||
if (vm.window_width > '992') {
|
||||
if (add === 'add') {
|
||||
el.classList.add(className);
|
||||
} else {
|
||||
el.classList.remove(className);
|
||||
}
|
||||
}
|
||||
},
|
||||
getWindowWidth() {
|
||||
let vm = this;
|
||||
const el = document.body;
|
||||
vm.window_width = document.documentElement.clientWidth;
|
||||
el.classList.add('closed-sidebar', 'closed-sidebar-md');
|
||||
},
|
||||
loadMenu() {
|
||||
let vm = this;
|
||||
vm.menu = NavigationAccess.menuDefine(vm.permission_list);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
let vm = this;
|
||||
vm.loadMenu();
|
||||
},
|
||||
mounted() {
|
||||
let vm = this;
|
||||
vm.$nextTick(function () {
|
||||
window.addEventListener('resize', vm.getWindowWidth);
|
||||
//Init
|
||||
vm.getWindowWidth()
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
let vm = this;
|
||||
window.removeEventListener('resize', vm.getWindowWidth);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-title :heading=heading :icon=icon></page-title>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageTitle from "../../components/titles/PageTitle.vue";
|
||||
|
||||
export default {
|
||||
metaInfo() {
|
||||
let vm = this;
|
||||
return {
|
||||
title: Constants.site.title,
|
||||
titleTemplate: '%s | ' + vm.$router.currentRoute.name
|
||||
}
|
||||
},
|
||||
components: {
|
||||
PageTitle,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
heading: 'Analytics Dashboard',
|
||||
icon: 'fa fa-home',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
},
|
||||
created() {
|
||||
let vm = this;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="vh-100 bg-plum-plate bg-animation">
|
||||
<div class="">
|
||||
<div class="app-header header-shadow bg-light text-lighter">
|
||||
<router-link
|
||||
:to="'/'"
|
||||
class="btn-link pl-3"
|
||||
>
|
||||
<div class="logo-src" :style='{ background: "url(" + share_url + "favicon.png" + ")" }'></div>
|
||||
</router-link>
|
||||
<div class="pl-3 app-header-title">
|
||||
<h5 class="card-title">Login</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<b-col md="4" class="top-margin-100">
|
||||
<div class="mx-auto p-0 m-0">
|
||||
<form @submit.prevent="onSubmit" @keydown="form.errors.clear($event.target.name)">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="h5 modal-title text-center">
|
||||
<div class="m-3 text-center">
|
||||
<img class="img-fluid big-logo" :src="share_url + 'logo.png'">
|
||||
</div>
|
||||
</div>
|
||||
<!-- ///////////////////////////////////////////////////// -->
|
||||
<text-input
|
||||
:input_name="'email'"
|
||||
:form="form"
|
||||
:label="'Email'"
|
||||
>
|
||||
</text-input>
|
||||
<!-- ///////////////////////////////////////////////////// -->
|
||||
<password-input
|
||||
:input_name="'password'"
|
||||
:form="form"
|
||||
:label="'Password'"
|
||||
>
|
||||
</password-input>
|
||||
<!-- ///////////////////////////////////////////////////// -->
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div>
|
||||
<v-btn
|
||||
:loading="loading"
|
||||
:disabled="loading"
|
||||
:block="true"
|
||||
type="submit"
|
||||
color="primary"
|
||||
class="white--text"
|
||||
@click="loader = 'loading'"
|
||||
>
|
||||
Login
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</b-col>
|
||||
</div>
|
||||
<div class="footer text-center text-white opacity-8 p-3">
|
||||
Copyright © {{ site_title }} {{ year }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TextInput from '../../components/inputs/TextInput';
|
||||
import PasswordInput from '../../components/inputs/PasswordInput';
|
||||
|
||||
export default {
|
||||
metaInfo() {
|
||||
let vm = this;
|
||||
return {
|
||||
title: Constants.site.title,
|
||||
titleTemplate: '%s | ' + vm.$router.currentRoute.name
|
||||
}
|
||||
},
|
||||
components: {
|
||||
TextInput,
|
||||
PasswordInput
|
||||
},
|
||||
data() {
|
||||
let vm = this;
|
||||
return {
|
||||
form: new Form({
|
||||
email: '',
|
||||
password: ''
|
||||
}),
|
||||
site_title: Constants.site.title,
|
||||
site_short_title: Constants.site.short_title,
|
||||
share_url: ENV.SHARE_URL,
|
||||
|
||||
year: General.getYear(),
|
||||
loader: null,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
let vm = this;
|
||||
const l = vm.loader
|
||||
vm[l] = !vm[l]
|
||||
vm.form.post(
|
||||
ENV.API_URL + 'login',
|
||||
{
|
||||
'App-Key': ENV.APP_KEY
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
localStorage.setItem('user_detail',JSON.stringify(response.data))
|
||||
localStorage.setItem('user_jwt', response.access_token);
|
||||
|
||||
if (localStorage.getItem('user_jwt') != null) {
|
||||
window.location.replace(ENV.APP_URL);
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
vm[l] = false
|
||||
vm.loader = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
let vm = this;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="vh-100 bg-plum-plate bg-animation">
|
||||
<div class="">
|
||||
<div class="app-header header-shadow bg-light text-lighter">
|
||||
<router-link
|
||||
:to="'/'"
|
||||
class="btn-link pl-3"
|
||||
>
|
||||
<div class="logo-src" :style='{ background: "url(" + share_url + "favicon.png" + ")" }'></div>
|
||||
</router-link>
|
||||
<div class="pl-3 app-header-title">
|
||||
<h5 class="card-title">Page 404</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<b-col class="mobile-none top-margin-100" md="5">
|
||||
<div class="mb-3 text-center">
|
||||
<img class="img-fluid big-logo" :src="share_url + 'logo.png'">
|
||||
</div>
|
||||
<div class="text-white text-center">
|
||||
<h1 class="mb-2">Page 404</h1>
|
||||
</div>
|
||||
</b-col>
|
||||
</div>
|
||||
<div class="footer text-center text-white opacity-8 p-3">
|
||||
Copyright © {{ site_title }} {{ year }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
metaInfo() {
|
||||
let vm = this;
|
||||
return {
|
||||
title: Constants.site.title,
|
||||
titleTemplate: '%s | ' + vm.$router.currentRoute.name
|
||||
}
|
||||
},
|
||||
data() {
|
||||
let vm = this;
|
||||
return {
|
||||
share_url: ENV.SHARE_URL,
|
||||
site_title: Constants.site.title,
|
||||
year: General.getYear()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-title
|
||||
:heading="heading"
|
||||
:icon="icon"
|
||||
:add="true"
|
||||
></page-title>
|
||||
<breadcrumb
|
||||
:menu_item_list="menu_item_list"
|
||||
></breadcrumb>
|
||||
<user-form
|
||||
:mode="'create'"
|
||||
></user-form>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import PageTitle from "../../components/titles/PageTitle";
|
||||
import Breadcrumb from "../../layouts/parts/Breadcrumb";
|
||||
import UserForm from "./Form";
|
||||
|
||||
export default {
|
||||
metaInfo() {
|
||||
let vm = this;
|
||||
return {
|
||||
title: Constants.site.title,
|
||||
titleTemplate: '%s | ' + vm.$router.currentRoute.name
|
||||
}
|
||||
},
|
||||
components: {
|
||||
PageTitle,
|
||||
Breadcrumb,
|
||||
UserForm
|
||||
},
|
||||
data() {
|
||||
let vm = this;
|
||||
return {
|
||||
heading: 'User Detail',
|
||||
icon: 'fa fa-plus',
|
||||
menu_item_list: [
|
||||
{
|
||||
title: 'Dashboard',
|
||||
active: false,
|
||||
path: '/'
|
||||
},
|
||||
{
|
||||
title: 'User List',
|
||||
active: false,
|
||||
path: '/user/list'
|
||||
},
|
||||
{
|
||||
title: 'User Detail',
|
||||
active: true,
|
||||
path: '/user/detail/' + vm.$route.params.hash_id
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<b-col md="12">
|
||||
<form @submit.prevent="$emit('clicked')" @keydown="form.errors.clear($event.target.name)">
|
||||
<text-input
|
||||
:input_name="'name'"
|
||||
:form="form"
|
||||
:label="'Name'"
|
||||
>
|
||||
</text-input>
|
||||
<text-input
|
||||
:input_name="'email'"
|
||||
:form="form"
|
||||
:label="'Email'"
|
||||
>
|
||||
</text-input>
|
||||
<select-option-auto-complete-input
|
||||
:input_name="'status'"
|
||||
:form="form"
|
||||
:exist_data="exist_user_status"
|
||||
:placeholder="''"
|
||||
:label="'Status'"
|
||||
:data_set="user_status"
|
||||
:array="true"
|
||||
></select-option-auto-complete-input>
|
||||
<v-btn
|
||||
depressed
|
||||
small
|
||||
type="submit"
|
||||
color="primary"
|
||||
class="m-0"
|
||||
>
|
||||
Submit
|
||||
</v-btn>
|
||||
<v-btn
|
||||
depressed
|
||||
small
|
||||
type="button"
|
||||
color="warning"
|
||||
class="m-0"
|
||||
@click="$emit('reset')"
|
||||
>
|
||||
Reset
|
||||
</v-btn>
|
||||
</form>
|
||||
</b-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TextInput from '../../components/inputs/TextInput';
|
||||
import SelectOptionAutoCompleteInput from '../../components/inputs/SelectOptionAutoCompleteInput';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
form: Object
|
||||
},
|
||||
components: {
|
||||
TextInput,
|
||||
SelectOptionAutoCompleteInput
|
||||
},
|
||||
data() {
|
||||
let vm = this;
|
||||
return {
|
||||
exist_user_status: [],
|
||||
user_status: General.autoCompleteKeyConvert(Constants.user_status)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initForm() {
|
||||
let vm = this;
|
||||
if (vm.form.status[0]) {
|
||||
let status_list = [];
|
||||
vm.exist_user_status = General.initFormArray(vm.user_status, vm.form.status);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
let vm = this;
|
||||
vm.initForm();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<b-card class="main-card mb-3">
|
||||
<div v-if="!this_user.data" class="text-center">
|
||||
<vue-element-loading :active="true" spinner="line-scale" color="var(--primary)"></vue-element-loading>
|
||||
</div>
|
||||
<div v-if="this_user.data">
|
||||
<form-wizard
|
||||
@on-complete="onComplete"
|
||||
title=""
|
||||
subtitle=""
|
||||
finish-button-text="Back"
|
||||
@keydown="form.errors.clear($event.target.name)"
|
||||
color="var(--primary)"
|
||||
>
|
||||
<tab-content
|
||||
title="Details"
|
||||
icon="fa fa-info-circle"
|
||||
>
|
||||
<image-read-only-input
|
||||
:label="'Profile'"
|
||||
:exist_img="this_user.data.img"
|
||||
>
|
||||
</image-read-only-input>
|
||||
<!-- ///////////////////////////////////////////////////// -->
|
||||
<text-read-only-input
|
||||
:label="'Username'"
|
||||
:value="this_user.data.username"
|
||||
>
|
||||
</text-read-only-input>
|
||||
<!-- ///////////////////////////////////////////////////// -->
|
||||
<text-read-only-input
|
||||
:label="'Email'"
|
||||
:value="this_user.data.email"
|
||||
>
|
||||
</text-read-only-input>
|
||||
<!-- ///////////////////////////////////////////////////// -->
|
||||
<text-read-only-input
|
||||
:label="'Country'"
|
||||
:value="this_user.data.country_list[0].name"
|
||||
>
|
||||
</text-read-only-input>
|
||||
<!-- ///////////////////////////////////////////////////// -->
|
||||
<text-read-only-input
|
||||
:label="'Status'"
|
||||
:value="this_user.data.status[0].name"
|
||||
>
|
||||
</text-read-only-input>
|
||||
<!-- ///////////////////////////////////////////////////// -->
|
||||
</tab-content>
|
||||
</form-wizard>
|
||||
</div>
|
||||
</b-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TextReadOnlyInput from '../../components/inputs/TextReadOnlyInput';
|
||||
import ImageReadOnlyInput from '../../components/inputs/ImageReadOnlyInput';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
mode: String
|
||||
},
|
||||
components: {
|
||||
TextReadOnlyInput,
|
||||
ImageReadOnlyInput
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
this_user: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onComplete() {
|
||||
let vm = this;
|
||||
vm.$router.push('/user/list');
|
||||
return true;
|
||||
},
|
||||
loadThisUser() {
|
||||
let vm = this;
|
||||
let url = 'user/show/' + vm.$route.params.hash_id;
|
||||
let form = new Form({});
|
||||
General.getCall(url, form).then(response => {
|
||||
vm.this_user = response;
|
||||
vm.convertStatus();
|
||||
});
|
||||
},
|
||||
convertStatus() {
|
||||
let vm = this;
|
||||
vm.this_user.data.status = General.initFormArray(Constants.admin_status, [vm.this_user.data.status]);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
let vm = this;
|
||||
vm.loadThisUser();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-title
|
||||
:heading="heading"
|
||||
:icon="icon"
|
||||
:add="true"
|
||||
></page-title>
|
||||
<breadcrumb
|
||||
:menu_item_list=menu_item_list
|
||||
></breadcrumb>
|
||||
<user-table></user-table>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import PageTitle from "../../components/titles/PageTitle";
|
||||
import Breadcrumb from "../../layouts/parts/Breadcrumb";
|
||||
import UserTable from "./Table";
|
||||
|
||||
export default {
|
||||
metaInfo() {
|
||||
let vm = this;
|
||||
return {
|
||||
title: Constants.site.title,
|
||||
titleTemplate: '%s | ' + vm.$router.currentRoute.name
|
||||
}
|
||||
},
|
||||
components: {
|
||||
PageTitle,
|
||||
Breadcrumb,
|
||||
UserTable
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
heading: 'User List',
|
||||
icon: 'fa fa-list',
|
||||
menu_item_list: [
|
||||
{
|
||||
title: 'Dashboard',
|
||||
active: false,
|
||||
path: '/'
|
||||
},
|
||||
{
|
||||
title: 'User List',
|
||||
active: true,
|
||||
path: 'list'
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,199 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-card class="main-card mb-4">
|
||||
<b-row class="mb-2" no-gutters>
|
||||
<b-col md="6">
|
||||
<button type="button" class="btn btn-primary" @click="filterToggle">
|
||||
<i class="fa fa-filter"></i>
|
||||
</button>
|
||||
<div v-if="filter_status" class="filter dropdown-menu show">
|
||||
<user-filter
|
||||
:form="form"
|
||||
@clicked="filterSubmit"
|
||||
@reset="FilterReset"
|
||||
></user-filter>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<paginate-nav
|
||||
v-if="user_list.page"
|
||||
:colspan="colspan"
|
||||
:pervious_page="user_list.page.pervious_page"
|
||||
:next_page="user_list.page.next_page"
|
||||
@clicked="dataHandler"
|
||||
></paginate-nav>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Profile</th>
|
||||
<th style="width: 25%">Details</th>
|
||||
<th>Status</th>
|
||||
<th>Created</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="user_list.data" v-for="(user, $index) in user_list.data" :key="$index">
|
||||
<td>{{ $index + 1 }}</td>
|
||||
<td>
|
||||
<img class="img-fluid pointer-cursor"
|
||||
:src="user.img[0].file_info.original.file" width="60px" height="60px"
|
||||
@click="ImgPopModalToggle(user.img)"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<div><b>Name</b>: {{ user.username }}</div>
|
||||
<div><b>Email</b>: {{ user.email }}</div>
|
||||
<div><b>Country</b>: {{ user.country_list[0].name }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<span :class="'badge badge-' + user.status[0].color">{{ user.status[0].name }}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ user.created_at }}
|
||||
</td>
|
||||
<td>
|
||||
<router-link
|
||||
v-if="permission_list.includes('view user')"
|
||||
:to="'detail/' + user.hash_id"
|
||||
class="btn btn-xs btn-light"
|
||||
title="View"
|
||||
>
|
||||
<i class="fa fa-eye"></i>
|
||||
</router-link>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="!user_list.data || !user_list.data[0]">
|
||||
<td class="text-center" :colspan="colspan">
|
||||
<vue-element-loading v-if="loading" :active="true" spinner="line-scale" color="var(--primary)"></vue-element-loading>
|
||||
<span v-if="!loading">Empty</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot v-if="user_list.page">
|
||||
<paginate-nav
|
||||
:colspan="colspan"
|
||||
:pervious_page="user_list.page.pervious_page"
|
||||
:next_page="user_list.page.next_page"
|
||||
@clicked="dataHandler"
|
||||
>
|
||||
</paginate-nav>
|
||||
<tr>
|
||||
<td class="text-right" :colspan="colspan">
|
||||
<small>Total: {{ user_list.total }}</small>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</b-card>
|
||||
<img-pop-modal
|
||||
:label="'Profile'"
|
||||
:img="selected_img"
|
||||
>
|
||||
</img-pop-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import userFilter from "./Filter";
|
||||
import ImgPopModal from "../../components/modals/ImgPopModal";
|
||||
import PaginateNav from '../../components/paginations/NextPerviousPagination';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
userFilter,
|
||||
ImgPopModal,
|
||||
PaginateNav
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form_permission: new Form({}),
|
||||
form: new Form({
|
||||
username: General.getURLParam('username'),
|
||||
email: General.getURLParam('email'),
|
||||
status: General.returnAray(General.getURLParam('status[]')),
|
||||
page: General.getURLParam('page')
|
||||
}),
|
||||
colspan: 6,
|
||||
filter_status: false,
|
||||
loading: true,
|
||||
user_list: [],
|
||||
selected_img: [],
|
||||
permission_list: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
ImgPopModalToggle(img) {
|
||||
let vm = this;
|
||||
vm.selected_img = img;
|
||||
vm.$bvModal.show('img-pop-modal');
|
||||
},
|
||||
getPermissionList() {
|
||||
let vm = this;
|
||||
let url = 'admin/self';
|
||||
let params = {}
|
||||
General.getCall(url, vm.form_permission).then(response => {
|
||||
vm.permission_list = response.data.permission_list;
|
||||
});
|
||||
},
|
||||
getUserList() {
|
||||
let vm = this;
|
||||
let url = 'user/list';
|
||||
vm.tableCall(url);
|
||||
},
|
||||
tableCall(url) {
|
||||
let vm = this;
|
||||
vm.loading = true;
|
||||
General.tableCall(url, vm.form).then(response => {
|
||||
vm.loading = false;
|
||||
vm.user_list = response;
|
||||
vm.form.page = General.getURLParam('page');
|
||||
vm.convertStatus();
|
||||
});
|
||||
},
|
||||
convertStatus() {
|
||||
let vm = this;
|
||||
vm.user_list.data.forEach(function(element, index) {
|
||||
element.status = General.initFormArray(Constants.user_status, [element.status]);
|
||||
});
|
||||
},
|
||||
dataHandler(url) {
|
||||
let vm = this;
|
||||
vm.form.page = General.getStringParam('page', url);
|
||||
vm.tableCall('user/list');
|
||||
},
|
||||
filterToggle() {
|
||||
let vm = this;
|
||||
vm.filter_status = !vm.filter_status;
|
||||
},
|
||||
filterCloseClick(e) {
|
||||
let vm = this;
|
||||
if (!vm.$el.contains(e.target) && !e.target.classList.contains('filter')) {
|
||||
vm.filter_status = false;
|
||||
}
|
||||
},
|
||||
filterSubmit() {
|
||||
let vm = this;
|
||||
let url = 'user/list';
|
||||
vm.tableCall(url);
|
||||
vm.form.page = '';
|
||||
vm.filterToggle();
|
||||
},
|
||||
FilterReset() {
|
||||
let vm = this;
|
||||
vm.form.reset();
|
||||
vm.filterSubmit();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let vm = this;
|
||||
document.addEventListener('click', vm.filterCloseClick);
|
||||
},
|
||||
created() {
|
||||
let vm = this;
|
||||
vm.getPermissionList();
|
||||
vm.getUserList();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Vendored
+168
@@ -0,0 +1,168 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
Vue.use(Router);
|
||||
|
||||
import Logins from './pages/logins/Index';
|
||||
// import Dashboards from './pages/dashboards/Index';
|
||||
|
||||
// import UserList from './pages/users/List';
|
||||
// import UserDetail from './pages/users/Detail';
|
||||
|
||||
// import AdminList from './pages/admins/List';
|
||||
// import AdminCreate from './pages/admins/Create';
|
||||
// import AdminEdit from './pages/admins/Edit';
|
||||
// import AdminProfile from './pages/admins/Profile';
|
||||
|
||||
// import AdminRoleList from './pages/admin-roles/List';
|
||||
// import AdminRoleCreate from './pages/admin-roles/Create';
|
||||
// import AdminRoleEdit from './pages/admin-roles/Edit';
|
||||
|
||||
// import PageNotFound from './pages/page-not-founds/Index';
|
||||
|
||||
let router = new Router({
|
||||
mode: 'history',
|
||||
base: process.env.MIX_SUB_URL,
|
||||
scrollBehavior() {
|
||||
return window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
},
|
||||
routes: [
|
||||
/////////////////////////////////////////////////////
|
||||
// Login
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
meta: {
|
||||
layout: 'userpages',
|
||||
guest: true
|
||||
},
|
||||
component: Logins
|
||||
},
|
||||
/////////////////////////////////////////////////////
|
||||
// // Dashboard
|
||||
// {
|
||||
// path: '/',
|
||||
// name: 'Dashboards',
|
||||
// meta: {
|
||||
// auth: true
|
||||
// },
|
||||
// component: Dashboards
|
||||
// },
|
||||
// /////////////////////////////////////////////////////
|
||||
// // User
|
||||
// {
|
||||
// path: '/user/list',
|
||||
// name: 'User List',
|
||||
// meta: {
|
||||
// auth: true
|
||||
// },
|
||||
// component: UserList
|
||||
// },
|
||||
// {
|
||||
// path: '/user/detail/:hash_id',
|
||||
// name: 'User Detail',
|
||||
// meta: {
|
||||
// auth: true
|
||||
// },
|
||||
// component: UserDetail
|
||||
// },
|
||||
// /////////////////////////////////////////////////////
|
||||
// // Admin
|
||||
// {
|
||||
// path: '/admin/list',
|
||||
// name: 'Admin List',
|
||||
// meta: {
|
||||
// auth: true
|
||||
// },
|
||||
// component: AdminList
|
||||
// },
|
||||
// {
|
||||
// path: '/admin/create',
|
||||
// name: 'Admin Create',
|
||||
// meta: {
|
||||
// auth: true
|
||||
// },
|
||||
// component: AdminCreate
|
||||
// },
|
||||
// {
|
||||
// path: '/admin/edit/:hash_id',
|
||||
// name: 'Admin Edit',
|
||||
// meta: {
|
||||
// auth: true
|
||||
// },
|
||||
// component: AdminEdit
|
||||
// },
|
||||
// {
|
||||
// path: '/admin/profile/update',
|
||||
// name: 'Admin Profile',
|
||||
// meta: {
|
||||
// auth: true
|
||||
// },
|
||||
// component: AdminProfile
|
||||
// },
|
||||
// /////////////////////////////////////////////////////
|
||||
// // Admin Role
|
||||
// {
|
||||
// path: '/admin-role/list',
|
||||
// name: 'Admin Role List',
|
||||
// meta: {
|
||||
// auth: true
|
||||
// },
|
||||
// component: AdminRoleList
|
||||
// },
|
||||
// {
|
||||
// path: '/admin-role/create',
|
||||
// name: 'Admin Role Create',
|
||||
// meta: {
|
||||
// auth: true
|
||||
// },
|
||||
// component: AdminRoleCreate
|
||||
// },
|
||||
// {
|
||||
// path: '/admin-role/edit/:hash_id',
|
||||
// name: 'Admin Role Edit',
|
||||
// meta: {
|
||||
// auth: true
|
||||
// },
|
||||
// component: AdminRoleEdit
|
||||
// },
|
||||
// /////////////////////////////////////////////////////
|
||||
// // 404
|
||||
// {
|
||||
// path: '*',
|
||||
// name: 'Page 404',
|
||||
// meta: {
|
||||
// layout: 'userpages',
|
||||
// guest: true
|
||||
// },
|
||||
// component: PageNotFound
|
||||
// },
|
||||
// /////////////////////////////////////////////////////
|
||||
]
|
||||
});
|
||||
|
||||
// window.USER_DETAIL = localStorage.getItem('user_detail') != null ? JSON.parse(localStorage.getItem('user_detail')) : null;
|
||||
window.TOKEN = localStorage.getItem('user_jwt');
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if(to.matched.some(record => record.meta.auth)) {
|
||||
if (localStorage.getItem('user_jwt') == null) {
|
||||
next({ path: 'login' })
|
||||
}
|
||||
else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
if(to.matched.some(record => record.meta.without_auth)) {
|
||||
if (localStorage.getItem('user_jwt') != null) {
|
||||
next({ path: '/' })
|
||||
}
|
||||
else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
|
||||
export default router;
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
const Constant = {
|
||||
'site': {
|
||||
'title': 'Unity',
|
||||
'short_title': 'Unity',
|
||||
'description': 'Unity'
|
||||
},
|
||||
'user_status': [
|
||||
{
|
||||
'id': 1,
|
||||
'name': 'Active',
|
||||
'color': 'primary'
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'name': 'De-active',
|
||||
'color': 'danger'
|
||||
},
|
||||
],
|
||||
'admin_status': [
|
||||
{
|
||||
'id': 1,
|
||||
'name': 'Active',
|
||||
'color': 'primary'
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'name': 'De-active',
|
||||
'color': 'danger'
|
||||
},
|
||||
],
|
||||
'role_status': [
|
||||
{
|
||||
'id': 1,
|
||||
'name': 'Active',
|
||||
'color': 'primary'
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'name': 'De-active',
|
||||
'color': 'danger'
|
||||
},
|
||||
],
|
||||
};
|
||||
export default Constant;
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
const ENV = {
|
||||
'APP_URL': process.env.MIX_APP_URL,
|
||||
'API_URL': process.env.MIX_API_URL + 'user-backsys/api/',
|
||||
'SHARE_URL': process.env.MIX_API_URL + 'share/',
|
||||
'APP_KEY': process.env.MIX_APP_KEY
|
||||
};
|
||||
export default ENV;
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
class Errors {
|
||||
|
||||
constructor() {
|
||||
this.errors = {};
|
||||
}
|
||||
|
||||
has(field) {
|
||||
return this.errors.hasOwnProperty(field);
|
||||
}
|
||||
|
||||
any() {
|
||||
return Object.keys(this.errors).length > 0;
|
||||
}
|
||||
|
||||
get(field) {
|
||||
if (this.errors[field]) {
|
||||
return this.errors[field][0];
|
||||
}
|
||||
}
|
||||
|
||||
record(errors) {
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
clear(field) {
|
||||
if (field) {
|
||||
delete this.errors[field];
|
||||
return;
|
||||
}
|
||||
|
||||
this.errors = {};
|
||||
}
|
||||
}
|
||||
|
||||
export default Errors;
|
||||
Vendored
+164
@@ -0,0 +1,164 @@
|
||||
import Errors from './errors';
|
||||
|
||||
class Form {
|
||||
|
||||
constructor(data) {
|
||||
this.originalData = data;
|
||||
|
||||
for (let field in data) {
|
||||
this[field] = data[field];
|
||||
}
|
||||
|
||||
this.errors = new Errors();
|
||||
}
|
||||
|
||||
data() {
|
||||
let data = {};
|
||||
|
||||
for (let property in this.originalData) {
|
||||
data[property] = this[property];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
reset() {
|
||||
for (let field in this.originalData) {
|
||||
this[field] = '';
|
||||
}
|
||||
|
||||
this.errors.clear();
|
||||
}
|
||||
|
||||
get(url, header) {
|
||||
return this.submit('get', url, header);
|
||||
}
|
||||
|
||||
post(url, header) {
|
||||
return this.submit('post', url, header);
|
||||
}
|
||||
|
||||
put(url, header) {
|
||||
return this.submit('put', url, header);
|
||||
}
|
||||
|
||||
patch(url, header) {
|
||||
return this.submit('patch', url, header);
|
||||
}
|
||||
|
||||
delete(url, header) {
|
||||
return this.submit('delete', url, header);
|
||||
}
|
||||
|
||||
submit(method, url, header) {
|
||||
let vm = this;
|
||||
let params = vm.data();
|
||||
|
||||
let set = {};
|
||||
|
||||
if (method == 'get') {
|
||||
set = {
|
||||
method: method,
|
||||
url: url,
|
||||
params: params,
|
||||
headers: header
|
||||
}
|
||||
}
|
||||
else {
|
||||
set = {
|
||||
method: method,
|
||||
url: url,
|
||||
data: params,
|
||||
headers: header
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
axios(set)
|
||||
.then(response => {
|
||||
vm.onSuccess(response.data);
|
||||
resolve(response.data);
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.response.data.errors) {
|
||||
let error_return = error.response.data.errors;
|
||||
Object.keys(error_return).forEach(function (key) {
|
||||
error.response.data.errors[key] = vm.errorCodeConvert(error_return[key], key);
|
||||
});
|
||||
|
||||
vm.onFail(error.response.data.errors)
|
||||
return reject(error.response.data.errors)
|
||||
}
|
||||
return reject(error)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onSuccess(data) {
|
||||
// this.reset();
|
||||
}
|
||||
|
||||
onFail(errors) {
|
||||
this.errors.record(errors);
|
||||
}
|
||||
|
||||
errorCodeConvert(code = [], key) {
|
||||
let value = {
|
||||
att: General.lang(key)
|
||||
}
|
||||
let return_message = [];
|
||||
for (var i = 0; i < code.length; i++) {
|
||||
if(code[i] == 'LOGIN_ERROR') {
|
||||
return_message.push(General.lang('incorrect_account_or_password'));
|
||||
}
|
||||
else if (code[i] == 'REQUIRED') {
|
||||
return_message.push(Sprintf(General.lang('this_field_required'), value));
|
||||
}
|
||||
else if(code[i] == 'EMAIL_ERROR') {
|
||||
return_message.push(General.lang('incorrect_email_format'));
|
||||
}
|
||||
else if(code[i] == 'UNIQUE_ERRO') {
|
||||
return_message.push(Sprintf(General.lang('the_content_has_already_been_taken'), value));
|
||||
}
|
||||
else if(code[i] == 'CONFIRM_ERROR') {
|
||||
return_message.push(Sprintf(General.lang('the_content_confirmation_does_not_match'), value));
|
||||
}
|
||||
else if(code[i] == 'IMG_ERROR') {
|
||||
return_message.push(Sprintf(General.lang('incorrect_image_format'), value));
|
||||
}
|
||||
else if(code[i] == 'EXCEL_ERROR') {
|
||||
return_message.push(Sprintf(General.lang('incorrect_excel_format'), value));
|
||||
}
|
||||
else if(code[i] == 'NUMERIC_ERROR') {
|
||||
return_message.push(Sprintf(General.lang('the_content_must_be_a_number'), value));
|
||||
}
|
||||
else if(code[i] == 'GREATER_ERROR_0') {
|
||||
return_message.push(Sprintf(General.lang('the_content_must_be_greater_than_0'), value));
|
||||
}
|
||||
else if(code[i] == 'GREATER_ERROR_-1') {
|
||||
return_message.push(Sprintf(General.lang('the_content_must_be_greater_than_0'), value));
|
||||
}
|
||||
else if(code[i] == 'LESS_ERROR_100') {
|
||||
return_message.push(Sprintf(General.lang('the_content_must_be_less_than_100'), value));
|
||||
}
|
||||
else if(code[i] == 'MIN_ERROR_10') {
|
||||
return_message.push(Sprintf(General.lang('the_content_must_be_at_least_10_characters'), value));
|
||||
}
|
||||
else if(code[i] == 'EXIST_ERROR') {
|
||||
return_message.push(Sprintf(General.lang('the_content_not_exist'), value));
|
||||
}
|
||||
else if(code[i] == 'STATUS_ERROR') {
|
||||
return_message.push(Sprintf(General.lang('status_error'), value));
|
||||
}
|
||||
else if(code[i] == 'CURRENT_PASSWORD_NOT_MATCH') {
|
||||
return_message.push(Sprintf(General.lang('current_password_not_match'), value));
|
||||
}
|
||||
else {
|
||||
return_message.push(code[i]);
|
||||
}
|
||||
}
|
||||
return return_message;
|
||||
}
|
||||
}
|
||||
|
||||
export default Form;
|
||||
+626
@@ -0,0 +1,626 @@
|
||||
module.exports.getYear = function() {
|
||||
let dt = new Date();
|
||||
return dt.getFullYear();
|
||||
};
|
||||
|
||||
module.exports.logout = function() {
|
||||
localStorage.removeItem('user_detail');
|
||||
localStorage.removeItem('user_jwt');
|
||||
localStorage.removeItem('login_redirect');
|
||||
window.location.replace(ENV.APP_URL + 'login');
|
||||
};
|
||||
|
||||
module.exports.tableCall = function(url, form, recall = 0, path = 'list', append = true) {
|
||||
if (recall <= 3) {
|
||||
|
||||
return form.get(
|
||||
ENV.API_URL + url,
|
||||
{
|
||||
'Authorization': 'Bearer '+ TOKEN,
|
||||
'App-Key': ENV.APP_KEY
|
||||
}
|
||||
).then(response => {
|
||||
if (append) {
|
||||
let query_string = response.query_string ? path + '?' + response.query_string : path;
|
||||
window.history.replaceState('', '', query_string);
|
||||
}
|
||||
return response
|
||||
})
|
||||
.catch(function (error) {
|
||||
if(error.response) {
|
||||
if (recall >= 3 && error.response.status == 401) {
|
||||
module.exports.logout();
|
||||
}
|
||||
console.log(recall++);
|
||||
return module.exports.tableCall(url, form, recall++, path, append);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.getCall = function(url, form, recall = 0) {
|
||||
if (recall <= 3) {
|
||||
return form.get(
|
||||
ENV.API_URL + url,
|
||||
{
|
||||
'Authorization': 'Bearer '+ TOKEN,
|
||||
'App-Key': ENV.APP_KEY
|
||||
}
|
||||
).then(response => {
|
||||
return response
|
||||
})
|
||||
.catch(function (error) {
|
||||
if(error.response) {
|
||||
if (recall >= 3 && error.response.status == 401) {
|
||||
module.exports.logout();
|
||||
}
|
||||
console.log(recall++);
|
||||
return module.exports.getCall(url, form, recall++);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.validCheck = function(url, form, ref_inputs) {
|
||||
return new Promise((resolve, reject) => {
|
||||
form.post(
|
||||
ENV.API_URL + url,
|
||||
{
|
||||
'Authorization': 'Bearer '+ TOKEN,
|
||||
'App-Key': ENV.APP_KEY
|
||||
}
|
||||
).then(data => {
|
||||
resolve(true);
|
||||
})
|
||||
.catch(error => {
|
||||
let ref_inputs_data = ref_inputs;
|
||||
let error_status = true;
|
||||
Object.keys(ref_inputs_data).forEach(function (key) {
|
||||
if (form.errors.has(ref_inputs_data[key])) {
|
||||
error_status = false;
|
||||
}
|
||||
});
|
||||
if (!error_status) {
|
||||
resolve(false);
|
||||
}
|
||||
else {
|
||||
resolve(true);
|
||||
form.errors.clear();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.onComplete = function(url, form, method) {
|
||||
return new Promise((resolve, reject) => {
|
||||
form[method](
|
||||
ENV.API_URL + url,
|
||||
{
|
||||
'Authorization': 'Bearer '+ TOKEN,
|
||||
'App-Key': ENV.APP_KEY
|
||||
}
|
||||
).then(data => {
|
||||
resolve(data);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(false);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.getURLParam = function(key) {
|
||||
const queryString = require('query-string');
|
||||
const parsed = queryString.parse(location.search);
|
||||
return parsed[key];
|
||||
};
|
||||
|
||||
module.exports.getStringParam = function(key, url) {
|
||||
url = url.split('?');
|
||||
url = url[1] ? url[1] : url[0];
|
||||
|
||||
const queryString = require('query-string');
|
||||
const parsed = queryString.parse('?' + url);
|
||||
return parsed[key];
|
||||
};
|
||||
|
||||
module.exports.getKey = function(set = [], type = 'key', null_type = 'text') {
|
||||
let return_data = [];
|
||||
|
||||
if (set.length > 0) {
|
||||
set.forEach(function(element, index) {
|
||||
return_data.push(element[type] ? element[type] : element[null_type]);
|
||||
});
|
||||
}
|
||||
return return_data;
|
||||
};
|
||||
|
||||
module.exports.autoCompleteKeyConvert = function(set = [], tag = '') {
|
||||
let return_date = [];
|
||||
set.forEach(function(element, index) {
|
||||
return_date.push({
|
||||
'id': element.id,
|
||||
'hash_id': element.hash_id,
|
||||
'row': element,
|
||||
'text': element.name,
|
||||
'tiClasses':[ "ti-valid" ]
|
||||
});
|
||||
});
|
||||
|
||||
return return_date;
|
||||
};
|
||||
|
||||
module.exports.initFormArray = function(set, form_field) {
|
||||
let return_date = [];
|
||||
|
||||
if (form_field[0]) {
|
||||
form_field.forEach(function(element, index) {
|
||||
return_date.push(module.exports.filterByProperty(set, 'id', element))
|
||||
});
|
||||
}
|
||||
return return_date;
|
||||
};
|
||||
|
||||
module.exports.selectStructure = function(set = [], id = '', value = '', other = null, other_2 = null) {
|
||||
let return_set = {
|
||||
'data': [],
|
||||
'actual_data': set
|
||||
};
|
||||
set.forEach(function(element, index) {
|
||||
return_set.data.push({
|
||||
'key': element[id],
|
||||
'value': element[value] + ' ' + (other ? '(' + element[other][0].name + ')' : '') + ' ' + (other_2 ? '(' + element[other_2][0].name + ')' : '')
|
||||
});
|
||||
});
|
||||
|
||||
return return_set;
|
||||
};
|
||||
|
||||
module.exports.returnAray = function(set) {
|
||||
if (set !== undefined && set !== null) {
|
||||
set = Array.isArray(set) ? set : [set];
|
||||
}
|
||||
else {
|
||||
set = [];
|
||||
}
|
||||
return set;
|
||||
};
|
||||
|
||||
|
||||
// module.exports.postCall = function(url, params = {}) {
|
||||
// return axios.post(
|
||||
// ENV.API_URL + url, params, {
|
||||
// headers: {
|
||||
// 'Authorization': 'Bearer '+ TOKEN
|
||||
// }
|
||||
// }).then(response => {
|
||||
// return response.data
|
||||
// })
|
||||
// .catch(function (error) {
|
||||
|
||||
// });
|
||||
// };
|
||||
|
||||
// module.exports.putCall = function(url, params = {}) {
|
||||
// return axios.put(
|
||||
// ENV.API_URL + url, params, {
|
||||
// headers: {
|
||||
// 'Authorization': 'Bearer '+ TOKEN
|
||||
// }
|
||||
// }).then(response => {
|
||||
// return response.data
|
||||
// })
|
||||
// .catch(function (error) {
|
||||
|
||||
// });
|
||||
// };
|
||||
|
||||
|
||||
|
||||
// module.exports.setpUpdate = function(url, ref_inputs, form) {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// form.put(
|
||||
// ENV.API_URL + url, {
|
||||
// headers: {
|
||||
// 'Authorization': 'Bearer '+ TOKEN
|
||||
// }
|
||||
// }).then(data => {
|
||||
// resolve(true);
|
||||
// })
|
||||
// .catch(error => {
|
||||
// let ref_inputs_data = ref_inputs.split(',');
|
||||
// let error_status = true;
|
||||
// Object.keys(ref_inputs_data).forEach(function (key) {
|
||||
|
||||
// // variation_rule
|
||||
// if(typeof form[ref_inputs_data[key]] === 'object') {
|
||||
// if (ref_inputs_data[key] == 'variation_rule' && form[ref_inputs_data[key]].type) {
|
||||
// if (form[ref_inputs_data[key]].type == 1) {
|
||||
// let set = form[ref_inputs_data[key]].content.detail;
|
||||
// set.forEach(function(element, index) {
|
||||
// let set_key = Object.keys(element);
|
||||
// set_key.forEach(function(element_2, index_2) {
|
||||
// if (form.errors.has(ref_inputs_data[key] + '.content.detail.' + index + '.' + element_2)) {
|
||||
// error_status = false;
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
// else {
|
||||
// let set = form[ref_inputs_data[key]].content.detail;
|
||||
|
||||
// set.forEach(function(element, index) {
|
||||
// let set_2 = element.content.detail;
|
||||
// set_2.forEach(function(element_2, index_2) {
|
||||
// let set_key = Object.keys(element_2);
|
||||
// set_key.forEach(function(element_3, index_3) {
|
||||
// if (form.errors.has(ref_inputs_data[key] + '.content.detail.' + index + '.content.detail.' + index_2 + '.' + element_3)) {
|
||||
// error_status = false;
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (Array.isArray(form[ref_inputs_data[key]])) {
|
||||
// let set = form[ref_inputs_data[key]];
|
||||
// set.forEach(function(element, index) {
|
||||
// let set_key = Object.keys(element);
|
||||
// set_key.forEach(function(element_2, index_2) {
|
||||
// if (form.errors.has(ref_inputs_data[key] + '.' + index + '.' + element_2)) {
|
||||
// error_status = false;
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
// else {
|
||||
// if (form.errors.has(ref_inputs_data[key])) {
|
||||
// error_status = false;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// if (!error_status) {
|
||||
// resolve(false);
|
||||
// }
|
||||
// else {
|
||||
// resolve(true);
|
||||
// form.errors.clear();
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// };
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports.filterByProperty = function(array, prop, value) {
|
||||
for(var i = 0; i < array.length; i++){
|
||||
var obj = array[i];
|
||||
for(var key in obj){
|
||||
if(typeof(obj[key] == "object")){
|
||||
var item = obj[key];
|
||||
if(key == prop && item == value){
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports.arrayConvert = function(set = []) {
|
||||
let return_date = [];
|
||||
|
||||
set.forEach(function(element, index) {
|
||||
return_date.push({
|
||||
'id': element,
|
||||
'name': element
|
||||
});
|
||||
});
|
||||
return return_date;
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports.imageBase64 = function(imgs) {
|
||||
let num = 1;
|
||||
let base = [];
|
||||
let count = imgs.length;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
Array.from(imgs).forEach(file => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
base.push(reader.result);
|
||||
if (num >= count) {
|
||||
resolve(base);
|
||||
}
|
||||
num++;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.toastedMessage = function(type, object, key) {
|
||||
let message = '';
|
||||
let value = [
|
||||
{att: object},
|
||||
{att: key},
|
||||
];
|
||||
|
||||
if (type == 'create') {
|
||||
message = Sprintf(module.exports.lang('have_been_successfully_created'), {value: value});
|
||||
}
|
||||
else if(type == 'edit') {
|
||||
message = Sprintf(module.exports.lang('have_been_successfully_updated'), {value: value});
|
||||
}
|
||||
|
||||
Vue.toasted.success(message, {
|
||||
icon : {
|
||||
name : 'check',
|
||||
}
|
||||
}).goAway(3500);
|
||||
};
|
||||
|
||||
module.exports.lang = function(code) {
|
||||
let lang_selected = localStorage.getItem('lang_selected');
|
||||
if (lang_selected == 'cn') {
|
||||
return Cn[code];
|
||||
}
|
||||
else {
|
||||
return En[code];
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.selectOptionConvert = function(set = [], id = '', value = '', other = null, other_2 = null) {
|
||||
let return_set = [];
|
||||
set.forEach(function(element, index) {
|
||||
return_set.push({
|
||||
'key': element[id],
|
||||
'value': element[value] + ' ' + (other ? '(' + element[other][0].name + ')' : '') + ' ' + (other_2 ? '(' + element[other_2][0].name + ')' : '')
|
||||
});
|
||||
});
|
||||
return return_set;
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports.filterOut = function(set = [], out = []) {
|
||||
let filter_set = [];
|
||||
|
||||
set.forEach(function(element, index) {
|
||||
let allow = true;
|
||||
out.forEach(function(element_out, index_out) {
|
||||
if (element.id == element_out) {
|
||||
allow = false;
|
||||
}
|
||||
});
|
||||
if (allow == true) {
|
||||
filter_set.push(element);
|
||||
}
|
||||
});
|
||||
|
||||
return filter_set;
|
||||
};
|
||||
|
||||
module.exports.videoBase64 = function(video) {
|
||||
let num = 1;
|
||||
let base = [];
|
||||
let count = video.length;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
Array.from(video).forEach(file => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
base.push(reader.result);
|
||||
if (num >= count) {
|
||||
resolve(base);
|
||||
}
|
||||
num++;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.errorData = function(data) {
|
||||
let error_data = [];
|
||||
let error_message = '';
|
||||
Object.keys(data).forEach(function(key) {
|
||||
error_data.push(data[key]);
|
||||
});
|
||||
error_message = error_data.join('<br>');
|
||||
return error_message;
|
||||
};
|
||||
|
||||
module.exports.timeSince = function(date) {
|
||||
var seconds = Math.floor((new Date() - date) / 1000);
|
||||
|
||||
var interval = Math.floor(seconds / 31536000);
|
||||
|
||||
if (interval > 1) {
|
||||
return 'Active ' + interval + ' Years Ago';
|
||||
}
|
||||
interval = (seconds / 2592000);
|
||||
if (interval > 1) {
|
||||
return 'Active ' + Math.floor(interval) + ' Months Ago';
|
||||
}
|
||||
interval = (seconds / 86400);
|
||||
if (interval > 1) {
|
||||
return 'Active ' + Math.floor(interval) + ' Days Ago';
|
||||
}
|
||||
interval = (seconds / 3600);
|
||||
if (interval > 1) {
|
||||
return 'Active' + Math.floor(interval) + ' Hours Ago';
|
||||
}
|
||||
interval = (seconds / 60);
|
||||
if (interval > 1) {
|
||||
return 'Active ' + Math.floor(interval) + ' Minutes Ago';
|
||||
}
|
||||
return 'Active ' + Math.floor(seconds) + ' Seconds Ago';
|
||||
};
|
||||
|
||||
module.exports.shortenNumber = function(num) {
|
||||
let nformat = 0;
|
||||
|
||||
if (num < 1000) {
|
||||
nformat = number_format(num);
|
||||
}
|
||||
else if (num < 1000000) {
|
||||
// Anything less than a million
|
||||
nformat = (num / 1000).toFixed(1) + 'k';
|
||||
}
|
||||
else if (num < 1000000000) {
|
||||
// Anything less than a billion
|
||||
nformat = (num / 1000000).toFixed(3) + 'm';
|
||||
}
|
||||
else {
|
||||
// At least a billion
|
||||
nformat = (num / 1000000000, 3).toFixed(3) + 'b';
|
||||
}
|
||||
|
||||
return nformat;
|
||||
};
|
||||
|
||||
// public static function shortenNumber($number){
|
||||
// if ($number < 1000) {
|
||||
// $nformat = number_format($number);
|
||||
// }else if ($number < 1000000) {
|
||||
// // Anything less than a million
|
||||
// $nformat = number_format($number/1000,1) . 'k';
|
||||
// } else if ($number < 1000000000) {
|
||||
// // Anything less than a billion
|
||||
// $nformat = number_format($number / 1000000, 3) . 'm';
|
||||
// } else {
|
||||
// // At least a billion
|
||||
// $nformat = number_format($number / 1000000000, 3) . 'b';
|
||||
// }
|
||||
|
||||
// return $nformat;
|
||||
// }
|
||||
|
||||
|
||||
// module.exports.selectStructureNonDB = function(set = []) {
|
||||
// let return_set = {
|
||||
// 'data': [],
|
||||
// 'actual_data': set.data
|
||||
// };
|
||||
// set.data.forEach(function(element, index) {
|
||||
// return_set.data.push({
|
||||
// 'key': element,
|
||||
// 'value': element
|
||||
// });
|
||||
// });
|
||||
// return return_set;
|
||||
// };
|
||||
|
||||
|
||||
|
||||
module.exports.httpConvert = function(params) {
|
||||
let query_string = '';
|
||||
Object.keys(params).forEach(function(element, index) {
|
||||
if( Object.prototype.toString.call( params[element] ) === '[object Array]' ) {
|
||||
if (params[element][0]) {
|
||||
params[element].forEach(function(element_2, index_2) {
|
||||
query_string += element + '[]=' + element_2 + '&';
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (params[element]) {
|
||||
console.log(params[element]);
|
||||
query_string += element + '=' + params[element] + '&';
|
||||
}
|
||||
}
|
||||
});
|
||||
return query_string;
|
||||
};
|
||||
|
||||
|
||||
// return filter_set;
|
||||
// };
|
||||
|
||||
// module.exports.pluckID = function(set = []) {
|
||||
// let return_set = [];
|
||||
// set.forEach(function(element, index) {
|
||||
// return_set.push(element.id);
|
||||
// });
|
||||
// return return_set;
|
||||
// };
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// module.exports.dateConvert = function(date) {
|
||||
// let return_date = '';
|
||||
// if (date) {
|
||||
// let parts = date.split('-');
|
||||
// return_date = new Date(parts[2], parts[1] - 1, parts[0]);
|
||||
// }
|
||||
// return return_date;
|
||||
// };
|
||||
|
||||
// module.exports.foreachErrorToasted = function(data) {
|
||||
// let error_data = module.exports.errorData(data);
|
||||
// error_data.forEach(function(element, index) {
|
||||
// Vue.toasted.error(element, {
|
||||
// icon : {
|
||||
// name : 'close',
|
||||
// }
|
||||
// }).goAway(3500);
|
||||
|
||||
// });
|
||||
// };
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// module.exports.getURLParam = function(key) {
|
||||
// const queryString = require('query-string');
|
||||
// const parsed = queryString.parse(location.search);
|
||||
// return parsed[key];
|
||||
// };
|
||||
|
||||
// module.exports.returnAray = function(set) {
|
||||
// if (set !== undefined) {
|
||||
// set = Array.isArray(set) ? set : [set];
|
||||
// }
|
||||
// else {
|
||||
// set = [];
|
||||
// }
|
||||
// return set;
|
||||
// };
|
||||
|
||||
// module.exports.autoCompleteKeyConvert = function(set = []) {
|
||||
// let return_date = [];
|
||||
// set.forEach(function(element, index) {
|
||||
// return_date.push({
|
||||
// 'id': element.id,
|
||||
// 'row': element,
|
||||
// 'text': element.name,
|
||||
// 'tiClasses':[ "ti-valid" ]
|
||||
// });
|
||||
// });
|
||||
|
||||
// return return_date;
|
||||
// };
|
||||
|
||||
// module.exports.formatNumber = function(set, form_field) {
|
||||
// if (form_field[0]) {
|
||||
// form_field.forEach(function(num) {
|
||||
// return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
module.exports.menuDefine = function(permission = []) {
|
||||
let menu = [];
|
||||
|
||||
menu.push({
|
||||
header: true,
|
||||
title: 'Main Navigation',
|
||||
});
|
||||
/////////////////////////////////////////////////////
|
||||
menu.push({
|
||||
title: 'Dashboard',
|
||||
icon: 'fa fa-home',
|
||||
child: [
|
||||
{
|
||||
href: '/',
|
||||
title: 'Analytics',
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
if (permission.includes('view user')) {
|
||||
let admin_access_menu = {};
|
||||
admin_access_menu = {
|
||||
title: 'User',
|
||||
icon: 'fa fa-user',
|
||||
child: [
|
||||
{
|
||||
href: '/user/list',
|
||||
title: 'List',
|
||||
},
|
||||
]
|
||||
};
|
||||
menu.push(admin_access_menu);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
if (permission.includes('view admin')) {
|
||||
let admin_access_menu = {};
|
||||
admin_access_menu = {
|
||||
title: 'Admin',
|
||||
icon: 'fa fa-user-astronaut',
|
||||
child: [
|
||||
{
|
||||
href: '/admin/list',
|
||||
title: 'List',
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
if (permission.includes('add admin')) {
|
||||
admin_access_menu.child.push(
|
||||
{
|
||||
href: '/admin/create',
|
||||
title: 'Create',
|
||||
}
|
||||
);
|
||||
}
|
||||
menu.push(admin_access_menu);
|
||||
}
|
||||
/////////////////////////////////////////////////////
|
||||
if (permission.includes('view role')) {
|
||||
let admin_access_menu = {};
|
||||
admin_access_menu = {
|
||||
title: 'Role',
|
||||
icon: 'fa fa-user-tag',
|
||||
child: [
|
||||
{
|
||||
href: '/admin-role/list',
|
||||
title: 'Admin List',
|
||||
},
|
||||
{
|
||||
href: '/reviewer-role/list',
|
||||
title: 'Reviewer List',
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
menu.push(admin_access_menu);
|
||||
}
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
return menu;
|
||||
};
|
||||
Vendored
+1
-1
@@ -210,7 +210,7 @@
|
||||
}
|
||||
|
||||
.big-logo {
|
||||
width: 250px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
// Applications
|
||||
|
||||
$app-layout-sidebar-width: 270px;
|
||||
$app-layout-aside-width: 60px;
|
||||
$app-layout-header-height: 50px;
|
||||
|
||||
.app-inner-layout {
|
||||
|
||||
&.rm-sidebar {
|
||||
.app-inner-layout__wrapper {
|
||||
.app-inner-layout__content {
|
||||
width: auto;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.app-inner-layout__header {
|
||||
width: 100%;
|
||||
padding: $layout-spacer-x;
|
||||
text-align: left;
|
||||
|
||||
.app-page-title {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
border-bottom: $gray-200 solid 1px;
|
||||
}
|
||||
|
||||
.app-inner-layout__header-boxed {
|
||||
padding: $layout-spacer-x;
|
||||
|
||||
.app-inner-layout__header {
|
||||
@include border-radius($border-radius-lg);
|
||||
}
|
||||
}
|
||||
|
||||
.app-inner-layout__wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
min-height: 100vh;
|
||||
|
||||
.app-inner-layout__content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
|
||||
&.card {
|
||||
box-shadow: 0 0 0 0 transparent;
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.app-inner-layout__top-pane {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
padding: ($layout-spacer-x / 2) $layout-spacer-x;
|
||||
}
|
||||
|
||||
.pane-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pane-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.app-inner-layout__bottom-pane {
|
||||
padding: $layout-spacer-x;
|
||||
border-top: $gray-200 solid 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.app-inner-layout__sidebar {
|
||||
width: $app-layout-sidebar-width;
|
||||
list-style: none;
|
||||
text-align: left;
|
||||
order: -1;
|
||||
flex: 0 0 $app-layout-sidebar-width;
|
||||
display: flex;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
|
||||
.dropdown-item {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
&.card {
|
||||
box-shadow: 0 0 0 0 transparent;
|
||||
border-radius: 0;
|
||||
background: $gray-100;
|
||||
border: 0;
|
||||
border-right: $gray-200 solid 1px;
|
||||
border-left: $gray-200 solid 1px;
|
||||
}
|
||||
|
||||
.app-inner-layout__sidebar-footer,
|
||||
.app-inner-layout__sidebar-header {
|
||||
background: $gray-100;
|
||||
}
|
||||
}
|
||||
|
||||
.app-inner-layout__aside {
|
||||
width: $app-layout-aside-width;
|
||||
}
|
||||
}
|
||||
|
||||
.app-inner-layout__footer {
|
||||
width: 100%;
|
||||
height: $app-layout-header-height;
|
||||
}
|
||||
}
|
||||
|
||||
.app-wrapper-footer {
|
||||
.app-footer {
|
||||
border-top: $gray-200 solid 1px;
|
||||
|
||||
.app-footer__inner {
|
||||
border-left: $gray-200 solid 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Components
|
||||
|
||||
@import "chat";
|
||||
|
||||
// Responsive
|
||||
|
||||
.mobile-app-menu-btn {
|
||||
display: none;
|
||||
margin: 3px $layout-spacer-x 0 0;
|
||||
}
|
||||
|
||||
@import "responsive";
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// Chat
|
||||
$app-layout-chat-sidebar-width: 360px;
|
||||
|
||||
.chat-layout {
|
||||
&.app-inner-layout {
|
||||
.app-inner-layout__sidebar {
|
||||
width: $app-layout-chat-sidebar-width;
|
||||
flex: 0 0 $app-layout-chat-sidebar-width;
|
||||
}
|
||||
}
|
||||
|
||||
.app-inner-layout__top-pane h4 {
|
||||
font-size: $h5-font-size;
|
||||
|
||||
div {
|
||||
font-size: $font-size-base;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-box-wrapper {
|
||||
padding: $layout-spacer-x;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(lg) {
|
||||
.chat-layout {
|
||||
&.app-inner-layout {
|
||||
.app-inner-layout__sidebar {
|
||||
.widget-content .widget-content-left .widget-subheading {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Responsive Applications
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
.app-inner-layout__sidebar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.mobile-app-menu-btn {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.open-mobile-menu {
|
||||
|
||||
.app-inner-layout__sidebar {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// Base styles
|
||||
//
|
||||
|
||||
.alert {
|
||||
position: relative;
|
||||
padding: $alert-padding-y $alert-padding-x;
|
||||
margin-bottom: $alert-margin-bottom;
|
||||
border: $alert-border-width solid transparent;
|
||||
@include border-radius($alert-border-radius);
|
||||
}
|
||||
|
||||
// Headings for larger alerts
|
||||
.alert-heading {
|
||||
// Specified to prevent conflicts of changing $headings-color
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
// Provide class for links that match alerts
|
||||
.alert-link {
|
||||
font-weight: $alert-link-font-weight;
|
||||
}
|
||||
|
||||
|
||||
// Dismissible alerts
|
||||
//
|
||||
// Expand the right padding and account for the close button's positioning.
|
||||
|
||||
.alert-dismissible {
|
||||
padding-right: $close-font-size + $alert-padding-x * 2;
|
||||
|
||||
// Adjust close link position
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: $alert-padding-y $alert-padding-x;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Alternate styles
|
||||
//
|
||||
// Generate contextual modifier classes for colorizing the alert.
|
||||
|
||||
@each $color, $value in $theme-colors {
|
||||
.alert-#{$color} {
|
||||
@include alert-variant(theme-color-level($color, $alert-bg-level), theme-color-level($color, $alert-border-level), theme-color-level($color, $alert-color-level));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Base class
|
||||
//
|
||||
// Requires one of the contextual, color modifier classes for `color` and
|
||||
// `background-color`.
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: $badge-padding-y $badge-padding-x;
|
||||
font-size: $badge-font-size;
|
||||
font-weight: $badge-font-weight;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: baseline;
|
||||
@include border-radius($badge-border-radius);
|
||||
|
||||
@at-root a#{&} {
|
||||
@include hover-focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Empty badges collapse automatically
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Quick fix for badges in buttons
|
||||
.btn .badge {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
// Pill badges
|
||||
//
|
||||
// Make them extra rounded with a modifier to replace v3's badges.
|
||||
|
||||
.badge-pill {
|
||||
padding-right: $badge-pill-padding-x;
|
||||
padding-left: $badge-pill-padding-x;
|
||||
@include border-radius($badge-pill-border-radius);
|
||||
}
|
||||
|
||||
// Colors
|
||||
//
|
||||
// Contextual variations (linked badges get darker on :hover).
|
||||
|
||||
@each $color, $value in $theme-colors {
|
||||
.badge-#{$color} {
|
||||
@include badge-variant($value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: $breadcrumb-padding-y $breadcrumb-padding-x;
|
||||
margin-bottom: $breadcrumb-margin-bottom;
|
||||
list-style: none;
|
||||
background-color: $breadcrumb-bg;
|
||||
@include border-radius($breadcrumb-border-radius);
|
||||
}
|
||||
|
||||
.breadcrumb-item {
|
||||
// The separator between breadcrumbs (by default, a forward-slash: "/")
|
||||
+ .breadcrumb-item {
|
||||
padding-left: $breadcrumb-item-padding;
|
||||
|
||||
&::before {
|
||||
display: inline-block; // Suppress underlining of the separator in modern browsers
|
||||
padding-right: $breadcrumb-item-padding;
|
||||
color: $breadcrumb-divider-color;
|
||||
content: $breadcrumb-divider;
|
||||
}
|
||||
}
|
||||
|
||||
// IE9-11 hack to properly handle hyperlink underlines for breadcrumbs built
|
||||
// without `<ul>`s. The `::before` pseudo-element generates an element
|
||||
// *within* the .breadcrumb-item and thereby inherits the `text-decoration`.
|
||||
//
|
||||
// To trick IE into suppressing the underline, we give the pseudo-element an
|
||||
// underline and then immediately remove it.
|
||||
+ .breadcrumb-item:hover::before {
|
||||
text-decoration: underline;
|
||||
}
|
||||
// stylelint-disable-next-line no-duplicate-selectors
|
||||
+ .breadcrumb-item:hover::before {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: $breadcrumb-active-color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// stylelint-disable selector-no-qualifying-type
|
||||
|
||||
// Make the div behave like a button
|
||||
.btn-group,
|
||||
.btn-group-vertical {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
vertical-align: middle; // match .btn alignment given font-size hack above
|
||||
|
||||
> .btn {
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
|
||||
// Bring the hover, focused, and "active" buttons to the front to overlay
|
||||
// the borders properly
|
||||
@include hover {
|
||||
z-index: 1;
|
||||
}
|
||||
&:focus,
|
||||
&:active,
|
||||
&.active {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Optional: Group multiple button groups together for a toolbar
|
||||
.btn-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
|
||||
.input-group {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
// Prevent double borders when buttons are next to each other
|
||||
> .btn:not(:first-child),
|
||||
> .btn-group:not(:first-child) {
|
||||
margin-left: -$btn-border-width;
|
||||
}
|
||||
|
||||
// Reset rounded corners
|
||||
> .btn:not(:last-child):not(.dropdown-toggle),
|
||||
> .btn-group:not(:last-child) > .btn {
|
||||
@include border-right-radius(0);
|
||||
}
|
||||
|
||||
> .btn:not(:first-child),
|
||||
> .btn-group:not(:first-child) > .btn {
|
||||
@include border-left-radius(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Sizing
|
||||
//
|
||||
// Remix the default button sizing classes into new ones for easier manipulation.
|
||||
|
||||
.btn-group-sm > .btn { @extend .btn-sm; }
|
||||
.btn-group-lg > .btn { @extend .btn-lg; }
|
||||
|
||||
|
||||
//
|
||||
// Split button dropdowns
|
||||
//
|
||||
|
||||
.dropdown-toggle-split {
|
||||
padding-right: $btn-padding-x * .75;
|
||||
padding-left: $btn-padding-x * .75;
|
||||
|
||||
&::after,
|
||||
.dropup &::after,
|
||||
.dropright &::after {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.dropleft &::before {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-sm + .dropdown-toggle-split {
|
||||
padding-right: $btn-padding-x-sm * .75;
|
||||
padding-left: $btn-padding-x-sm * .75;
|
||||
}
|
||||
|
||||
.btn-lg + .dropdown-toggle-split {
|
||||
padding-right: $btn-padding-x-lg * .75;
|
||||
padding-left: $btn-padding-x-lg * .75;
|
||||
}
|
||||
|
||||
|
||||
// The clickable button for toggling the menu
|
||||
// Set the same inset shadow as the :active state
|
||||
.btn-group.show .dropdown-toggle {
|
||||
@include box-shadow($btn-active-box-shadow);
|
||||
|
||||
// Show no shadow for `.btn-link` since it has no other button styles.
|
||||
&.btn-link {
|
||||
@include box-shadow(none);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Vertical button groups
|
||||
//
|
||||
|
||||
.btn-group-vertical {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
|
||||
> .btn,
|
||||
> .btn-group {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
> .btn:not(:first-child),
|
||||
> .btn-group:not(:first-child) {
|
||||
margin-top: -$btn-border-width;
|
||||
}
|
||||
|
||||
// Reset rounded corners
|
||||
> .btn:not(:last-child):not(.dropdown-toggle),
|
||||
> .btn-group:not(:last-child) > .btn {
|
||||
@include border-bottom-radius(0);
|
||||
}
|
||||
|
||||
> .btn:not(:first-child),
|
||||
> .btn-group:not(:first-child) > .btn {
|
||||
@include border-top-radius(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Checkbox and radio options
|
||||
//
|
||||
// In order to support the browser's form validation feedback, powered by the
|
||||
// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use
|
||||
// `display: none;` or `visibility: hidden;` as that also hides the popover.
|
||||
// Simply visually hiding the inputs via `opacity` would leave them clickable in
|
||||
// certain cases which is prevented by using `clip` and `pointer-events`.
|
||||
// This way, we ensure a DOM element is visible to position the popover from.
|
||||
//
|
||||
// See https://github.com/twbs/bootstrap/pull/12794 and
|
||||
// https://github.com/twbs/bootstrap/pull/14559 for more information.
|
||||
|
||||
.btn-group-toggle {
|
||||
> .btn,
|
||||
> .btn-group > .btn {
|
||||
margin-bottom: 0; // Override default `<label>` value
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
position: absolute;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
// stylelint-disable selector-no-qualifying-type
|
||||
|
||||
//
|
||||
// Base styles
|
||||
//
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
font-weight: $btn-font-weight;
|
||||
color: $body-color;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
user-select: none;
|
||||
background-color: transparent;
|
||||
border: $btn-border-width solid transparent;
|
||||
@include button-size($btn-padding-y, $btn-padding-x, $btn-font-size, $btn-line-height, $btn-border-radius);
|
||||
@include transition($btn-transition);
|
||||
|
||||
@include hover {
|
||||
color: $body-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&.focus {
|
||||
outline: 0;
|
||||
box-shadow: $btn-focus-box-shadow;
|
||||
}
|
||||
|
||||
// Disabled comes first so active can properly restyle
|
||||
&.disabled,
|
||||
&:disabled {
|
||||
opacity: $btn-disabled-opacity;
|
||||
@include box-shadow(none);
|
||||
}
|
||||
|
||||
// Opinionated: add "hand" cursor to non-disabled .btn elements
|
||||
&:not(:disabled):not(.disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:not(:disabled):not(.disabled):active,
|
||||
&:not(:disabled):not(.disabled).active {
|
||||
@include box-shadow($btn-active-box-shadow);
|
||||
|
||||
&:focus {
|
||||
@include box-shadow($btn-focus-box-shadow, $btn-active-box-shadow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Future-proof disabling of clicks on `<a>` elements
|
||||
a.btn.disabled,
|
||||
fieldset:disabled a.btn {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Alternate buttons
|
||||
//
|
||||
|
||||
@each $color, $value in $theme-colors {
|
||||
.btn-#{$color} {
|
||||
@include button-variant($value, $value);
|
||||
}
|
||||
}
|
||||
|
||||
@each $color, $value in $theme-colors {
|
||||
.btn-outline-#{$color} {
|
||||
@include button-outline-variant($value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Link buttons
|
||||
//
|
||||
|
||||
// Make a button look and behave like a link
|
||||
.btn-link {
|
||||
font-weight: $font-weight-normal;
|
||||
color: $link-color;
|
||||
|
||||
@include hover {
|
||||
color: $link-hover-color;
|
||||
text-decoration: $link-hover-decoration;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&.focus {
|
||||
text-decoration: $link-hover-decoration;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:disabled,
|
||||
&.disabled {
|
||||
color: $btn-link-disabled-color;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// No need for an active state here
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Button Sizes
|
||||
//
|
||||
|
||||
.btn-lg {
|
||||
@include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-line-height-lg, $btn-border-radius-lg);
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
@include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-line-height-sm, $btn-border-radius-sm);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Block button
|
||||
//
|
||||
|
||||
.btn-block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
// Vertically space out multiple block buttons
|
||||
+ .btn-block {
|
||||
margin-top: $btn-block-spacing-y;
|
||||
}
|
||||
}
|
||||
|
||||
// Specificity overrides
|
||||
input[type="submit"],
|
||||
input[type="reset"],
|
||||
input[type="button"] {
|
||||
&.btn-block {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
//
|
||||
// Base styles
|
||||
//
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
word-wrap: break-word;
|
||||
background-color: $card-bg;
|
||||
background-clip: border-box;
|
||||
border: $card-border-width solid $card-border-color;
|
||||
@include border-radius($card-border-radius);
|
||||
|
||||
> hr {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
> .list-group:first-child {
|
||||
.list-group-item:first-child {
|
||||
@include border-top-radius($card-border-radius);
|
||||
}
|
||||
}
|
||||
|
||||
> .list-group:last-child {
|
||||
.list-group-item:last-child {
|
||||
@include border-bottom-radius($card-border-radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-body {
|
||||
// Enable `flex-grow: 1` for decks and groups so that card blocks take up
|
||||
// as much space as possible, ensuring footers are aligned to the bottom.
|
||||
flex: 1 1 auto;
|
||||
padding: $card-spacer-x;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin-bottom: $card-spacer-y;
|
||||
}
|
||||
|
||||
.card-subtitle {
|
||||
margin-top: -$card-spacer-y / 2;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.card-text:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.card-link {
|
||||
@include hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
+ .card-link {
|
||||
margin-left: $card-spacer-x;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Optional textual caps
|
||||
//
|
||||
|
||||
.card-header {
|
||||
padding: $card-spacer-y $card-spacer-x;
|
||||
margin-bottom: 0; // Removes the default margin-bottom of <hN>
|
||||
color: $card-cap-color;
|
||||
background-color: $card-cap-bg;
|
||||
border-bottom: $card-border-width solid $card-border-color;
|
||||
|
||||
&:first-child {
|
||||
@include border-radius($card-inner-border-radius $card-inner-border-radius 0 0);
|
||||
}
|
||||
|
||||
+ .list-group {
|
||||
.list-group-item:first-child {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
padding: $card-spacer-y $card-spacer-x;
|
||||
background-color: $card-cap-bg;
|
||||
border-top: $card-border-width solid $card-border-color;
|
||||
|
||||
&:last-child {
|
||||
@include border-radius(0 0 $card-inner-border-radius $card-inner-border-radius);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Header navs
|
||||
//
|
||||
|
||||
.card-header-tabs.nav-tabs {
|
||||
margin-right: -$card-spacer-x / 2;
|
||||
margin-bottom: 0;
|
||||
margin-left: -$card-spacer-x / 2;
|
||||
border-bottom: 0;
|
||||
|
||||
.nav-link {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.card-header-pills.nav-pills {
|
||||
margin-right: -$card-spacer-x / 2;
|
||||
margin-left: -$card-spacer-x / 2;
|
||||
margin-bottom: 0;
|
||||
|
||||
.nav-link {
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: $white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Card image
|
||||
.card-img-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: $card-img-overlay-padding;
|
||||
}
|
||||
|
||||
.card-img {
|
||||
width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
|
||||
@include border-radius($card-inner-border-radius);
|
||||
}
|
||||
|
||||
// Card image caps
|
||||
.card-img-top {
|
||||
width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
|
||||
@include border-top-radius($card-inner-border-radius);
|
||||
}
|
||||
|
||||
.card-img-bottom {
|
||||
width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
|
||||
@include border-bottom-radius($card-inner-border-radius);
|
||||
}
|
||||
|
||||
|
||||
// Card deck
|
||||
|
||||
.card-deck {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.card {
|
||||
margin-bottom: $card-deck-margin;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
flex-flow: row wrap;
|
||||
margin-right: -$card-deck-margin;
|
||||
margin-left: -$card-deck-margin;
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
// Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4
|
||||
flex: 1 0 0%;
|
||||
flex-direction: column;
|
||||
margin-right: $card-deck-margin;
|
||||
margin-bottom: 0; // Override the default
|
||||
margin-left: $card-deck-margin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Card groups
|
||||
//
|
||||
|
||||
.card-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
// The child selector allows nested `.card` within `.card-group`
|
||||
// to display properly.
|
||||
> .card {
|
||||
margin-bottom: $card-group-margin;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
flex-flow: row wrap;
|
||||
// The child selector allows nested `.card` within `.card-group`
|
||||
// to display properly.
|
||||
> .card {
|
||||
// Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4
|
||||
flex: 1 0 0%;
|
||||
margin-bottom: 0;
|
||||
|
||||
+ .card {
|
||||
margin-left: 0;
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
// Handle rounded corners
|
||||
@if $enable-rounded {
|
||||
&:first-child {
|
||||
@include border-right-radius(0);
|
||||
|
||||
.card-img-top,
|
||||
.card-header {
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
.card-img-bottom,
|
||||
.card-footer {
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
@include border-left-radius(0);
|
||||
|
||||
.card-img-top,
|
||||
.card-header {
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
.card-img-bottom,
|
||||
.card-footer {
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:only-child {
|
||||
@include border-radius($card-border-radius);
|
||||
|
||||
.card-img-top,
|
||||
.card-header {
|
||||
@include border-top-radius($card-border-radius);
|
||||
}
|
||||
.card-img-bottom,
|
||||
.card-footer {
|
||||
@include border-bottom-radius($card-border-radius);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:first-child):not(:last-child):not(:only-child) {
|
||||
@include border-radius(0);
|
||||
|
||||
.card-img-top,
|
||||
.card-img-bottom,
|
||||
.card-header,
|
||||
.card-footer {
|
||||
@include border-radius(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Columns
|
||||
//
|
||||
|
||||
.card-columns {
|
||||
.card {
|
||||
margin-bottom: $card-columns-margin;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
column-count: $card-columns-count;
|
||||
column-gap: $card-columns-gap;
|
||||
orphans: 1;
|
||||
widows: 1;
|
||||
|
||||
.card {
|
||||
display: inline-block; // Don't let them vertically span multiple columns
|
||||
width: 100%; // Don't let their width change
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Accordion
|
||||
//
|
||||
|
||||
.accordion {
|
||||
.card {
|
||||
overflow: hidden;
|
||||
|
||||
&:not(:first-of-type) {
|
||||
.card-header:first-child {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&:not(:last-of-type) {
|
||||
border-bottom: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:first-of-type {
|
||||
border-bottom: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
margin-bottom: -$card-border-width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
// Notes on the classes:
|
||||
//
|
||||
// 1. .carousel.pointer-event should ideally be pan-y (to allow for users to scroll vertically)
|
||||
// even when their scroll action started on a carousel, but for compatibility (with Firefox)
|
||||
// we're preventing all actions instead
|
||||
// 2. The .carousel-item-left and .carousel-item-right is used to indicate where
|
||||
// the active slide is heading.
|
||||
// 3. .active.carousel-item is the current slide.
|
||||
// 4. .active.carousel-item-left and .active.carousel-item-right is the current
|
||||
// slide in its in-transition state. Only one of these occurs at a time.
|
||||
// 5. .carousel-item-next.carousel-item-left and .carousel-item-prev.carousel-item-right
|
||||
// is the upcoming slide in transition.
|
||||
|
||||
.carousel {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.carousel.pointer-event {
|
||||
touch-action: pan-y;
|
||||
}
|
||||
|
||||
.carousel-inner {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
@include clearfix();
|
||||
}
|
||||
|
||||
.carousel-item {
|
||||
position: relative;
|
||||
display: none;
|
||||
float: left;
|
||||
width: 100%;
|
||||
margin-right: -100%;
|
||||
backface-visibility: hidden;
|
||||
@include transition($carousel-transition);
|
||||
}
|
||||
|
||||
.carousel-item.active,
|
||||
.carousel-item-next,
|
||||
.carousel-item-prev {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.carousel-item-next:not(.carousel-item-left),
|
||||
.active.carousel-item-right {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.carousel-item-prev:not(.carousel-item-right),
|
||||
.active.carousel-item-left {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Alternate transitions
|
||||
//
|
||||
|
||||
.carousel-fade {
|
||||
.carousel-item {
|
||||
opacity: 0;
|
||||
transition-property: opacity;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.carousel-item.active,
|
||||
.carousel-item-next.carousel-item-left,
|
||||
.carousel-item-prev.carousel-item-right {
|
||||
z-index: 1;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.active.carousel-item-left,
|
||||
.active.carousel-item-right {
|
||||
z-index: 0;
|
||||
opacity: 0;
|
||||
@include transition(0s $carousel-transition-duration opacity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Left/right controls for nav
|
||||
//
|
||||
|
||||
.carousel-control-prev,
|
||||
.carousel-control-next {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
// Use flex for alignment (1-3)
|
||||
display: flex; // 1. allow flex styles
|
||||
align-items: center; // 2. vertically center contents
|
||||
justify-content: center; // 3. horizontally center contents
|
||||
width: $carousel-control-width;
|
||||
color: $carousel-control-color;
|
||||
text-align: center;
|
||||
opacity: $carousel-control-opacity;
|
||||
@include transition($carousel-control-transition);
|
||||
|
||||
// Hover/focus state
|
||||
@include hover-focus {
|
||||
color: $carousel-control-color;
|
||||
text-decoration: none;
|
||||
outline: 0;
|
||||
opacity: $carousel-control-hover-opacity;
|
||||
}
|
||||
}
|
||||
.carousel-control-prev {
|
||||
left: 0;
|
||||
@if $enable-gradients {
|
||||
background: linear-gradient(90deg, rgba($black, .25), rgba($black, .001));
|
||||
}
|
||||
}
|
||||
.carousel-control-next {
|
||||
right: 0;
|
||||
@if $enable-gradients {
|
||||
background: linear-gradient(270deg, rgba($black, .25), rgba($black, .001));
|
||||
}
|
||||
}
|
||||
|
||||
// Icons for within
|
||||
.carousel-control-prev-icon,
|
||||
.carousel-control-next-icon {
|
||||
display: inline-block;
|
||||
width: $carousel-control-icon-width;
|
||||
height: $carousel-control-icon-width;
|
||||
background: transparent no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.carousel-control-prev-icon {
|
||||
background-image: $carousel-control-prev-icon-bg;
|
||||
}
|
||||
.carousel-control-next-icon {
|
||||
background-image: $carousel-control-next-icon-bg;
|
||||
}
|
||||
|
||||
|
||||
// Optional indicator pips
|
||||
//
|
||||
// Add an ordered list with the following class and add a list item for each
|
||||
// slide your carousel holds.
|
||||
|
||||
.carousel-indicators {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 15;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-left: 0; // override <ol> default
|
||||
// Use the .carousel-control's width as margin so we don't overlay those
|
||||
margin-right: $carousel-control-width;
|
||||
margin-left: $carousel-control-width;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
box-sizing: content-box;
|
||||
flex: 0 1 auto;
|
||||
width: $carousel-indicator-width;
|
||||
height: $carousel-indicator-height;
|
||||
margin-right: $carousel-indicator-spacer;
|
||||
margin-left: $carousel-indicator-spacer;
|
||||
text-indent: -999px;
|
||||
cursor: pointer;
|
||||
background-color: $carousel-indicator-active-bg;
|
||||
background-clip: padding-box;
|
||||
// Use transparent borders to increase the hit area by 10px on top and bottom.
|
||||
border-top: $carousel-indicator-hit-area-height solid transparent;
|
||||
border-bottom: $carousel-indicator-hit-area-height solid transparent;
|
||||
opacity: .5;
|
||||
@include transition($carousel-indicator-transition);
|
||||
}
|
||||
|
||||
.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Optional captions
|
||||
//
|
||||
//
|
||||
|
||||
.carousel-caption {
|
||||
position: absolute;
|
||||
right: (100% - $carousel-caption-width) / 2;
|
||||
bottom: 20px;
|
||||
left: (100% - $carousel-caption-width) / 2;
|
||||
z-index: 10;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
color: $carousel-caption-color;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
.close {
|
||||
float: right;
|
||||
font-size: $close-font-size;
|
||||
font-weight: $close-font-weight;
|
||||
line-height: 1;
|
||||
color: $close-color;
|
||||
text-shadow: $close-text-shadow;
|
||||
opacity: .5;
|
||||
|
||||
// Override <a>'s hover style
|
||||
@include hover {
|
||||
color: $close-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:not(:disabled):not(.disabled) {
|
||||
@include hover-focus {
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
// Opinionated: add "hand" cursor to non-disabled .close elements
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
// Additional properties for button version
|
||||
// iOS requires the button element instead of an anchor tag.
|
||||
// If you want the anchor version, it requires `href="#"`.
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
|
||||
|
||||
// stylelint-disable-next-line selector-no-qualifying-type
|
||||
button.close {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
// Future-proof disabling of clicks on `<a>` elements
|
||||
|
||||
// stylelint-disable-next-line selector-no-qualifying-type
|
||||
a.close.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Inline code
|
||||
code {
|
||||
font-size: $code-font-size;
|
||||
color: $code-color;
|
||||
word-break: break-word;
|
||||
|
||||
// Streamline the style when inside anchors to avoid broken underline and more
|
||||
a > & {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
// User input typically entered via keyboard
|
||||
kbd {
|
||||
padding: $kbd-padding-y $kbd-padding-x;
|
||||
font-size: $kbd-font-size;
|
||||
color: $kbd-color;
|
||||
background-color: $kbd-bg;
|
||||
@include border-radius($border-radius-sm);
|
||||
@include box-shadow($kbd-box-shadow);
|
||||
|
||||
kbd {
|
||||
padding: 0;
|
||||
font-size: 100%;
|
||||
font-weight: $nested-kbd-font-weight;
|
||||
@include box-shadow(none);
|
||||
}
|
||||
}
|
||||
|
||||
// Blocks of code
|
||||
pre {
|
||||
display: block;
|
||||
font-size: $code-font-size;
|
||||
color: $pre-color;
|
||||
|
||||
// Account for some code outputs that place code tags in pre tags
|
||||
code {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
}
|
||||
|
||||
// Enable scrollable blocks of code
|
||||
.pre-scrollable {
|
||||
max-height: $pre-scrollable-max-height;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
// Embedded icons from Open Iconic.
|
||||
// Released under MIT and copyright 2014 Waybury.
|
||||
// https://useiconic.com/open
|
||||
|
||||
|
||||
// Checkboxes and radios
|
||||
//
|
||||
// Base class takes care of all the key behavioral aspects.
|
||||
|
||||
.custom-control {
|
||||
position: relative;
|
||||
display: block;
|
||||
min-height: $font-size-base * $line-height-base;
|
||||
padding-left: $custom-control-gutter + $custom-control-indicator-size;
|
||||
}
|
||||
|
||||
.custom-control-inline {
|
||||
display: inline-flex;
|
||||
margin-right: $custom-control-spacer-x;
|
||||
}
|
||||
|
||||
.custom-control-input {
|
||||
position: absolute;
|
||||
z-index: -1; // Put the input behind the label so it doesn't overlay text
|
||||
opacity: 0;
|
||||
|
||||
&:checked ~ .custom-control-label::before {
|
||||
color: $custom-control-indicator-checked-color;
|
||||
border-color: $custom-control-indicator-checked-border-color;
|
||||
@include gradient-bg($custom-control-indicator-checked-bg);
|
||||
@include box-shadow($custom-control-indicator-checked-box-shadow);
|
||||
}
|
||||
|
||||
&:focus ~ .custom-control-label::before {
|
||||
// the mixin is not used here to make sure there is feedback
|
||||
@if $enable-shadows {
|
||||
box-shadow: $input-box-shadow, $input-focus-box-shadow;
|
||||
} @else {
|
||||
box-shadow: $custom-control-indicator-focus-box-shadow;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus:not(:checked) ~ .custom-control-label::before {
|
||||
border-color: $custom-control-indicator-focus-border-color;
|
||||
}
|
||||
|
||||
&:not(:disabled):active ~ .custom-control-label::before {
|
||||
color: $custom-control-indicator-active-color;
|
||||
background-color: $custom-control-indicator-active-bg;
|
||||
border-color: $custom-control-indicator-active-border-color;
|
||||
@include box-shadow($custom-control-indicator-active-box-shadow);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
~ .custom-control-label {
|
||||
color: $custom-control-label-disabled-color;
|
||||
|
||||
&::before {
|
||||
background-color: $custom-control-indicator-disabled-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom control indicators
|
||||
//
|
||||
// Build the custom controls out of pseudo-elements.
|
||||
|
||||
.custom-control-label {
|
||||
position: relative;
|
||||
margin-bottom: 0;
|
||||
vertical-align: top;
|
||||
|
||||
// Background-color and (when enabled) gradient
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: ($font-size-base * $line-height-base - $custom-control-indicator-size) / 2;
|
||||
left: -($custom-control-gutter + $custom-control-indicator-size);
|
||||
display: block;
|
||||
width: $custom-control-indicator-size;
|
||||
height: $custom-control-indicator-size;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
background-color: $custom-control-indicator-bg;
|
||||
border: $custom-control-indicator-border-color solid $custom-control-indicator-border-width;
|
||||
@include box-shadow($custom-control-indicator-box-shadow);
|
||||
}
|
||||
|
||||
// Foreground (icon)
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: ($font-size-base * $line-height-base - $custom-control-indicator-size) / 2;
|
||||
left: -($custom-control-gutter + $custom-control-indicator-size);
|
||||
display: block;
|
||||
width: $custom-control-indicator-size;
|
||||
height: $custom-control-indicator-size;
|
||||
content: "";
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: $custom-control-indicator-bg-size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Checkboxes
|
||||
//
|
||||
// Tweak just a few things for checkboxes.
|
||||
|
||||
.custom-checkbox {
|
||||
.custom-control-label::before {
|
||||
@include border-radius($custom-checkbox-indicator-border-radius);
|
||||
}
|
||||
|
||||
.custom-control-input:checked ~ .custom-control-label {
|
||||
&::after {
|
||||
background-image: $custom-checkbox-indicator-icon-checked;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-control-input:indeterminate ~ .custom-control-label {
|
||||
&::before {
|
||||
border-color: $custom-checkbox-indicator-indeterminate-border-color;
|
||||
@include gradient-bg($custom-checkbox-indicator-indeterminate-bg);
|
||||
@include box-shadow($custom-checkbox-indicator-indeterminate-box-shadow);
|
||||
}
|
||||
&::after {
|
||||
background-image: $custom-checkbox-indicator-icon-indeterminate;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-control-input:disabled {
|
||||
&:checked ~ .custom-control-label::before {
|
||||
background-color: $custom-control-indicator-checked-disabled-bg;
|
||||
}
|
||||
&:indeterminate ~ .custom-control-label::before {
|
||||
background-color: $custom-control-indicator-checked-disabled-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Radios
|
||||
//
|
||||
// Tweak just a few things for radios.
|
||||
|
||||
.custom-radio {
|
||||
.custom-control-label::before {
|
||||
border-radius: $custom-radio-indicator-border-radius;
|
||||
}
|
||||
|
||||
.custom-control-input:checked ~ .custom-control-label {
|
||||
&::after {
|
||||
background-image: $custom-radio-indicator-icon-checked;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-control-input:disabled {
|
||||
&:checked ~ .custom-control-label::before {
|
||||
background-color: $custom-control-indicator-checked-disabled-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// switches
|
||||
//
|
||||
// Tweak a few things for switches
|
||||
|
||||
.custom-switch {
|
||||
padding-left: $custom-switch-width + $custom-control-gutter;
|
||||
|
||||
.custom-control-label {
|
||||
&::before {
|
||||
left: -($custom-switch-width + $custom-control-gutter);
|
||||
width: $custom-switch-width;
|
||||
pointer-events: all;
|
||||
border-radius: $custom-switch-indicator-border-radius;
|
||||
}
|
||||
|
||||
&::after {
|
||||
top: calc(#{(($font-size-base * $line-height-base - $custom-control-indicator-size) / 2)} + #{$custom-control-indicator-border-width * 2});
|
||||
left: calc(#{-($custom-switch-width + $custom-control-gutter)} + #{$custom-control-indicator-border-width * 2});
|
||||
width: $custom-switch-indicator-size;
|
||||
height: $custom-switch-indicator-size;
|
||||
background-color: $custom-control-indicator-border-color;
|
||||
border-radius: $custom-switch-indicator-border-radius;
|
||||
@include transition(transform .15s ease-in-out, $custom-forms-transition);
|
||||
}
|
||||
}
|
||||
|
||||
.custom-control-input:checked ~ .custom-control-label {
|
||||
&::after {
|
||||
background-color: $custom-control-indicator-bg;
|
||||
transform: translateX($custom-switch-width - $custom-control-indicator-size);
|
||||
}
|
||||
}
|
||||
|
||||
.custom-control-input:disabled {
|
||||
&:checked ~ .custom-control-label::before {
|
||||
background-color: $custom-control-indicator-checked-disabled-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Select
|
||||
//
|
||||
// Replaces the browser default select with a custom one, mostly pulled from
|
||||
// https://primer.github.io/.
|
||||
//
|
||||
|
||||
.custom-select {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: $custom-select-height;
|
||||
padding: $custom-select-padding-y ($custom-select-padding-x + $custom-select-indicator-padding) $custom-select-padding-y $custom-select-padding-x;
|
||||
font-weight: $custom-select-font-weight;
|
||||
line-height: $custom-select-line-height;
|
||||
color: $custom-select-color;
|
||||
vertical-align: middle;
|
||||
background: $custom-select-background;
|
||||
background-color: $custom-select-bg;
|
||||
border: $custom-select-border-width solid $custom-select-border-color;
|
||||
@if $enable-rounded {
|
||||
border-radius: $custom-select-border-radius;
|
||||
} @else {
|
||||
border-radius: 0;
|
||||
}
|
||||
@include box-shadow($custom-select-box-shadow);
|
||||
appearance: none;
|
||||
|
||||
&:focus {
|
||||
border-color: $custom-select-focus-border-color;
|
||||
outline: 0;
|
||||
@if $enable-shadows {
|
||||
box-shadow: $custom-select-box-shadow, $custom-select-focus-box-shadow;
|
||||
} @else {
|
||||
box-shadow: $custom-select-focus-box-shadow;
|
||||
}
|
||||
|
||||
&::-ms-value {
|
||||
// For visual consistency with other platforms/browsers,
|
||||
// suppress the default white text on blue background highlight given to
|
||||
// the selected option text when the (still closed) <select> receives focus
|
||||
// in IE and (under certain conditions) Edge.
|
||||
// See https://github.com/twbs/bootstrap/issues/19398.
|
||||
color: $input-color;
|
||||
background-color: $input-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&[multiple],
|
||||
&[size]:not([size="1"]) {
|
||||
height: auto;
|
||||
padding-right: $custom-select-padding-x;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: $custom-select-disabled-color;
|
||||
background-color: $custom-select-disabled-bg;
|
||||
}
|
||||
|
||||
// Hides the default caret in IE11
|
||||
&::-ms-expand {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-select-sm {
|
||||
height: $custom-select-height-sm;
|
||||
padding-top: $custom-select-padding-y-sm;
|
||||
padding-bottom: $custom-select-padding-y-sm;
|
||||
padding-left: $custom-select-padding-x-sm;
|
||||
font-size: $custom-select-font-size-sm;
|
||||
}
|
||||
|
||||
.custom-select-lg {
|
||||
height: $custom-select-height-lg;
|
||||
padding-top: $custom-select-padding-y-lg;
|
||||
padding-bottom: $custom-select-padding-y-lg;
|
||||
padding-left: $custom-select-padding-x-lg;
|
||||
font-size: $custom-select-font-size-lg;
|
||||
}
|
||||
|
||||
|
||||
// File
|
||||
//
|
||||
// Custom file input.
|
||||
|
||||
.custom-file {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: $custom-file-height;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.custom-file-input {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: $custom-file-height;
|
||||
margin: 0;
|
||||
opacity: 0;
|
||||
|
||||
&:focus ~ .custom-file-label {
|
||||
border-color: $custom-file-focus-border-color;
|
||||
box-shadow: $custom-file-focus-box-shadow;
|
||||
}
|
||||
|
||||
&:disabled ~ .custom-file-label {
|
||||
background-color: $custom-file-disabled-bg;
|
||||
}
|
||||
|
||||
@each $lang, $value in $custom-file-text {
|
||||
&:lang(#{$lang}) ~ .custom-file-label::after {
|
||||
content: $value;
|
||||
}
|
||||
}
|
||||
|
||||
~ .custom-file-label[data-browse]::after {
|
||||
content: attr(data-browse);
|
||||
}
|
||||
}
|
||||
|
||||
.custom-file-label {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
height: $custom-file-height;
|
||||
padding: $custom-file-padding-y $custom-file-padding-x;
|
||||
font-weight: $custom-file-font-weight;
|
||||
line-height: $custom-file-line-height;
|
||||
color: $custom-file-color;
|
||||
background-color: $custom-file-bg;
|
||||
border: $custom-file-border-width solid $custom-file-border-color;
|
||||
@include border-radius($custom-file-border-radius);
|
||||
@include box-shadow($custom-file-box-shadow);
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 3;
|
||||
display: block;
|
||||
height: $custom-file-height-inner;
|
||||
padding: $custom-file-padding-y $custom-file-padding-x;
|
||||
line-height: $custom-file-line-height;
|
||||
color: $custom-file-button-color;
|
||||
content: "Browse";
|
||||
@include gradient-bg($custom-file-button-bg);
|
||||
border-left: inherit;
|
||||
@include border-radius(0 $custom-file-border-radius $custom-file-border-radius 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Range
|
||||
//
|
||||
// Style range inputs the same across browsers. Vendor-specific rules for pseudo
|
||||
// elements cannot be mixed. As such, there are no shared styles for focus or
|
||||
// active states on prefixed selectors.
|
||||
|
||||
.custom-range {
|
||||
width: 100%;
|
||||
height: calc(#{$custom-range-thumb-height} + #{$custom-range-thumb-focus-box-shadow-width * 2});
|
||||
padding: 0; // Need to reset padding
|
||||
background-color: transparent;
|
||||
appearance: none;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
|
||||
// Pseudo-elements must be split across multiple rulesets to have an effect.
|
||||
// No box-shadow() mixin for focus accessibility.
|
||||
&::-webkit-slider-thumb { box-shadow: $custom-range-thumb-focus-box-shadow; }
|
||||
&::-moz-range-thumb { box-shadow: $custom-range-thumb-focus-box-shadow; }
|
||||
&::-ms-thumb { box-shadow: $custom-range-thumb-focus-box-shadow; }
|
||||
}
|
||||
|
||||
&::-moz-focus-outer {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
width: $custom-range-thumb-width;
|
||||
height: $custom-range-thumb-height;
|
||||
margin-top: ($custom-range-track-height - $custom-range-thumb-height) / 2; // Webkit specific
|
||||
@include gradient-bg($custom-range-thumb-bg);
|
||||
border: $custom-range-thumb-border;
|
||||
@include border-radius($custom-range-thumb-border-radius);
|
||||
@include box-shadow($custom-range-thumb-box-shadow);
|
||||
@include transition($custom-forms-transition);
|
||||
appearance: none;
|
||||
|
||||
&:active {
|
||||
@include gradient-bg($custom-range-thumb-active-bg);
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
width: $custom-range-track-width;
|
||||
height: $custom-range-track-height;
|
||||
color: transparent; // Why?
|
||||
cursor: $custom-range-track-cursor;
|
||||
background-color: $custom-range-track-bg;
|
||||
border-color: transparent;
|
||||
@include border-radius($custom-range-track-border-radius);
|
||||
@include box-shadow($custom-range-track-box-shadow);
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
width: $custom-range-thumb-width;
|
||||
height: $custom-range-thumb-height;
|
||||
@include gradient-bg($custom-range-thumb-bg);
|
||||
border: $custom-range-thumb-border;
|
||||
@include border-radius($custom-range-thumb-border-radius);
|
||||
@include box-shadow($custom-range-thumb-box-shadow);
|
||||
@include transition($custom-forms-transition);
|
||||
appearance: none;
|
||||
|
||||
&:active {
|
||||
@include gradient-bg($custom-range-thumb-active-bg);
|
||||
}
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
width: $custom-range-track-width;
|
||||
height: $custom-range-track-height;
|
||||
color: transparent;
|
||||
cursor: $custom-range-track-cursor;
|
||||
background-color: $custom-range-track-bg;
|
||||
border-color: transparent; // Firefox specific?
|
||||
@include border-radius($custom-range-track-border-radius);
|
||||
@include box-shadow($custom-range-track-box-shadow);
|
||||
}
|
||||
|
||||
&::-ms-thumb {
|
||||
width: $custom-range-thumb-width;
|
||||
height: $custom-range-thumb-height;
|
||||
margin-top: 0; // Edge specific
|
||||
margin-right: $custom-range-thumb-focus-box-shadow-width; // Workaround that overflowed box-shadow is hidden.
|
||||
margin-left: $custom-range-thumb-focus-box-shadow-width; // Workaround that overflowed box-shadow is hidden.
|
||||
@include gradient-bg($custom-range-thumb-bg);
|
||||
border: $custom-range-thumb-border;
|
||||
@include border-radius($custom-range-thumb-border-radius);
|
||||
@include box-shadow($custom-range-thumb-box-shadow);
|
||||
@include transition($custom-forms-transition);
|
||||
appearance: none;
|
||||
|
||||
&:active {
|
||||
@include gradient-bg($custom-range-thumb-active-bg);
|
||||
}
|
||||
}
|
||||
|
||||
&::-ms-track {
|
||||
width: $custom-range-track-width;
|
||||
height: $custom-range-track-height;
|
||||
color: transparent;
|
||||
cursor: $custom-range-track-cursor;
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
border-width: $custom-range-thumb-height / 2;
|
||||
@include box-shadow($custom-range-track-box-shadow);
|
||||
}
|
||||
|
||||
&::-ms-fill-lower {
|
||||
background-color: $custom-range-track-bg;
|
||||
@include border-radius($custom-range-track-border-radius);
|
||||
}
|
||||
|
||||
&::-ms-fill-upper {
|
||||
margin-right: 15px; // arbitrary?
|
||||
background-color: $custom-range-track-bg;
|
||||
@include border-radius($custom-range-track-border-radius);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
&::-webkit-slider-thumb {
|
||||
background-color: $custom-range-thumb-disabled-bg;
|
||||
}
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
background-color: $custom-range-thumb-disabled-bg;
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&::-ms-thumb {
|
||||
background-color: $custom-range-thumb-disabled-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.custom-control-label::before,
|
||||
.custom-file-label,
|
||||
.custom-select {
|
||||
@include transition($custom-forms-transition);
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
// The dropdown wrapper (`<div>`)
|
||||
.dropup,
|
||||
.dropright,
|
||||
.dropdown,
|
||||
.dropleft {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
// Generate the caret automatically
|
||||
@include caret;
|
||||
}
|
||||
|
||||
// The dropdown menu
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
z-index: $zindex-dropdown;
|
||||
display: none; // none by default, but block on "open" of the menu
|
||||
float: left;
|
||||
min-width: $dropdown-min-width;
|
||||
padding: $dropdown-padding-y 0;
|
||||
margin: $dropdown-spacer 0 0; // override default ul
|
||||
font-size: $font-size-base; // Redeclare because nesting can cause inheritance issues
|
||||
color: $body-color;
|
||||
text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)
|
||||
list-style: none;
|
||||
background-color: $dropdown-bg;
|
||||
background-clip: padding-box;
|
||||
border: $dropdown-border-width solid $dropdown-border-color;
|
||||
@include border-radius($dropdown-border-radius);
|
||||
@include box-shadow($dropdown-box-shadow);
|
||||
}
|
||||
|
||||
@each $breakpoint in map-keys($grid-breakpoints) {
|
||||
@include media-breakpoint-up($breakpoint) {
|
||||
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
|
||||
|
||||
.dropdown-menu#{$infix}-right {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@each $breakpoint in map-keys($grid-breakpoints) {
|
||||
@include media-breakpoint-up($breakpoint) {
|
||||
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
|
||||
|
||||
.dropdown-menu#{$infix}-left {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow for dropdowns to go bottom up (aka, dropup-menu)
|
||||
// Just add .dropup after the standard .dropdown class and you're set.
|
||||
.dropup {
|
||||
.dropdown-menu {
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
margin-top: 0;
|
||||
margin-bottom: $dropdown-spacer;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
@include caret(up);
|
||||
}
|
||||
}
|
||||
|
||||
.dropright {
|
||||
.dropdown-menu {
|
||||
top: 0;
|
||||
right: auto;
|
||||
left: 100%;
|
||||
margin-top: 0;
|
||||
margin-left: $dropdown-spacer;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
@include caret(right);
|
||||
&::after {
|
||||
vertical-align: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropleft {
|
||||
.dropdown-menu {
|
||||
top: 0;
|
||||
right: 100%;
|
||||
left: auto;
|
||||
margin-top: 0;
|
||||
margin-right: $dropdown-spacer;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
@include caret(left);
|
||||
&::before {
|
||||
vertical-align: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When enabled Popper.js, reset basic dropdown position
|
||||
// stylelint-disable-next-line no-duplicate-selectors
|
||||
.dropdown-menu {
|
||||
&[x-placement^="top"],
|
||||
&[x-placement^="right"],
|
||||
&[x-placement^="bottom"],
|
||||
&[x-placement^="left"] {
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Dividers (basically an `<hr>`) within the dropdown
|
||||
.dropdown-divider {
|
||||
@include nav-divider($dropdown-divider-bg);
|
||||
}
|
||||
|
||||
// Links, buttons, and more within the dropdown menu
|
||||
//
|
||||
// `<button>`-specific styles are denoted with `// For <button>s`
|
||||
.dropdown-item {
|
||||
display: block;
|
||||
width: 100%; // For `<button>`s
|
||||
padding: $dropdown-item-padding-y $dropdown-item-padding-x;
|
||||
clear: both;
|
||||
font-weight: $font-weight-normal;
|
||||
color: $dropdown-link-color;
|
||||
text-align: inherit; // For `<button>`s
|
||||
white-space: nowrap; // prevent links from randomly breaking onto new lines
|
||||
background-color: transparent; // For `<button>`s
|
||||
border: 0; // For `<button>`s
|
||||
|
||||
&:first-child {
|
||||
@include border-top-radius($dropdown-inner-border-radius);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
@include border-bottom-radius($dropdown-inner-border-radius);
|
||||
}
|
||||
|
||||
@include hover-focus {
|
||||
color: $dropdown-link-hover-color;
|
||||
text-decoration: none;
|
||||
@include gradient-bg($dropdown-link-hover-bg);
|
||||
}
|
||||
|
||||
&.active,
|
||||
&:active {
|
||||
color: $dropdown-link-active-color;
|
||||
text-decoration: none;
|
||||
@include gradient-bg($dropdown-link-active-bg);
|
||||
}
|
||||
|
||||
&.disabled,
|
||||
&:disabled {
|
||||
color: $dropdown-link-disabled-color;
|
||||
pointer-events: none;
|
||||
background-color: transparent;
|
||||
// Remove CSS gradients if they're enabled
|
||||
@if $enable-gradients {
|
||||
background-image: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
// Dropdown section headers
|
||||
.dropdown-header {
|
||||
display: block;
|
||||
padding: $dropdown-padding-y $dropdown-item-padding-x;
|
||||
margin-bottom: 0; // for use with heading elements
|
||||
font-size: $font-size-sm;
|
||||
color: $dropdown-header-color;
|
||||
white-space: nowrap; // as with > li > a
|
||||
}
|
||||
|
||||
// Dropdown text
|
||||
.dropdown-item-text {
|
||||
display: block;
|
||||
padding: $dropdown-item-padding-y $dropdown-item-padding-x;
|
||||
color: $dropdown-link-color;
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
// stylelint-disable selector-no-qualifying-type
|
||||
|
||||
//
|
||||
// Textual form controls
|
||||
//
|
||||
|
||||
.form-control {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: $input-height;
|
||||
padding: $input-padding-y $input-padding-x;
|
||||
font-size: $input-font-size;
|
||||
font-weight: $input-font-weight;
|
||||
line-height: $input-line-height;
|
||||
color: $input-color;
|
||||
background-color: $input-bg;
|
||||
background-clip: padding-box;
|
||||
border: $input-border-width solid $input-border-color;
|
||||
|
||||
// Note: This has no effect on <select>s in some browsers, due to the limited stylability of `<select>`s in CSS.
|
||||
@if $enable-rounded {
|
||||
// Manually use the if/else instead of the mixin to account for iOS override
|
||||
border-radius: $input-border-radius;
|
||||
} @else {
|
||||
// Otherwise undo the iOS default
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
@include box-shadow($input-box-shadow);
|
||||
@include transition($input-transition);
|
||||
|
||||
// Unstyle the caret on `<select>`s in IE10+.
|
||||
&::-ms-expand {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
// Customize the `:focus` state to imitate native WebKit styles.
|
||||
@include form-control-focus();
|
||||
|
||||
// Placeholder
|
||||
&::placeholder {
|
||||
color: $input-placeholder-color;
|
||||
// Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526.
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
// Disabled and read-only inputs
|
||||
//
|
||||
// HTML5 says that controls under a fieldset > legend:first-child won't be
|
||||
// disabled if the fieldset is disabled. Due to implementation difficulty, we
|
||||
// don't honor that edge case; we style them as disabled anyway.
|
||||
&:disabled,
|
||||
&[readonly] {
|
||||
background-color: $input-disabled-bg;
|
||||
// iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655.
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
select.form-control {
|
||||
&:focus::-ms-value {
|
||||
// Suppress the nested default white text on blue background highlight given to
|
||||
// the selected option text when the (still closed) <select> receives focus
|
||||
// in IE and (under certain conditions) Edge, as it looks bad and cannot be made to
|
||||
// match the appearance of the native widget.
|
||||
// See https://github.com/twbs/bootstrap/issues/19398.
|
||||
color: $input-color;
|
||||
background-color: $input-bg;
|
||||
}
|
||||
}
|
||||
|
||||
// Make file inputs better match text inputs by forcing them to new lines.
|
||||
.form-control-file,
|
||||
.form-control-range {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Labels
|
||||
//
|
||||
|
||||
// For use with horizontal and inline forms, when you need the label (or legend)
|
||||
// text to align with the form controls.
|
||||
.col-form-label {
|
||||
padding-top: calc(#{$input-padding-y} + #{$input-border-width});
|
||||
padding-bottom: calc(#{$input-padding-y} + #{$input-border-width});
|
||||
margin-bottom: 0; // Override the `<label>/<legend>` default
|
||||
font-size: inherit; // Override the `<legend>` default
|
||||
line-height: $input-line-height;
|
||||
}
|
||||
|
||||
.col-form-label-lg {
|
||||
padding-top: calc(#{$input-padding-y-lg} + #{$input-border-width});
|
||||
padding-bottom: calc(#{$input-padding-y-lg} + #{$input-border-width});
|
||||
font-size: $input-font-size-lg;
|
||||
line-height: $input-line-height-lg;
|
||||
}
|
||||
|
||||
.col-form-label-sm {
|
||||
padding-top: calc(#{$input-padding-y-sm} + #{$input-border-width});
|
||||
padding-bottom: calc(#{$input-padding-y-sm} + #{$input-border-width});
|
||||
font-size: $input-font-size-sm;
|
||||
line-height: $input-line-height-sm;
|
||||
}
|
||||
|
||||
|
||||
// Readonly controls as plain text
|
||||
//
|
||||
// Apply class to a readonly input to make it appear like regular plain
|
||||
// text (without any border, background color, focus indicator)
|
||||
|
||||
.form-control-plaintext {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding-top: $input-padding-y;
|
||||
padding-bottom: $input-padding-y;
|
||||
margin-bottom: 0; // match inputs if this class comes on inputs with default margins
|
||||
line-height: $input-line-height;
|
||||
color: $input-plaintext-color;
|
||||
background-color: transparent;
|
||||
border: solid transparent;
|
||||
border-width: $input-border-width 0;
|
||||
|
||||
&.form-control-sm,
|
||||
&.form-control-lg {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Form control sizing
|
||||
//
|
||||
// Build on `.form-control` with modifier classes to decrease or increase the
|
||||
// height and font-size of form controls.
|
||||
//
|
||||
// Repeated in `_input_group.scss` to avoid Sass extend issues.
|
||||
|
||||
.form-control-sm {
|
||||
height: $input-height-sm;
|
||||
padding: $input-padding-y-sm $input-padding-x-sm;
|
||||
font-size: $input-font-size-sm;
|
||||
line-height: $input-line-height-sm;
|
||||
@include border-radius($input-border-radius-sm);
|
||||
}
|
||||
|
||||
.form-control-lg {
|
||||
height: $input-height-lg;
|
||||
padding: $input-padding-y-lg $input-padding-x-lg;
|
||||
font-size: $input-font-size-lg;
|
||||
line-height: $input-line-height-lg;
|
||||
@include border-radius($input-border-radius-lg);
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line no-duplicate-selectors
|
||||
select.form-control {
|
||||
&[size],
|
||||
&[multiple] {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line no-duplicate-selectors
|
||||
textarea.form-control {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
// Form groups
|
||||
//
|
||||
// Designed to help with the organization and spacing of vertical forms. For
|
||||
// horizontal forms, use the predefined grid classes.
|
||||
|
||||
.form-group {
|
||||
margin-bottom: $form-group-margin-bottom;
|
||||
}
|
||||
|
||||
.form-text {
|
||||
display: block;
|
||||
margin-top: $form-text-margin-top;
|
||||
}
|
||||
|
||||
|
||||
// Form grid
|
||||
//
|
||||
// Special replacement for our grid system's `.row` for tighter form layouts.
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-right: -$form-grid-gutter-width / 2;
|
||||
margin-left: -$form-grid-gutter-width / 2;
|
||||
|
||||
> .col,
|
||||
> [class*="col-"] {
|
||||
padding-right: $form-grid-gutter-width / 2;
|
||||
padding-left: $form-grid-gutter-width / 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Checkboxes and radios
|
||||
//
|
||||
// Indent the labels to position radios/checkboxes as hanging controls.
|
||||
|
||||
.form-check {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding-left: $form-check-input-gutter;
|
||||
}
|
||||
|
||||
.form-check-input {
|
||||
position: absolute;
|
||||
margin-top: $form-check-input-margin-y;
|
||||
margin-left: -$form-check-input-gutter;
|
||||
|
||||
&:disabled ~ .form-check-label {
|
||||
color: $text-muted;
|
||||
}
|
||||
}
|
||||
|
||||
.form-check-label {
|
||||
margin-bottom: 0; // Override default `<label>` bottom margin
|
||||
}
|
||||
|
||||
.form-check-inline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding-left: 0; // Override base .form-check
|
||||
margin-right: $form-check-inline-margin-x;
|
||||
|
||||
// Undo .form-check-input defaults and add some `margin-right`.
|
||||
.form-check-input {
|
||||
position: static;
|
||||
margin-top: 0;
|
||||
margin-right: $form-check-inline-input-margin-x;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Form validation
|
||||
//
|
||||
// Provide feedback to users when form field values are valid or invalid. Works
|
||||
// primarily for client-side validation via scoped `:invalid` and `:valid`
|
||||
// pseudo-classes but also includes `.is-invalid` and `.is-valid` classes for
|
||||
// server side validation.
|
||||
|
||||
@include form-validation-state("valid", $form-feedback-valid-color);
|
||||
@include form-validation-state("invalid", $form-feedback-invalid-color);
|
||||
|
||||
// Inline forms
|
||||
//
|
||||
// Make forms appear inline(-block) by adding the `.form-inline` class. Inline
|
||||
// forms begin stacked on extra small (mobile) devices and then go inline when
|
||||
// viewports reach <768px.
|
||||
//
|
||||
// Requires wrapping inputs and labels with `.form-group` for proper display of
|
||||
// default HTML form controls and our custom form controls (e.g., input groups).
|
||||
|
||||
.form-inline {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: center; // Prevent shorter elements from growing to same height as others (e.g., small buttons growing to normal sized button height)
|
||||
|
||||
// Because we use flex, the initial sizing of checkboxes is collapsed and
|
||||
// doesn't occupy the full-width (which is what we want for xs grid tier),
|
||||
// so we force that here.
|
||||
.form-check {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// Kick in the inline
|
||||
@include media-breakpoint-up(sm) {
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
// Inline-block all the things for "inline"
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
flex-flow: row wrap;
|
||||
align-items: center;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
// Allow folks to *not* use `.form-group`
|
||||
.form-control {
|
||||
display: inline-block;
|
||||
width: auto; // Prevent labels from stacking above inputs in `.form-group`
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
// Make static controls behave like regular ones
|
||||
.form-control-plaintext {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.input-group,
|
||||
.custom-select {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
// Remove default margin on radios/checkboxes that were used for stacking, and
|
||||
// then undo the floating of radios and checkboxes to match.
|
||||
.form-check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: auto;
|
||||
padding-left: 0;
|
||||
}
|
||||
.form-check-input {
|
||||
position: relative;
|
||||
margin-top: 0;
|
||||
margin-right: $form-check-input-margin-x;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.custom-control {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.custom-control-label {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Bootstrap functions
|
||||
//
|
||||
// Utility mixins and functions for evaluating source code across our variables, maps, and mixins.
|
||||
|
||||
// Ascending
|
||||
// Used to evaluate Sass maps like our grid breakpoints.
|
||||
@mixin _assert-ascending($map, $map-name) {
|
||||
$prev-key: null;
|
||||
$prev-num: null;
|
||||
@each $key, $num in $map {
|
||||
@if $prev-num == null or unit($num) == "%" {
|
||||
// Do nothing
|
||||
} @else if not comparable($prev-num, $num) {
|
||||
@warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
|
||||
} @else if $prev-num >= $num {
|
||||
@warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !";
|
||||
}
|
||||
$prev-key: $key;
|
||||
$prev-num: $num;
|
||||
}
|
||||
}
|
||||
|
||||
// Starts at zero
|
||||
// Another grid mixin that ensures the min-width of the lowest breakpoint starts at 0.
|
||||
@mixin _assert-starts-at-zero($map) {
|
||||
$values: map-values($map);
|
||||
$first-value: nth($values, 1);
|
||||
@if $first-value != 0 {
|
||||
@warn "First breakpoint in `$grid-breakpoints` must start at 0, but starts at #{$first-value}.";
|
||||
}
|
||||
}
|
||||
|
||||
// Replace `$search` with `$replace` in `$string`
|
||||
// Used on our SVG icon backgrounds for custom forms.
|
||||
//
|
||||
// @author Hugo Giraudel
|
||||
// @param {String} $string - Initial string
|
||||
// @param {String} $search - Substring to replace
|
||||
// @param {String} $replace ('') - New value
|
||||
// @return {String} - Updated string
|
||||
@function str-replace($string, $search, $replace: "") {
|
||||
$index: str-index($string, $search);
|
||||
|
||||
@if $index {
|
||||
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
|
||||
}
|
||||
|
||||
@return $string;
|
||||
}
|
||||
|
||||
// Color contrast
|
||||
@function color-yiq($color, $dark: $yiq-text-dark, $light: $yiq-text-light) {
|
||||
$r: red($color);
|
||||
$g: green($color);
|
||||
$b: blue($color);
|
||||
|
||||
$yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
|
||||
|
||||
@if ($yiq >= $yiq-contrasted-threshold) {
|
||||
@return $dark;
|
||||
} @else {
|
||||
@return $light;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve color Sass maps
|
||||
@function color($key: "blue") {
|
||||
@return map-get($colors, $key);
|
||||
}
|
||||
|
||||
@function theme-color($key: "primary") {
|
||||
@return map-get($theme-colors, $key);
|
||||
}
|
||||
|
||||
@function gray($key: "100") {
|
||||
@return map-get($grays, $key);
|
||||
}
|
||||
|
||||
// Request a theme color level
|
||||
@function theme-color-level($color-name: "primary", $level: 0) {
|
||||
$color: theme-color($color-name);
|
||||
$color-base: if($level > 0, $black, $white);
|
||||
$level: abs($level);
|
||||
|
||||
@return mix($color-base, $color, $level * $theme-color-interval);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Container widths
|
||||
//
|
||||
// Set the container width, and override it for fixed navbars in media queries.
|
||||
|
||||
@if $enable-grid-classes {
|
||||
.container {
|
||||
@include make-container();
|
||||
@include make-container-max-widths();
|
||||
}
|
||||
}
|
||||
|
||||
// Fluid container
|
||||
//
|
||||
// Utilizes the mixin meant for fixed width containers, but with 100% width for
|
||||
// fluid, full width layouts.
|
||||
|
||||
@if $enable-grid-classes {
|
||||
.container-fluid {
|
||||
@include make-container();
|
||||
}
|
||||
}
|
||||
|
||||
// Row
|
||||
//
|
||||
// Rows contain and clear the floats of your columns.
|
||||
|
||||
@if $enable-grid-classes {
|
||||
.row {
|
||||
@include make-row();
|
||||
}
|
||||
|
||||
// Remove the negative margin from default .row, then the horizontal padding
|
||||
// from all immediate children columns (to prevent runaway style inheritance).
|
||||
.no-gutters {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
|
||||
> .col,
|
||||
> [class*="col-"] {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Columns
|
||||
//
|
||||
// Common styles for small and large grid columns
|
||||
|
||||
@if $enable-grid-classes {
|
||||
@include make-grid-columns();
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Responsive images (ensure images don't scale beyond their parents)
|
||||
//
|
||||
// This is purposefully opt-in via an explicit class rather than being the default for all `<img>`s.
|
||||
// We previously tried the "images are responsive by default" approach in Bootstrap v2,
|
||||
// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)
|
||||
// which weren't expecting the images within themselves to be involuntarily resized.
|
||||
// See also https://github.com/twbs/bootstrap/issues/18178
|
||||
.img-fluid {
|
||||
@include img-fluid;
|
||||
}
|
||||
|
||||
|
||||
// Image thumbnails
|
||||
.img-thumbnail {
|
||||
padding: $thumbnail-padding;
|
||||
background-color: $thumbnail-bg;
|
||||
border: $thumbnail-border-width solid $thumbnail-border-color;
|
||||
@include border-radius($thumbnail-border-radius);
|
||||
@include box-shadow($thumbnail-box-shadow);
|
||||
|
||||
// Keep them at most 100% wide
|
||||
@include img-fluid;
|
||||
}
|
||||
|
||||
//
|
||||
// Figures
|
||||
//
|
||||
|
||||
.figure {
|
||||
// Ensures the caption's text aligns with the image.
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.figure-img {
|
||||
margin-bottom: $spacer / 2;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.figure-caption {
|
||||
font-size: $figure-caption-font-size;
|
||||
color: $figure-caption-color;
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
// stylelint-disable selector-no-qualifying-type
|
||||
|
||||
//
|
||||
// Base styles
|
||||
//
|
||||
|
||||
.input-group {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap; // For form validation feedback
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
|
||||
> .form-control,
|
||||
> .form-control-plaintext,
|
||||
> .custom-select,
|
||||
> .custom-file {
|
||||
position: relative; // For focus state's z-index
|
||||
flex: 1 1 auto;
|
||||
// Add width 1% and flex-basis auto to ensure that button will not wrap out
|
||||
// the column. Applies to IE Edge+ and Firefox. Chrome does not require this.
|
||||
width: 1%;
|
||||
margin-bottom: 0;
|
||||
|
||||
+ .form-control,
|
||||
+ .custom-select,
|
||||
+ .custom-file {
|
||||
margin-left: -$input-border-width;
|
||||
}
|
||||
}
|
||||
|
||||
// Bring the "active" form control to the top of surrounding elements
|
||||
> .form-control:focus,
|
||||
> .custom-select:focus,
|
||||
> .custom-file .custom-file-input:focus ~ .custom-file-label {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
// Bring the custom file input above the label
|
||||
> .custom-file .custom-file-input:focus {
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
> .form-control,
|
||||
> .custom-select {
|
||||
&:not(:last-child) { @include border-right-radius(0); }
|
||||
&:not(:first-child) { @include border-left-radius(0); }
|
||||
}
|
||||
|
||||
// Custom file inputs have more complex markup, thus requiring different
|
||||
// border-radius overrides.
|
||||
> .custom-file {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:not(:last-child) .custom-file-label,
|
||||
&:not(:last-child) .custom-file-label::after { @include border-right-radius(0); }
|
||||
&:not(:first-child) .custom-file-label { @include border-left-radius(0); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Prepend and append
|
||||
//
|
||||
// While it requires one extra layer of HTML for each, dedicated prepend and
|
||||
// append elements allow us to 1) be less clever, 2) simplify our selectors, and
|
||||
// 3) support HTML5 form validation.
|
||||
|
||||
.input-group-prepend,
|
||||
.input-group-append {
|
||||
display: flex;
|
||||
|
||||
// Ensure buttons are always above inputs for more visually pleasing borders.
|
||||
// This isn't needed for `.input-group-text` since it shares the same border-color
|
||||
// as our inputs.
|
||||
.btn {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
&:focus {
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
|
||||
.btn + .btn,
|
||||
.btn + .input-group-text,
|
||||
.input-group-text + .input-group-text,
|
||||
.input-group-text + .btn {
|
||||
margin-left: -$input-border-width;
|
||||
}
|
||||
}
|
||||
|
||||
.input-group-prepend { margin-right: -$input-border-width; }
|
||||
.input-group-append { margin-left: -$input-border-width; }
|
||||
|
||||
|
||||
// Textual addons
|
||||
//
|
||||
// Serves as a catch-all element for any text or radio/checkbox input you wish
|
||||
// to prepend or append to an input.
|
||||
|
||||
.input-group-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: $input-padding-y $input-padding-x;
|
||||
margin-bottom: 0; // Allow use of <label> elements by overriding our default margin-bottom
|
||||
font-size: $font-size-base; // Match inputs
|
||||
font-weight: $font-weight-normal;
|
||||
line-height: $input-line-height;
|
||||
color: $input-group-addon-color;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
background-color: $input-group-addon-bg;
|
||||
border: $input-border-width solid $input-group-addon-border-color;
|
||||
@include border-radius($input-border-radius);
|
||||
|
||||
// Nuke default margins from checkboxes and radios to vertically center within.
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sizing
|
||||
//
|
||||
// Remix the default form control sizing classes into new ones for easier
|
||||
// manipulation.
|
||||
|
||||
.input-group-lg > .form-control:not(textarea),
|
||||
.input-group-lg > .custom-select {
|
||||
height: $input-height-lg;
|
||||
}
|
||||
|
||||
.input-group-lg > .form-control,
|
||||
.input-group-lg > .custom-select,
|
||||
.input-group-lg > .input-group-prepend > .input-group-text,
|
||||
.input-group-lg > .input-group-append > .input-group-text,
|
||||
.input-group-lg > .input-group-prepend > .btn,
|
||||
.input-group-lg > .input-group-append > .btn {
|
||||
padding: $input-padding-y-lg $input-padding-x-lg;
|
||||
font-size: $input-font-size-lg;
|
||||
line-height: $input-line-height-lg;
|
||||
@include border-radius($input-border-radius-lg);
|
||||
}
|
||||
|
||||
.input-group-sm > .form-control:not(textarea),
|
||||
.input-group-sm > .custom-select {
|
||||
height: $input-height-sm;
|
||||
}
|
||||
|
||||
.input-group-sm > .form-control,
|
||||
.input-group-sm > .custom-select,
|
||||
.input-group-sm > .input-group-prepend > .input-group-text,
|
||||
.input-group-sm > .input-group-append > .input-group-text,
|
||||
.input-group-sm > .input-group-prepend > .btn,
|
||||
.input-group-sm > .input-group-append > .btn {
|
||||
padding: $input-padding-y-sm $input-padding-x-sm;
|
||||
font-size: $input-font-size-sm;
|
||||
line-height: $input-line-height-sm;
|
||||
@include border-radius($input-border-radius-sm);
|
||||
}
|
||||
|
||||
.input-group-lg > .custom-select,
|
||||
.input-group-sm > .custom-select {
|
||||
padding-right: $custom-select-padding-x + $custom-select-indicator-padding;
|
||||
}
|
||||
|
||||
|
||||
// Prepend and append rounded corners
|
||||
//
|
||||
// These rulesets must come after the sizing ones to properly override sm and lg
|
||||
// border-radius values when extending. They're more specific than we'd like
|
||||
// with the `.input-group >` part, but without it, we cannot override the sizing.
|
||||
|
||||
|
||||
.input-group > .input-group-prepend > .btn,
|
||||
.input-group > .input-group-prepend > .input-group-text,
|
||||
.input-group > .input-group-append:not(:last-child) > .btn,
|
||||
.input-group > .input-group-append:not(:last-child) > .input-group-text,
|
||||
.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),
|
||||
.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {
|
||||
@include border-right-radius(0);
|
||||
}
|
||||
|
||||
.input-group > .input-group-append > .btn,
|
||||
.input-group > .input-group-append > .input-group-text,
|
||||
.input-group > .input-group-prepend:not(:first-child) > .btn,
|
||||
.input-group > .input-group-prepend:not(:first-child) > .input-group-text,
|
||||
.input-group > .input-group-prepend:first-child > .btn:not(:first-child),
|
||||
.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {
|
||||
@include border-left-radius(0);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
.jumbotron {
|
||||
padding: $jumbotron-padding ($jumbotron-padding / 2);
|
||||
margin-bottom: $jumbotron-padding;
|
||||
background-color: $jumbotron-bg;
|
||||
@include border-radius($border-radius-lg);
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding: ($jumbotron-padding * 2) $jumbotron-padding;
|
||||
}
|
||||
}
|
||||
|
||||
.jumbotron-fluid {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
@include border-radius(0);
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// Base class
|
||||
//
|
||||
// Easily usable on <ul>, <ol>, or <div>.
|
||||
|
||||
.list-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
// No need to set list-style: none; since .list-group-item is block level
|
||||
padding-left: 0; // reset padding because ul and ol
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
||||
// Interactive list items
|
||||
//
|
||||
// Use anchor or button elements instead of `li`s or `div`s to create interactive
|
||||
// list items. Includes an extra `.active` modifier class for selected items.
|
||||
|
||||
.list-group-item-action {
|
||||
width: 100%; // For `<button>`s (anchors become 100% by default though)
|
||||
color: $list-group-action-color;
|
||||
text-align: inherit; // For `<button>`s (anchors inherit)
|
||||
|
||||
// Hover state
|
||||
@include hover-focus {
|
||||
color: $list-group-action-hover-color;
|
||||
text-decoration: none;
|
||||
background-color: $list-group-hover-bg;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: $list-group-action-active-color;
|
||||
background-color: $list-group-action-active-bg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Individual list items
|
||||
//
|
||||
// Use on `li`s or `div`s within the `.list-group` parent.
|
||||
|
||||
.list-group-item {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: $list-group-item-padding-y $list-group-item-padding-x;
|
||||
// Place the border on the list items and negative margin up for better styling
|
||||
margin-bottom: -$list-group-border-width;
|
||||
background-color: $list-group-bg;
|
||||
border: $list-group-border-width solid $list-group-border-color;
|
||||
|
||||
&:first-child {
|
||||
@include border-top-radius($list-group-border-radius);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
@include border-bottom-radius($list-group-border-radius);
|
||||
}
|
||||
|
||||
@include hover-focus {
|
||||
z-index: 1; // Place hover/active items above their siblings for proper border styling
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.disabled,
|
||||
&:disabled {
|
||||
color: $list-group-disabled-color;
|
||||
pointer-events: none;
|
||||
background-color: $list-group-disabled-bg;
|
||||
}
|
||||
|
||||
// Include both here for `<a>`s and `<button>`s
|
||||
&.active {
|
||||
z-index: 2; // Place active items above their siblings for proper border styling
|
||||
color: $list-group-active-color;
|
||||
background-color: $list-group-active-bg;
|
||||
border-color: $list-group-active-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Flush list items
|
||||
//
|
||||
// Remove borders and border-radius to keep list group items edge-to-edge. Most
|
||||
// useful within other components (e.g., cards).
|
||||
|
||||
.list-group-flush {
|
||||
.list-group-item {
|
||||
border-right: 0;
|
||||
border-left: 0;
|
||||
@include border-radius(0);
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: -$list-group-border-width;
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
.list-group-item:first-child {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.list-group-item:last-child {
|
||||
margin-bottom: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Contextual variants
|
||||
//
|
||||
// Add modifier classes to change text and background color on individual items.
|
||||
// Organizationally, this must come after the `:hover` states.
|
||||
|
||||
@each $color, $value in $theme-colors {
|
||||
@include list-group-item-variant($color, theme-color-level($color, -9), theme-color-level($color, 6));
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
.media {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.media-body {
|
||||
flex: 1;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Toggles
|
||||
//
|
||||
// Used in conjunction with global variables to enable certain theme features.
|
||||
|
||||
// Utilities
|
||||
@import "mixins/breakpoints";
|
||||
@import "mixins/hover";
|
||||
@import "mixins/image";
|
||||
@import "mixins/badge";
|
||||
@import "mixins/resize";
|
||||
@import "mixins/screen-reader";
|
||||
@import "mixins/size";
|
||||
@import "mixins/reset-text";
|
||||
@import "mixins/text-emphasis";
|
||||
@import "mixins/text-hide";
|
||||
@import "mixins/text-truncate";
|
||||
@import "mixins/visibility";
|
||||
|
||||
// // Components
|
||||
@import "mixins/alert";
|
||||
@import "mixins/buttons";
|
||||
@import "mixins/caret";
|
||||
@import "mixins/pagination";
|
||||
@import "mixins/lists";
|
||||
@import "mixins/list-group";
|
||||
@import "mixins/nav-divider";
|
||||
@import "mixins/forms";
|
||||
@import "mixins/table-row";
|
||||
|
||||
// // Skins
|
||||
@import "mixins/background-variant";
|
||||
@import "mixins/border-radius";
|
||||
@import "mixins/box-shadow";
|
||||
@import "mixins/gradients";
|
||||
@import "mixins/transition";
|
||||
|
||||
// // Layout
|
||||
@import "mixins/clearfix";
|
||||
@import "mixins/grid-framework";
|
||||
@import "mixins/grid";
|
||||
@import "mixins/float";
|
||||
@@ -0,0 +1,186 @@
|
||||
// .modal-open - body class for killing the scroll
|
||||
// .modal - container to scroll within
|
||||
// .modal-dialog - positioning shell for the actual modal
|
||||
// .modal-content - actual modal w/ bg and corners and stuff
|
||||
|
||||
|
||||
.modal-open {
|
||||
// Kill the scroll on the body
|
||||
overflow: hidden;
|
||||
|
||||
.modal {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Container that the modal scrolls within
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: $zindex-modal;
|
||||
display: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
// Prevent Chrome on Windows from adding a focus outline. For details, see
|
||||
// https://github.com/twbs/bootstrap/pull/10951.
|
||||
outline: 0;
|
||||
// We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a
|
||||
// gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342
|
||||
// See also https://github.com/twbs/bootstrap/issues/17695
|
||||
}
|
||||
|
||||
// Shell div to position the modal with bottom padding
|
||||
.modal-dialog {
|
||||
position: relative;
|
||||
width: auto;
|
||||
margin: $modal-dialog-margin;
|
||||
// allow clicks to pass through for custom click handling to close modal
|
||||
pointer-events: none;
|
||||
|
||||
// When fading in the modal, animate it to slide down
|
||||
.modal.fade & {
|
||||
@include transition($modal-transition);
|
||||
transform: $modal-fade-transform;
|
||||
}
|
||||
.modal.show & {
|
||||
transform: $modal-show-transform;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-dialog-centered {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: calc(100% - (#{$modal-dialog-margin} * 2));
|
||||
|
||||
// Ensure `modal-dialog-centered` extends the full height of the view (IE10/11)
|
||||
&::before {
|
||||
display: block; // IE10
|
||||
height: calc(100vh - (#{$modal-dialog-margin} * 2));
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
|
||||
// Actual modal
|
||||
.modal-content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%; // Ensure `.modal-content` extends the full width of the parent `.modal-dialog`
|
||||
// counteract the pointer-events: none; in the .modal-dialog
|
||||
pointer-events: auto;
|
||||
background-color: $modal-content-bg;
|
||||
background-clip: padding-box;
|
||||
border: $modal-content-border-width solid $modal-content-border-color;
|
||||
@include border-radius($modal-content-border-radius);
|
||||
@include box-shadow($modal-content-box-shadow-xs);
|
||||
// Remove focus outline from opened modal
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
// Modal background
|
||||
.modal-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: $zindex-modal-backdrop;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: $modal-backdrop-bg;
|
||||
|
||||
// Fade for backdrop
|
||||
&.fade { opacity: 0; }
|
||||
&.show { opacity: $modal-backdrop-opacity; }
|
||||
}
|
||||
|
||||
// Modal header
|
||||
// Top section of the modal w/ title and dismiss
|
||||
.modal-header {
|
||||
display: flex;
|
||||
align-items: flex-start; // so the close btn always stays on the upper right corner
|
||||
justify-content: space-between; // Put modal header elements (title and dismiss) on opposite ends
|
||||
padding: $modal-header-padding;
|
||||
border-bottom: $modal-header-border-width solid $modal-header-border-color;
|
||||
@include border-top-radius($modal-content-border-radius);
|
||||
|
||||
.close {
|
||||
padding: $modal-header-padding;
|
||||
// auto on the left force icon to the right even when there is no .modal-title
|
||||
margin: (-$modal-header-padding-y) (-$modal-header-padding-x) (-$modal-header-padding-y) auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Title text within header
|
||||
.modal-title {
|
||||
margin-bottom: 0;
|
||||
line-height: $modal-title-line-height;
|
||||
}
|
||||
|
||||
// Modal body
|
||||
// Where all modal content resides (sibling of .modal-header and .modal-footer)
|
||||
.modal-body {
|
||||
position: relative;
|
||||
// Enable `flex-grow: 1` so that the body take up as much space as possible
|
||||
// when should there be a fixed height on `.modal-dialog`.
|
||||
flex: 1 1 auto;
|
||||
padding: $modal-inner-padding;
|
||||
}
|
||||
|
||||
// Footer (for actions)
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
align-items: center; // vertically center
|
||||
justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items
|
||||
padding: $modal-inner-padding;
|
||||
border-top: $modal-footer-border-width solid $modal-footer-border-color;
|
||||
@include border-bottom-radius($modal-content-border-radius);
|
||||
|
||||
// Easily place margin between footer elements
|
||||
> :not(:first-child) { margin-left: .25rem; }
|
||||
> :not(:last-child) { margin-right: .25rem; }
|
||||
}
|
||||
|
||||
// Measure scrollbar width for padding body during modal show/hide
|
||||
.modal-scrollbar-measure {
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
// Scale up the modal
|
||||
@include media-breakpoint-up(sm) {
|
||||
// Automatically set modal's width for larger viewports
|
||||
.modal-dialog {
|
||||
max-width: $modal-md;
|
||||
margin: $modal-dialog-margin-y-sm-up auto;
|
||||
}
|
||||
|
||||
.modal-dialog-centered {
|
||||
min-height: calc(100% - (#{$modal-dialog-margin-y-sm-up} * 2));
|
||||
|
||||
&::before {
|
||||
height: calc(100vh - (#{$modal-dialog-margin-y-sm-up} * 2));
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
@include box-shadow($modal-content-box-shadow-sm-up);
|
||||
}
|
||||
|
||||
.modal-sm { max-width: $modal-sm; }
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
.modal-lg,
|
||||
.modal-xl {
|
||||
max-width: $modal-lg;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xl) {
|
||||
.modal-xl { max-width: $modal-xl; }
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// Base class
|
||||
//
|
||||
// Kickstart any navigation component with a set of style resets. Works with
|
||||
// `<nav>`s or `<ul>`s.
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: block;
|
||||
padding: $nav-link-padding-y $nav-link-padding-x;
|
||||
|
||||
@include hover-focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
// Disabled state lightens text
|
||||
&.disabled {
|
||||
color: $nav-link-disabled-color;
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Tabs
|
||||
//
|
||||
|
||||
.nav-tabs {
|
||||
border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color;
|
||||
|
||||
.nav-item {
|
||||
margin-bottom: -$nav-tabs-border-width;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
border: $nav-tabs-border-width solid transparent;
|
||||
@include border-top-radius($nav-tabs-border-radius);
|
||||
|
||||
@include hover-focus {
|
||||
border-color: $nav-tabs-link-hover-border-color;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
color: $nav-link-disabled-color;
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-link.active,
|
||||
.nav-item.show .nav-link {
|
||||
color: $nav-tabs-link-active-color;
|
||||
background-color: $nav-tabs-link-active-bg;
|
||||
border-color: $nav-tabs-link-active-border-color;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
// Make dropdown border overlap tab border
|
||||
margin-top: -$nav-tabs-border-width;
|
||||
// Remove the top rounded corners here since there is a hard edge above the menu
|
||||
@include border-top-radius(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Pills
|
||||
//
|
||||
|
||||
.nav-pills {
|
||||
.nav-link {
|
||||
@include border-radius($nav-pills-border-radius);
|
||||
}
|
||||
|
||||
.nav-link.active,
|
||||
.show > .nav-link {
|
||||
color: $nav-pills-link-active-color;
|
||||
background-color: $nav-pills-link-active-bg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Justified variants
|
||||
//
|
||||
|
||||
.nav-fill {
|
||||
.nav-item {
|
||||
flex: 1 1 auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-justified {
|
||||
.nav-item {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Tabbable tabs
|
||||
//
|
||||
// Hide tabbable panes to start, show them when `.active`
|
||||
|
||||
.tab-content {
|
||||
> .tab-pane {
|
||||
display: none;
|
||||
outline: none !important;
|
||||
}
|
||||
> .active {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
// Contents
|
||||
//
|
||||
// Navbar
|
||||
// Navbar brand
|
||||
// Navbar nav
|
||||
// Navbar text
|
||||
// Navbar divider
|
||||
// Responsive navbar
|
||||
// Navbar position
|
||||
// Navbar themes
|
||||
|
||||
|
||||
// Navbar
|
||||
//
|
||||
// Provide a static navbar from which we expand to create full-width, fixed, and
|
||||
// other navbar variations.
|
||||
|
||||
.navbar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap; // allow us to do the line break for collapsing content
|
||||
align-items: center;
|
||||
justify-content: space-between; // space out brand from logo
|
||||
padding: $navbar-padding-y $navbar-padding-x;
|
||||
|
||||
// Because flex properties aren't inherited, we need to redeclare these first
|
||||
// few properties so that content nested within behave properly.
|
||||
> .container,
|
||||
> .container-fluid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Navbar brand
|
||||
//
|
||||
// Used for brand, project, or site names.
|
||||
|
||||
.navbar-brand {
|
||||
display: inline-block;
|
||||
padding-top: $navbar-brand-padding-y;
|
||||
padding-bottom: $navbar-brand-padding-y;
|
||||
margin-right: $navbar-padding-x;
|
||||
font-size: $navbar-brand-font-size;
|
||||
line-height: inherit;
|
||||
white-space: nowrap;
|
||||
|
||||
@include hover-focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Navbar nav
|
||||
//
|
||||
// Custom navbar navigation (doesn't require `.nav`, but does make use of `.nav-link`).
|
||||
|
||||
.navbar-nav {
|
||||
display: flex;
|
||||
flex-direction: column; // cannot use `inherit` to get the `.navbar`s value
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none;
|
||||
|
||||
.nav-link {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: static;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Navbar text
|
||||
//
|
||||
//
|
||||
|
||||
.navbar-text {
|
||||
display: inline-block;
|
||||
padding-top: $nav-link-padding-y;
|
||||
padding-bottom: $nav-link-padding-y;
|
||||
}
|
||||
|
||||
|
||||
// Responsive navbar
|
||||
//
|
||||
// Custom styles for responsive collapsing and toggling of navbar contents.
|
||||
// Powered by the collapse Bootstrap JavaScript plugin.
|
||||
|
||||
// When collapsed, prevent the toggleable navbar contents from appearing in
|
||||
// the default flexbox row orientation. Requires the use of `flex-wrap: wrap`
|
||||
// on the `.navbar` parent.
|
||||
.navbar-collapse {
|
||||
flex-basis: 100%;
|
||||
flex-grow: 1;
|
||||
// For always expanded or extra full navbars, ensure content aligns itself
|
||||
// properly vertically. Can be easily overridden with flex utilities.
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// Button for toggling the navbar when in its collapsed state
|
||||
.navbar-toggler {
|
||||
padding: $navbar-toggler-padding-y $navbar-toggler-padding-x;
|
||||
font-size: $navbar-toggler-font-size;
|
||||
line-height: 1;
|
||||
background-color: transparent; // remove default button style
|
||||
border: $border-width solid transparent; // remove default button style
|
||||
@include border-radius($navbar-toggler-border-radius);
|
||||
|
||||
@include hover-focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
// Opinionated: add "hand" cursor to non-disabled .navbar-toggler elements
|
||||
&:not(:disabled):not(.disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep as a separate element so folks can easily override it with another icon
|
||||
// or image file as needed.
|
||||
.navbar-toggler-icon {
|
||||
display: inline-block;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
vertical-align: middle;
|
||||
content: "";
|
||||
background: no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
// Generate series of `.navbar-expand-*` responsive classes for configuring
|
||||
// where your navbar collapses.
|
||||
.navbar-expand {
|
||||
@each $breakpoint in map-keys($grid-breakpoints) {
|
||||
$next: breakpoint-next($breakpoint, $grid-breakpoints);
|
||||
$infix: breakpoint-infix($next, $grid-breakpoints);
|
||||
|
||||
&#{$infix} {
|
||||
@include media-breakpoint-down($breakpoint) {
|
||||
> .container,
|
||||
> .container-fluid {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up($next) {
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start;
|
||||
|
||||
.navbar-nav {
|
||||
flex-direction: row;
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
padding-right: $navbar-nav-link-padding-x;
|
||||
padding-left: $navbar-nav-link-padding-x;
|
||||
}
|
||||
}
|
||||
|
||||
// For nesting containers, have to redeclare for alignment purposes
|
||||
> .container,
|
||||
> .container-fluid {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
display: flex !important; // stylelint-disable-line declaration-no-important
|
||||
|
||||
// Changes flex-bases to auto because of an IE10 bug
|
||||
flex-basis: auto;
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Navbar themes
|
||||
//
|
||||
// Styles for switching between navbars with light or dark background.
|
||||
|
||||
// Dark links against a light background
|
||||
.navbar-light {
|
||||
.navbar-brand {
|
||||
color: $navbar-light-brand-color;
|
||||
|
||||
@include hover-focus {
|
||||
color: $navbar-light-brand-hover-color;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
.nav-link {
|
||||
color: $navbar-light-color;
|
||||
|
||||
@include hover-focus {
|
||||
color: $navbar-light-hover-color;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
color: $navbar-light-disabled-color;
|
||||
}
|
||||
}
|
||||
|
||||
.show > .nav-link,
|
||||
.active > .nav-link,
|
||||
.nav-link.show,
|
||||
.nav-link.active {
|
||||
color: $navbar-light-active-color;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
color: $navbar-light-color;
|
||||
border-color: $navbar-light-toggler-border-color;
|
||||
}
|
||||
|
||||
.navbar-toggler-icon {
|
||||
background-image: $navbar-light-toggler-icon-bg;
|
||||
}
|
||||
|
||||
.navbar-text {
|
||||
color: $navbar-light-color;
|
||||
a {
|
||||
color: $navbar-light-active-color;
|
||||
|
||||
@include hover-focus {
|
||||
color: $navbar-light-active-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// White links against a dark background
|
||||
.navbar-dark {
|
||||
.navbar-brand {
|
||||
color: $navbar-dark-brand-color;
|
||||
|
||||
@include hover-focus {
|
||||
color: $navbar-dark-brand-hover-color;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
.nav-link {
|
||||
color: $navbar-dark-color;
|
||||
|
||||
@include hover-focus {
|
||||
color: $navbar-dark-hover-color;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
color: $navbar-dark-disabled-color;
|
||||
}
|
||||
}
|
||||
|
||||
.show > .nav-link,
|
||||
.active > .nav-link,
|
||||
.nav-link.show,
|
||||
.nav-link.active {
|
||||
color: $navbar-dark-active-color;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
color: $navbar-dark-color;
|
||||
border-color: $navbar-dark-toggler-border-color;
|
||||
}
|
||||
|
||||
.navbar-toggler-icon {
|
||||
background-image: $navbar-dark-toggler-icon-bg;
|
||||
}
|
||||
|
||||
.navbar-text {
|
||||
color: $navbar-dark-color;
|
||||
a {
|
||||
color: $navbar-dark-active-color;
|
||||
|
||||
@include hover-focus {
|
||||
color: $navbar-dark-active-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
.pagination {
|
||||
display: flex;
|
||||
@include list-unstyled();
|
||||
@include border-radius();
|
||||
}
|
||||
|
||||
.page-link {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: $pagination-padding-y $pagination-padding-x;
|
||||
margin-left: -$pagination-border-width;
|
||||
line-height: $pagination-line-height;
|
||||
color: $pagination-color;
|
||||
background-color: $pagination-bg;
|
||||
border: $pagination-border-width solid $pagination-border-color;
|
||||
|
||||
&:hover {
|
||||
z-index: 2;
|
||||
color: $pagination-hover-color;
|
||||
text-decoration: none;
|
||||
background-color: $pagination-hover-bg;
|
||||
border-color: $pagination-hover-border-color;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
z-index: 2;
|
||||
outline: $pagination-focus-outline;
|
||||
box-shadow: $pagination-focus-box-shadow;
|
||||
}
|
||||
|
||||
// Opinionated: add "hand" cursor to non-disabled .page-link elements
|
||||
&:not(:disabled):not(.disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.page-item {
|
||||
&:first-child {
|
||||
.page-link {
|
||||
margin-left: 0;
|
||||
@include border-left-radius($border-radius);
|
||||
}
|
||||
}
|
||||
&:last-child {
|
||||
.page-link {
|
||||
@include border-right-radius($border-radius);
|
||||
}
|
||||
}
|
||||
|
||||
&.active .page-link {
|
||||
z-index: 1;
|
||||
color: $pagination-active-color;
|
||||
background-color: $pagination-active-bg;
|
||||
border-color: $pagination-active-border-color;
|
||||
}
|
||||
|
||||
&.disabled .page-link {
|
||||
color: $pagination-disabled-color;
|
||||
pointer-events: none;
|
||||
// Opinionated: remove the "hand" cursor set previously for .page-link
|
||||
cursor: auto;
|
||||
background-color: $pagination-disabled-bg;
|
||||
border-color: $pagination-disabled-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Sizing
|
||||
//
|
||||
|
||||
.pagination-lg {
|
||||
@include pagination-size($pagination-padding-y-lg, $pagination-padding-x-lg, $font-size-lg, $line-height-lg, $border-radius-lg);
|
||||
}
|
||||
|
||||
.pagination-sm {
|
||||
@include pagination-size($pagination-padding-y-sm, $pagination-padding-x-sm, $font-size-sm, $line-height-sm, $border-radius-sm);
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
.popover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: $zindex-popover;
|
||||
display: block;
|
||||
max-width: $popover-max-width;
|
||||
// Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
|
||||
// So reset our font and text properties to avoid inheriting weird values.
|
||||
@include reset-text();
|
||||
font-size: $popover-font-size;
|
||||
// Allow breaking very long words so they don't overflow the popover's bounds
|
||||
word-wrap: break-word;
|
||||
background-color: $popover-bg;
|
||||
background-clip: padding-box;
|
||||
border: $popover-border-width solid $popover-border-color;
|
||||
@include border-radius($popover-border-radius);
|
||||
@include box-shadow($popover-box-shadow);
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: $popover-arrow-width;
|
||||
height: $popover-arrow-height;
|
||||
margin: 0 $border-radius-lg;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
content: "";
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bs-popover-top {
|
||||
margin-bottom: $popover-arrow-height;
|
||||
|
||||
.arrow {
|
||||
bottom: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
|
||||
}
|
||||
|
||||
.arrow::before,
|
||||
.arrow::after {
|
||||
border-width: $popover-arrow-height ($popover-arrow-width / 2) 0;
|
||||
}
|
||||
|
||||
.arrow::before {
|
||||
bottom: 0;
|
||||
border-top-color: $popover-arrow-outer-color;
|
||||
}
|
||||
|
||||
.arrow::after {
|
||||
bottom: $popover-border-width;
|
||||
border-top-color: $popover-arrow-color;
|
||||
}
|
||||
}
|
||||
|
||||
.bs-popover-right {
|
||||
margin-left: $popover-arrow-height;
|
||||
|
||||
.arrow {
|
||||
left: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
|
||||
width: $popover-arrow-height;
|
||||
height: $popover-arrow-width;
|
||||
margin: $border-radius-lg 0; // make sure the arrow does not touch the popover's rounded corners
|
||||
}
|
||||
|
||||
.arrow::before,
|
||||
.arrow::after {
|
||||
border-width: ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2) 0;
|
||||
}
|
||||
|
||||
.arrow::before {
|
||||
left: 0;
|
||||
border-right-color: $popover-arrow-outer-color;
|
||||
}
|
||||
|
||||
.arrow::after {
|
||||
left: $popover-border-width;
|
||||
border-right-color: $popover-arrow-color;
|
||||
}
|
||||
}
|
||||
|
||||
.bs-popover-bottom {
|
||||
margin-top: $popover-arrow-height;
|
||||
|
||||
.arrow {
|
||||
top: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
|
||||
}
|
||||
|
||||
.arrow::before,
|
||||
.arrow::after {
|
||||
border-width: 0 ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2);
|
||||
}
|
||||
|
||||
.arrow::before {
|
||||
top: 0;
|
||||
border-bottom-color: $popover-arrow-outer-color;
|
||||
}
|
||||
|
||||
.arrow::after {
|
||||
top: $popover-border-width;
|
||||
border-bottom-color: $popover-arrow-color;
|
||||
}
|
||||
|
||||
// This will remove the popover-header's border just below the arrow
|
||||
.popover-header::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
display: block;
|
||||
width: $popover-arrow-width;
|
||||
margin-left: -$popover-arrow-width / 2;
|
||||
content: "";
|
||||
border-bottom: $popover-border-width solid $popover-header-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.bs-popover-left {
|
||||
margin-right: $popover-arrow-height;
|
||||
|
||||
.arrow {
|
||||
right: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
|
||||
width: $popover-arrow-height;
|
||||
height: $popover-arrow-width;
|
||||
margin: $border-radius-lg 0; // make sure the arrow does not touch the popover's rounded corners
|
||||
}
|
||||
|
||||
.arrow::before,
|
||||
.arrow::after {
|
||||
border-width: ($popover-arrow-width / 2) 0 ($popover-arrow-width / 2) $popover-arrow-height;
|
||||
}
|
||||
|
||||
.arrow::before {
|
||||
right: 0;
|
||||
border-left-color: $popover-arrow-outer-color;
|
||||
}
|
||||
|
||||
.arrow::after {
|
||||
right: $popover-border-width;
|
||||
border-left-color: $popover-arrow-color;
|
||||
}
|
||||
}
|
||||
|
||||
.bs-popover-auto {
|
||||
&[x-placement^="top"] {
|
||||
@extend .bs-popover-top;
|
||||
}
|
||||
&[x-placement^="right"] {
|
||||
@extend .bs-popover-right;
|
||||
}
|
||||
&[x-placement^="bottom"] {
|
||||
@extend .bs-popover-bottom;
|
||||
}
|
||||
&[x-placement^="left"] {
|
||||
@extend .bs-popover-left;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Offset the popover to account for the popover arrow
|
||||
.popover-header {
|
||||
padding: $popover-header-padding-y $popover-header-padding-x;
|
||||
margin-bottom: 0; // Reset the default from Reboot
|
||||
font-size: $font-size-base;
|
||||
color: $popover-header-color;
|
||||
background-color: $popover-header-bg;
|
||||
border-bottom: $popover-border-width solid darken($popover-header-bg, 5%);
|
||||
$offset-border-width: calc(#{$border-radius-lg} - #{$popover-border-width});
|
||||
@include border-top-radius($offset-border-width);
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.popover-body {
|
||||
padding: $popover-body-padding-y $popover-body-padding-x;
|
||||
color: $popover-body-color;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
// stylelint-disable declaration-no-important, selector-no-qualifying-type
|
||||
|
||||
// Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css
|
||||
|
||||
// ==========================================================================
|
||||
// Print styles.
|
||||
// Inlined to avoid the additional HTTP request:
|
||||
// https://www.phpied.com/delay-loading-your-print-css/
|
||||
// ==========================================================================
|
||||
|
||||
@if $enable-print-styles {
|
||||
@media print {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
// Bootstrap specific; comment out `color` and `background`
|
||||
//color: $black !important; // Black prints faster
|
||||
text-shadow: none !important;
|
||||
//background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
a {
|
||||
&:not(.btn) {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
// Bootstrap specific; comment the following selector out
|
||||
//a[href]::after {
|
||||
// content: " (" attr(href) ")";
|
||||
//}
|
||||
|
||||
abbr[title]::after {
|
||||
content: " (" attr(title) ")";
|
||||
}
|
||||
|
||||
// Bootstrap specific; comment the following selector out
|
||||
//
|
||||
// Don't show links that are fragment identifiers,
|
||||
// or use the `javascript:` pseudo protocol
|
||||
//
|
||||
|
||||
//a[href^="#"]::after,
|
||||
//a[href^="javascript:"]::after {
|
||||
// content: "";
|
||||
//}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
pre,
|
||||
blockquote {
|
||||
border: $border-width solid $gray-500; // Bootstrap custom code; using `$border-width` instead of 1px
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
//
|
||||
// Printing Tables:
|
||||
// http://css-discuss.incutio.com/wiki/Printing_Tables
|
||||
//
|
||||
|
||||
thead {
|
||||
display: table-header-group;
|
||||
}
|
||||
|
||||
tr,
|
||||
img {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
p,
|
||||
h2,
|
||||
h3 {
|
||||
orphans: 3;
|
||||
widows: 3;
|
||||
}
|
||||
|
||||
h2,
|
||||
h3 {
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
// Bootstrap specific changes start
|
||||
|
||||
// Specify a size and min-width to make printing closer across browsers.
|
||||
// We don't set margin here because it breaks `size` in Chrome. We also
|
||||
// don't use `!important` on `size` as it breaks in Chrome.
|
||||
@page {
|
||||
size: $print-page-size;
|
||||
}
|
||||
body {
|
||||
min-width: $print-body-min-width !important;
|
||||
}
|
||||
.container {
|
||||
min-width: $print-body-min-width !important;
|
||||
}
|
||||
|
||||
// Bootstrap components
|
||||
.navbar {
|
||||
display: none;
|
||||
}
|
||||
.badge {
|
||||
border: $border-width solid $black;
|
||||
}
|
||||
|
||||
.table {
|
||||
border-collapse: collapse !important;
|
||||
|
||||
td,
|
||||
th {
|
||||
background-color: $white !important;
|
||||
}
|
||||
}
|
||||
|
||||
.table-bordered {
|
||||
th,
|
||||
td {
|
||||
border: 1px solid $gray-300 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.table-dark {
|
||||
color: inherit;
|
||||
|
||||
th,
|
||||
td,
|
||||
thead th,
|
||||
tbody + tbody {
|
||||
border-color: $table-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
.table .thead-dark th {
|
||||
color: inherit;
|
||||
border-color: $table-border-color;
|
||||
}
|
||||
|
||||
// Bootstrap specific changes end
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
@keyframes progress-bar-stripes {
|
||||
from { background-position: $progress-height 0; }
|
||||
to { background-position: 0 0; }
|
||||
}
|
||||
|
||||
.progress {
|
||||
display: flex;
|
||||
height: $progress-height;
|
||||
overflow: hidden; // force rounded corners by cropping it
|
||||
font-size: $progress-font-size;
|
||||
background-color: $progress-bg;
|
||||
@include border-radius($progress-border-radius);
|
||||
@include box-shadow($progress-box-shadow);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
color: $progress-bar-color;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
background-color: $progress-bar-bg;
|
||||
@include transition($progress-bar-transition);
|
||||
}
|
||||
|
||||
.progress-bar-striped {
|
||||
@include gradient-striped();
|
||||
background-size: $progress-height $progress-height;
|
||||
}
|
||||
|
||||
.progress-bar-animated {
|
||||
animation: progress-bar-stripes $progress-bar-animation-timing;
|
||||
}
|
||||
@@ -0,0 +1,462 @@
|
||||
// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
|
||||
|
||||
// Reboot
|
||||
//
|
||||
// Normalization of HTML elements, manually forked from Normalize.css to remove
|
||||
// styles targeting irrelevant browsers while applying new styles.
|
||||
//
|
||||
// Normalize is licensed MIT. https://github.com/necolas/normalize.css
|
||||
|
||||
|
||||
// Document
|
||||
//
|
||||
// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.
|
||||
// 2. Change the default font family in all browsers.
|
||||
// 3. Correct the line height in all browsers.
|
||||
// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.
|
||||
// 5. Change the default tap highlight to be completely transparent in iOS.
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box; // 1
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif; // 2
|
||||
line-height: 1.15; // 3
|
||||
-webkit-text-size-adjust: 100%; // 4
|
||||
-webkit-tap-highlight-color: rgba($black, 0); // 5
|
||||
}
|
||||
|
||||
// Shim for "new" HTML5 structural elements to display correctly (IE10, older browsers)
|
||||
// TODO: remove in v5
|
||||
// stylelint-disable-next-line selector-list-comma-newline-after
|
||||
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
// Body
|
||||
//
|
||||
// 1. Remove the margin in all browsers.
|
||||
// 2. As a best practice, apply a default `background-color`.
|
||||
// 3. Set an explicit initial text-align value so that we can later use
|
||||
// the `inherit` value on things like `<th>` elements.
|
||||
|
||||
body {
|
||||
margin: 0; // 1
|
||||
font-family: $font-family-base;
|
||||
font-size: $font-size-base;
|
||||
font-weight: $font-weight-base;
|
||||
line-height: $line-height-base;
|
||||
color: $body-color;
|
||||
text-align: left; // 3
|
||||
background-color: $body-bg; // 2
|
||||
}
|
||||
|
||||
// Suppress the focus outline on elements that cannot be accessed via keyboard.
|
||||
// This prevents an unwanted focus outline from appearing around elements that
|
||||
// might still respond to pointer events.
|
||||
//
|
||||
// Credit: https://github.com/suitcss/base
|
||||
[tabindex="-1"]:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
// Content grouping
|
||||
//
|
||||
// 1. Add the correct box sizing in Firefox.
|
||||
// 2. Show the overflow in Edge and IE.
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; // 1
|
||||
height: 0; // 1
|
||||
overflow: visible; // 2
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Typography
|
||||
//
|
||||
|
||||
// Remove top margins from headings
|
||||
//
|
||||
// By default, `<h1>`-`<h6>` all receive top and bottom margins. We nuke the top
|
||||
// margin for easier control within type scales as it avoids margin collapsing.
|
||||
// stylelint-disable-next-line selector-list-comma-newline-after
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: $headings-margin-bottom;
|
||||
}
|
||||
|
||||
// Reset margins on paragraphs
|
||||
//
|
||||
// Similarly, the top margin on `<p>`s get reset. However, we also reset the
|
||||
// bottom margin to use `rem` units instead of `em`.
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: $paragraph-margin-bottom;
|
||||
}
|
||||
|
||||
// Abbreviations
|
||||
//
|
||||
// 1. Duplicate behavior to the data-* attribute for our tooltip plugin
|
||||
// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
// 3. Add explicit cursor to indicate changed behavior.
|
||||
// 4. Remove the bottom border in Firefox 39-.
|
||||
// 5. Prevent the text-decoration to be skipped.
|
||||
|
||||
abbr[title],
|
||||
abbr[data-original-title] { // 1
|
||||
text-decoration: underline; // 2
|
||||
text-decoration: underline dotted; // 2
|
||||
cursor: help; // 3
|
||||
border-bottom: 0; // 4
|
||||
text-decoration-skip-ink: none; // 5
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: $dt-font-weight;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0; // Undo browser default
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: $font-weight-bolder; // Add the correct font weight in Chrome, Edge, and Safari
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%; // Add the correct font size in all browsers
|
||||
}
|
||||
|
||||
//
|
||||
// Prevent `sub` and `sup` elements from affecting the line height in
|
||||
// all browsers.
|
||||
//
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub { bottom: -.25em; }
|
||||
sup { top: -.5em; }
|
||||
|
||||
|
||||
//
|
||||
// Links
|
||||
//
|
||||
|
||||
a {
|
||||
color: $link-color;
|
||||
text-decoration: $link-decoration;
|
||||
background-color: transparent; // Remove the gray background on active links in IE 10.
|
||||
|
||||
@include hover {
|
||||
color: $link-hover-color;
|
||||
text-decoration: $link-hover-decoration;
|
||||
}
|
||||
}
|
||||
|
||||
// And undo these styles for placeholder links/named anchors (without href)
|
||||
// which have not been made explicitly keyboard-focusable (without tabindex).
|
||||
// It would be more straightforward to just use a[href] in previous block, but that
|
||||
// causes specificity issues in many other styles that are too complex to fix.
|
||||
// See https://github.com/twbs/bootstrap/issues/19402
|
||||
|
||||
a:not([href]):not([tabindex]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
|
||||
@include hover-focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Code
|
||||
//
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: $font-family-monospace;
|
||||
font-size: 1em; // Correct the odd `em` font sizing in all browsers.
|
||||
}
|
||||
|
||||
pre {
|
||||
// Remove browser default top margin
|
||||
margin-top: 0;
|
||||
// Reset browser default of `1em` to use `rem`s
|
||||
margin-bottom: 1rem;
|
||||
// Don't allow content to break outside
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Figures
|
||||
//
|
||||
|
||||
figure {
|
||||
// Apply a consistent margin strategy (matches our type styles).
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Images and content
|
||||
//
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none; // Remove the border on images inside links in IE 10-.
|
||||
}
|
||||
|
||||
svg {
|
||||
// Workaround for the SVG overflow bug in IE10/11 is still required.
|
||||
// See https://github.com/twbs/bootstrap/issues/26878
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Tables
|
||||
//
|
||||
|
||||
table {
|
||||
border-collapse: collapse; // Prevent double borders
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: $table-cell-padding;
|
||||
padding-bottom: $table-cell-padding;
|
||||
color: $table-caption-color;
|
||||
text-align: left;
|
||||
caption-side: bottom;
|
||||
}
|
||||
|
||||
th {
|
||||
// Matches default `<td>` alignment by inheriting from the `<body>`, or the
|
||||
// closest parent with a set `text-align`.
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Forms
|
||||
//
|
||||
|
||||
label {
|
||||
// Allow labels to use `margin` for spacing.
|
||||
display: inline-block;
|
||||
margin-bottom: $label-margin-bottom;
|
||||
}
|
||||
|
||||
// Remove the default `border-radius` that macOS Chrome adds.
|
||||
//
|
||||
// Details at https://github.com/twbs/bootstrap/issues/24093
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
// Work around a Firefox/IE bug where the transparent `button` background
|
||||
// results in a loss of the default `button` focus styles.
|
||||
//
|
||||
// Credit: https://github.com/suitcss/base/
|
||||
button:focus {
|
||||
outline: 1px dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0; // Remove the margin in Firefox and Safari
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
overflow: visible; // Show the overflow in Edge
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none; // Remove the inheritance of text transform in Firefox
|
||||
}
|
||||
|
||||
// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||
// controls in Android 4.
|
||||
// 2. Correct the inability to style clickable types in iOS and Safari.
|
||||
button,
|
||||
[type="button"], // 1
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button; // 2
|
||||
}
|
||||
|
||||
// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
box-sizing: border-box; // 1. Add the correct box sizing in IE 10-
|
||||
padding: 0; // 2. Remove the padding in IE 10-
|
||||
}
|
||||
|
||||
|
||||
input[type="date"],
|
||||
input[type="time"],
|
||||
input[type="datetime-local"],
|
||||
input[type="month"] {
|
||||
// Remove the default appearance of temporal inputs to avoid a Mobile Safari
|
||||
// bug where setting a custom line-height prevents text from being vertically
|
||||
// centered within the input.
|
||||
// See https://bugs.webkit.org/show_bug.cgi?id=139848
|
||||
// and https://github.com/twbs/bootstrap/issues/11266
|
||||
-webkit-appearance: listbox;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto; // Remove the default vertical scrollbar in IE.
|
||||
// Textareas should really only resize vertically so they don't break their (horizontal) containers.
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
// Browsers set a default `min-width: min-content;` on fieldsets,
|
||||
// unlike e.g. `<div>`s, which have `min-width: 0;` by default.
|
||||
// So we reset that to ensure fieldsets behave more like a standard block element.
|
||||
// See https://github.com/twbs/bootstrap/issues/12359
|
||||
// and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements
|
||||
min-width: 0;
|
||||
// Reset the default outline behavior of fieldsets so they don't affect page layout.
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
// 1. Correct the text wrapping in Edge and IE.
|
||||
// 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
legend {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%; // 1
|
||||
padding: 0;
|
||||
margin-bottom: .5rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: inherit;
|
||||
color: inherit; // 2
|
||||
white-space: normal; // 1
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
}
|
||||
|
||||
// Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type="search"] {
|
||||
// This overrides the extra rounded corners on search inputs in iOS so that our
|
||||
// `.form-control` class can properly style them. Note that this cannot simply
|
||||
// be added to `.form-control` as it's not specific enough. For details, see
|
||||
// https://github.com/twbs/bootstrap/issues/11586.
|
||||
outline-offset: -2px; // 2. Correct the outline style in Safari.
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
//
|
||||
// Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
|
||||
//
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
//
|
||||
// 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
// 2. Change font properties to `inherit` in Safari.
|
||||
//
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit; // 2
|
||||
-webkit-appearance: button; // 1
|
||||
}
|
||||
|
||||
//
|
||||
// Correct element displays
|
||||
//
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item; // Add the correct display in all browsers
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none; // Add the correct display in IE
|
||||
}
|
||||
|
||||
// Always hide an element with the `hidden` HTML attribute (from PureCSS).
|
||||
// Needed for proper display in IE 10-.
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
:root {
|
||||
// Custom variable values only support SassScript inside `#{}`.
|
||||
@each $color, $value in $colors {
|
||||
--#{$color}: #{$value};
|
||||
}
|
||||
|
||||
@each $color, $value in $theme-colors {
|
||||
--#{$color}: #{$value};
|
||||
}
|
||||
|
||||
@each $bp, $value in $grid-breakpoints {
|
||||
--breakpoint-#{$bp}: #{$value};
|
||||
}
|
||||
|
||||
// Use `inspect` for lists so that quoted items keep the quotes.
|
||||
// See https://github.com/sass/sass/issues/2383#issuecomment-336349172
|
||||
--font-family-sans-serif: #{inspect($font-family-sans-serif)};
|
||||
--font-family-monospace: #{inspect($font-family-monospace)};
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// Rotating border
|
||||
//
|
||||
|
||||
@keyframes spinner-border {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.spinner-border {
|
||||
display: inline-block;
|
||||
width: $spinner-width;
|
||||
height: $spinner-height;
|
||||
vertical-align: text-bottom;
|
||||
border: $spinner-border-width solid currentColor;
|
||||
border-right-color: transparent;
|
||||
border-radius: 50%;
|
||||
animation: spinner-border .75s linear infinite;
|
||||
}
|
||||
|
||||
.spinner-border-sm {
|
||||
width: $spinner-width-sm;
|
||||
height: $spinner-height-sm;
|
||||
border-width: $spinner-border-width-sm;
|
||||
}
|
||||
|
||||
//
|
||||
// Growing circle
|
||||
//
|
||||
|
||||
@keyframes spinner-grow {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.spinner-grow {
|
||||
display: inline-block;
|
||||
width: $spinner-width;
|
||||
height: $spinner-height;
|
||||
vertical-align: text-bottom;
|
||||
background-color: currentColor;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
animation: spinner-grow .75s linear infinite;
|
||||
}
|
||||
|
||||
.spinner-grow-sm {
|
||||
width: $spinner-width-sm;
|
||||
height: $spinner-height-sm;
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
//
|
||||
// Basic Bootstrap table
|
||||
//
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
margin-bottom: $spacer;
|
||||
background-color: $table-bg; // Reset for nesting within parents with `background-color`.
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: $table-cell-padding;
|
||||
vertical-align: top;
|
||||
border-top: $table-border-width solid $table-border-color;
|
||||
}
|
||||
|
||||
thead th {
|
||||
vertical-align: bottom;
|
||||
border-bottom: (2 * $table-border-width) solid $table-border-color;
|
||||
}
|
||||
|
||||
tbody + tbody {
|
||||
border-top: (2 * $table-border-width) solid $table-border-color;
|
||||
}
|
||||
|
||||
.table {
|
||||
background-color: $body-bg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Condensed table w/ half padding
|
||||
//
|
||||
|
||||
.table-sm {
|
||||
th,
|
||||
td {
|
||||
padding: $table-cell-padding-sm;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Border versions
|
||||
//
|
||||
// Add or remove borders all around the table and between all the columns.
|
||||
|
||||
.table-bordered {
|
||||
border: $table-border-width solid $table-border-color;
|
||||
|
||||
th,
|
||||
td {
|
||||
border: $table-border-width solid $table-border-color;
|
||||
}
|
||||
|
||||
thead {
|
||||
th,
|
||||
td {
|
||||
border-bottom-width: 2 * $table-border-width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-borderless {
|
||||
th,
|
||||
td,
|
||||
thead th,
|
||||
tbody + tbody {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Zebra-striping
|
||||
//
|
||||
// Default zebra-stripe styles (alternating gray and transparent backgrounds)
|
||||
|
||||
.table-striped {
|
||||
tbody tr:nth-of-type(#{$table-striped-order}) {
|
||||
background-color: $table-accent-bg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Hover effect
|
||||
//
|
||||
// Placed here since it has to come after the potential zebra striping
|
||||
|
||||
.table-hover {
|
||||
tbody tr {
|
||||
@include hover {
|
||||
background-color: $table-hover-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Table backgrounds
|
||||
//
|
||||
// Exact selectors below required to override `.table-striped` and prevent
|
||||
// inheritance to nested tables.
|
||||
|
||||
@each $color, $value in $theme-colors {
|
||||
@include table-row-variant($color, theme-color-level($color, $table-bg-level), theme-color-level($color, $table-border-level));
|
||||
}
|
||||
|
||||
@include table-row-variant(active, $table-active-bg);
|
||||
|
||||
|
||||
// Dark styles
|
||||
//
|
||||
// Same table markup, but inverted color scheme: dark background and light text.
|
||||
|
||||
// stylelint-disable-next-line no-duplicate-selectors
|
||||
.table {
|
||||
.thead-dark {
|
||||
th {
|
||||
color: $table-dark-color;
|
||||
background-color: $table-dark-bg;
|
||||
border-color: $table-dark-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
.thead-light {
|
||||
th {
|
||||
color: $table-head-color;
|
||||
background-color: $table-head-bg;
|
||||
border-color: $table-border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-dark {
|
||||
color: $table-dark-color;
|
||||
background-color: $table-dark-bg;
|
||||
|
||||
th,
|
||||
td,
|
||||
thead th {
|
||||
border-color: $table-dark-border-color;
|
||||
}
|
||||
|
||||
&.table-bordered {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&.table-striped {
|
||||
tbody tr:nth-of-type(odd) {
|
||||
background-color: $table-dark-accent-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&.table-hover {
|
||||
tbody tr {
|
||||
@include hover {
|
||||
background-color: $table-dark-hover-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Responsive tables
|
||||
//
|
||||
// Generate series of `.table-responsive-*` classes for configuring the screen
|
||||
// size of where your table will overflow.
|
||||
|
||||
.table-responsive {
|
||||
@each $breakpoint in map-keys($grid-breakpoints) {
|
||||
$next: breakpoint-next($breakpoint, $grid-breakpoints);
|
||||
$infix: breakpoint-infix($next, $grid-breakpoints);
|
||||
|
||||
&#{$infix} {
|
||||
@include media-breakpoint-down($breakpoint) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-ms-overflow-style: -ms-autohiding-scrollbar; // See https://github.com/twbs/bootstrap/pull/10057
|
||||
|
||||
// Prevent double border on horizontal scroll due to use of `display: block;`
|
||||
> .table-bordered {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
.toast {
|
||||
max-width: $toast-max-width;
|
||||
overflow: hidden; // cheap rounded corners on nested items
|
||||
font-size: $toast-font-size; // knock it down to 14px
|
||||
background-color: $toast-background-color;
|
||||
background-clip: padding-box;
|
||||
border: $toast-border-width solid $toast-border-color;
|
||||
border-radius: $toast-border-radius;
|
||||
box-shadow: $toast-box-shadow;
|
||||
backdrop-filter: blur(10px);
|
||||
opacity: 0;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: $toast-padding-x;
|
||||
}
|
||||
|
||||
&.showing {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.show {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.hide {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.toast-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: $toast-padding-y $toast-padding-x;
|
||||
color: $toast-header-color;
|
||||
background-color: $toast-header-background-color;
|
||||
background-clip: padding-box;
|
||||
border-bottom: $toast-border-width solid $toast-header-border-color;
|
||||
}
|
||||
|
||||
.toast-body {
|
||||
padding: $toast-padding-x; // apply to both vertical and horizontal
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// Base class
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
z-index: $zindex-tooltip;
|
||||
display: block;
|
||||
margin: $tooltip-margin;
|
||||
// Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
|
||||
// So reset our font and text properties to avoid inheriting weird values.
|
||||
@include reset-text();
|
||||
font-size: $tooltip-font-size;
|
||||
// Allow breaking very long words so they don't overflow the tooltip's bounds
|
||||
word-wrap: break-word;
|
||||
opacity: 0;
|
||||
|
||||
&.show { opacity: $tooltip-opacity; }
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: $tooltip-arrow-width;
|
||||
height: $tooltip-arrow-height;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bs-tooltip-top {
|
||||
padding: $tooltip-arrow-height 0;
|
||||
|
||||
.arrow {
|
||||
bottom: 0;
|
||||
|
||||
&::before {
|
||||
top: 0;
|
||||
border-width: $tooltip-arrow-height ($tooltip-arrow-width / 2) 0;
|
||||
border-top-color: $tooltip-arrow-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bs-tooltip-right {
|
||||
padding: 0 $tooltip-arrow-height;
|
||||
|
||||
.arrow {
|
||||
left: 0;
|
||||
width: $tooltip-arrow-height;
|
||||
height: $tooltip-arrow-width;
|
||||
|
||||
&::before {
|
||||
right: 0;
|
||||
border-width: ($tooltip-arrow-width / 2) $tooltip-arrow-height ($tooltip-arrow-width / 2) 0;
|
||||
border-right-color: $tooltip-arrow-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bs-tooltip-bottom {
|
||||
padding: $tooltip-arrow-height 0;
|
||||
|
||||
.arrow {
|
||||
top: 0;
|
||||
|
||||
&::before {
|
||||
bottom: 0;
|
||||
border-width: 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height;
|
||||
border-bottom-color: $tooltip-arrow-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bs-tooltip-left {
|
||||
padding: 0 $tooltip-arrow-height;
|
||||
|
||||
.arrow {
|
||||
right: 0;
|
||||
width: $tooltip-arrow-height;
|
||||
height: $tooltip-arrow-width;
|
||||
|
||||
&::before {
|
||||
left: 0;
|
||||
border-width: ($tooltip-arrow-width / 2) 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height;
|
||||
border-left-color: $tooltip-arrow-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bs-tooltip-auto {
|
||||
&[x-placement^="top"] {
|
||||
@extend .bs-tooltip-top;
|
||||
}
|
||||
&[x-placement^="right"] {
|
||||
@extend .bs-tooltip-right;
|
||||
}
|
||||
&[x-placement^="bottom"] {
|
||||
@extend .bs-tooltip-bottom;
|
||||
}
|
||||
&[x-placement^="left"] {
|
||||
@extend .bs-tooltip-left;
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper for the tooltip content
|
||||
.tooltip-inner {
|
||||
max-width: $tooltip-max-width;
|
||||
padding: $tooltip-padding-y $tooltip-padding-x;
|
||||
color: $tooltip-color;
|
||||
text-align: center;
|
||||
background-color: $tooltip-bg;
|
||||
@include border-radius($tooltip-border-radius);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// stylelint-disable selector-no-qualifying-type
|
||||
|
||||
.fade {
|
||||
@include transition($transition-fade);
|
||||
|
||||
&:not(.show) {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.collapse {
|
||||
&:not(.show) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.collapsing {
|
||||
position: relative;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
@include transition($transition-collapse);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
// stylelint-disable declaration-no-important, selector-list-comma-newline-after
|
||||
|
||||
//
|
||||
// Headings
|
||||
//
|
||||
|
||||
h1, h2, h3, h4, h5, h6,
|
||||
.h1, .h2, .h3, .h4, .h5, .h6 {
|
||||
margin-bottom: $headings-margin-bottom;
|
||||
font-family: $headings-font-family;
|
||||
font-weight: $headings-font-weight;
|
||||
line-height: $headings-line-height;
|
||||
color: $headings-color;
|
||||
}
|
||||
|
||||
h1, .h1 { font-size: $h1-font-size; }
|
||||
h2, .h2 { font-size: $h2-font-size; }
|
||||
h3, .h3 { font-size: $h3-font-size; }
|
||||
h4, .h4 { font-size: $h4-font-size; }
|
||||
h5, .h5 { font-size: $h5-font-size; }
|
||||
h6, .h6 { font-size: $h6-font-size; }
|
||||
|
||||
.lead {
|
||||
font-size: $lead-font-size;
|
||||
font-weight: $lead-font-weight;
|
||||
}
|
||||
|
||||
// Type display classes
|
||||
.display-1 {
|
||||
font-size: $display1-size;
|
||||
font-weight: $display1-weight;
|
||||
line-height: $display-line-height;
|
||||
}
|
||||
.display-2 {
|
||||
font-size: $display2-size;
|
||||
font-weight: $display2-weight;
|
||||
line-height: $display-line-height;
|
||||
}
|
||||
.display-3 {
|
||||
font-size: $display3-size;
|
||||
font-weight: $display3-weight;
|
||||
line-height: $display-line-height;
|
||||
}
|
||||
.display-4 {
|
||||
font-size: $display4-size;
|
||||
font-weight: $display4-weight;
|
||||
line-height: $display-line-height;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Horizontal rules
|
||||
//
|
||||
|
||||
hr {
|
||||
margin-top: $hr-margin-y;
|
||||
margin-bottom: $hr-margin-y;
|
||||
border: 0;
|
||||
border-top: $hr-border-width solid $hr-border-color;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Emphasis
|
||||
//
|
||||
|
||||
small,
|
||||
.small {
|
||||
font-size: $small-font-size;
|
||||
font-weight: $font-weight-normal;
|
||||
}
|
||||
|
||||
mark,
|
||||
.mark {
|
||||
padding: $mark-padding;
|
||||
background-color: $mark-bg;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Lists
|
||||
//
|
||||
|
||||
.list-unstyled {
|
||||
@include list-unstyled;
|
||||
}
|
||||
|
||||
// Inline turns list items into inline-block
|
||||
.list-inline {
|
||||
@include list-unstyled;
|
||||
}
|
||||
.list-inline-item {
|
||||
display: inline-block;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: $list-inline-padding;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Misc
|
||||
//
|
||||
|
||||
// Builds on `abbr`
|
||||
.initialism {
|
||||
font-size: 90%;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
// Blockquotes
|
||||
.blockquote {
|
||||
margin-bottom: $spacer;
|
||||
font-size: $blockquote-font-size;
|
||||
}
|
||||
|
||||
.blockquote-footer {
|
||||
display: block;
|
||||
font-size: $blockquote-small-font-size;
|
||||
color: $blockquote-small-color;
|
||||
|
||||
&::before {
|
||||
content: "\2014\00A0"; // em dash, nbsp
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
@import "utilities/align";
|
||||
@import "utilities/background";
|
||||
@import "utilities/borders";
|
||||
@import "utilities/clearfix";
|
||||
@import "utilities/display";
|
||||
@import "utilities/embed";
|
||||
@import "utilities/flex";
|
||||
@import "utilities/float";
|
||||
@import "utilities/overflow";
|
||||
@import "utilities/position";
|
||||
@import "utilities/screenreaders";
|
||||
@import "utilities/shadows";
|
||||
@import "utilities/sizing";
|
||||
@import "utilities/spacing";
|
||||
@import "utilities/text";
|
||||
@import "utilities/visibility";
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
* Bootstrap Grid v4.2.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2018 The Bootstrap Authors
|
||||
* Copyright 2011-2018 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
@import "functions";
|
||||
@import "variables";
|
||||
|
||||
@import "mixins/breakpoints";
|
||||
@import "mixins/grid-framework";
|
||||
@import "mixins/grid";
|
||||
|
||||
@import "grid";
|
||||
@import "utilities/display";
|
||||
@import "utilities/flex";
|
||||
@import "utilities/spacing";
|
||||
@@ -0,0 +1,12 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v4.2.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2018 The Bootstrap Authors
|
||||
* Copyright 2011-2018 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
|
||||
@import "functions";
|
||||
@import "variables";
|
||||
@import "mixins";
|
||||
@import "reboot";
|
||||
@@ -0,0 +1,44 @@
|
||||
/*!
|
||||
* Bootstrap v4.2.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2018 The Bootstrap Authors
|
||||
* Copyright 2011-2018 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
@import "functions";
|
||||
@import "variables";
|
||||
@import "mixins";
|
||||
@import "root";
|
||||
@import "reboot";
|
||||
@import "type";
|
||||
@import "images";
|
||||
@import "code";
|
||||
@import "grid";
|
||||
@import "tables";
|
||||
@import "forms";
|
||||
@import "buttons";
|
||||
@import "transitions";
|
||||
@import "dropdown";
|
||||
@import "button-group";
|
||||
@import "input-group";
|
||||
@import "custom-forms";
|
||||
@import "nav";
|
||||
@import "navbar";
|
||||
@import "card";
|
||||
@import "breadcrumb";
|
||||
@import "pagination";
|
||||
@import "badge";
|
||||
@import "jumbotron";
|
||||
@import "alert";
|
||||
@import "progress";
|
||||
@import "media";
|
||||
@import "list-group";
|
||||
@import "close";
|
||||
@import "toasts";
|
||||
@import "modal";
|
||||
@import "tooltip";
|
||||
@import "popover";
|
||||
@import "carousel";
|
||||
@import "spinners";
|
||||
@import "utilities";
|
||||
@import "print";
|
||||
@@ -0,0 +1,13 @@
|
||||
@mixin alert-variant($background, $border, $color) {
|
||||
color: $color;
|
||||
@include gradient-bg($background);
|
||||
border-color: $border;
|
||||
|
||||
hr {
|
||||
border-top-color: darken($border, 5%);
|
||||
}
|
||||
|
||||
.alert-link {
|
||||
color: darken($color, 10%);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// stylelint-disable declaration-no-important
|
||||
|
||||
// Contextual backgrounds
|
||||
|
||||
@mixin bg-variant($parent, $color) {
|
||||
#{$parent} {
|
||||
background-color: $color !important;
|
||||
}
|
||||
a#{$parent},
|
||||
button#{$parent} {
|
||||
@include hover-focus {
|
||||
background-color: darken($color, 10%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin bg-gradient-variant($parent, $color) {
|
||||
#{$parent} {
|
||||
background: $color linear-gradient(180deg, mix($body-bg, $color, 15%), $color) repeat-x !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
@mixin badge-variant($bg) {
|
||||
color: color-yiq($bg);
|
||||
background-color: $bg;
|
||||
|
||||
@at-root a#{&} {
|
||||
@include hover-focus {
|
||||
color: color-yiq($bg);
|
||||
background-color: darken($bg, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Single side border-radius
|
||||
|
||||
@mixin border-radius($radius: $border-radius) {
|
||||
@if $enable-rounded {
|
||||
border-radius: $radius;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin border-top-radius($radius) {
|
||||
@if $enable-rounded {
|
||||
border-top-left-radius: $radius;
|
||||
border-top-right-radius: $radius;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin border-right-radius($radius) {
|
||||
@if $enable-rounded {
|
||||
border-top-right-radius: $radius;
|
||||
border-bottom-right-radius: $radius;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin border-bottom-radius($radius) {
|
||||
@if $enable-rounded {
|
||||
border-bottom-right-radius: $radius;
|
||||
border-bottom-left-radius: $radius;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin border-left-radius($radius) {
|
||||
@if $enable-rounded {
|
||||
border-top-left-radius: $radius;
|
||||
border-bottom-left-radius: $radius;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user