initial commit : all you need is attention
This commit is contained in:
commit
ea81f339d0
|
|
@ -0,0 +1,13 @@
|
|||
# Ignore files type
|
||||
*.log
|
||||
*.env
|
||||
*.exe
|
||||
*.wbk
|
||||
*.cmd
|
||||
*~*
|
||||
|
||||
# Ignore directory
|
||||
**/vbs/
|
||||
**/node_modules/
|
||||
**/logs/
|
||||
**/.vscode/
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Description
|
||||
EasyTransport est une application en cours de transition technologique, destinée à la plateforme AxA IARD Transport. Le backend est écrit en Node.js avec une base de données embarquée PocketBase, et le frontend utilise EJS et Materialize CSS. L'application propose divers modules tels que la tarification, la génération de contrats à partir de formulaires, l'authentification via JWT, et la génération d'attestations.
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"name": "easytransport",
|
||||
"version": "2.0.1",
|
||||
"description": "Plateforme AxA IARD Transport",
|
||||
"main": "./src/server.js",
|
||||
"scripts": {
|
||||
"start": "nodemon ./src/server.js",
|
||||
"build": "pkg ./src/server.js -o EasyTransport",
|
||||
"test": "jest",
|
||||
"db" : "cd ./src/db && start cmd /c Lancement_Pocketbase.cmd"
|
||||
},
|
||||
"pkg": {
|
||||
"assets": [
|
||||
"public/**"
|
||||
]
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "cyril.ducaffy@axa.fr",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^1.7.2",
|
||||
"cjs": "^0.0.11",
|
||||
"docxtemplater": "^3.46.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"ejs": "^3.1.9",
|
||||
"express": "^4.19.2",
|
||||
"fs": "^0.0.1-security",
|
||||
"jsonwebtoken": "^9.0.1",
|
||||
"materialize-css": "^1.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"node-cron": "^3.0.3",
|
||||
"numeral": "^2.0.6",
|
||||
"pizzip": "^3.1.6",
|
||||
"pocketbase": "^0.15.3",
|
||||
"winston": "^3.13.0",
|
||||
"winston-daily-rotate-file": "^4.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^29.6.0",
|
||||
"nodemon": "^2.0.22",
|
||||
"pkg": "^5.8.1",
|
||||
"supertest": "^6.3.3"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,466 @@
|
|||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: darkblue !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
label {
|
||||
color: darkblue !important;
|
||||
font-weight: bold;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
i {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
ul li a:hover {
|
||||
background-color: #26a69a !important;
|
||||
}
|
||||
|
||||
ul li a:active {
|
||||
background-color: gray !important;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: darkblue !important;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #26a69a !important;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background-color: gray !important;
|
||||
}
|
||||
|
||||
hr.form {
|
||||
border-top: lightgray;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 90% !important;
|
||||
}
|
||||
|
||||
.chapter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
background-color: darkblue;
|
||||
color: white;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.chip-info {
|
||||
padding: 5px;
|
||||
width: 30%;
|
||||
text-align: center;
|
||||
background: darkblue;
|
||||
color: white;
|
||||
border-radius: 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.nav-wrapper {
|
||||
background-color: darkblue !important;
|
||||
}
|
||||
|
||||
.brand-logo img {
|
||||
height: 120px;
|
||||
width: auto;
|
||||
transform: translate(25px, 25px);
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.brand-logo img:hover {
|
||||
box-shadow: -1px 1px 44px 0px rgba(0, 0, 0, 0.245);
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.sidenav-trigger {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.btn-floating {
|
||||
background-color: darkred !important;
|
||||
}
|
||||
|
||||
.btn-floating:hover {
|
||||
background-color: gray !important;
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
display: none;
|
||||
color: red;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.mrg {
|
||||
padding: 0 5% !important;
|
||||
}
|
||||
|
||||
.mrg-b-off {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.circle {
|
||||
cursor: pointer;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
background-color: #F44336 !important;
|
||||
border-radius: 50%;
|
||||
border: 4px solid darkblue !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white !important;
|
||||
font-weight: bold !important;
|
||||
margin: 2.5px;
|
||||
}
|
||||
|
||||
.circle-null:hover {
|
||||
background-color: #4f4f4f !important;
|
||||
}
|
||||
|
||||
.circle-null:active {
|
||||
background-color: #9f9f9f !important;
|
||||
}
|
||||
|
||||
.circle-null {
|
||||
cursor: pointer;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
background-color: #8f8f8f !important;
|
||||
border-radius: 50%;
|
||||
border: 4px solid #7f7f7f !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white !important;
|
||||
font-weight: bold !important;
|
||||
margin: 2.5px;
|
||||
}
|
||||
|
||||
.circle:hover {
|
||||
background-color: #f44336dc !important;
|
||||
}
|
||||
|
||||
.circle:active {
|
||||
background-color: #f4433688 !important;
|
||||
}
|
||||
|
||||
.rectangle {
|
||||
cursor: pointer;
|
||||
background-color: #F44336 !important;
|
||||
padding: 2px 25px;
|
||||
border-radius: 10px;
|
||||
border: 4px solid darkblue !important;
|
||||
color: white !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.rectangle:hover {
|
||||
background-color: #f44336dc !important;
|
||||
}
|
||||
|
||||
.rectangle:active {
|
||||
background-color: #f4433688 !important;
|
||||
}
|
||||
|
||||
.chip {
|
||||
height: auto !important;
|
||||
line-height: 20px !important;
|
||||
padding: 5px 12px !important;
|
||||
}
|
||||
|
||||
#goodParcoursModal {
|
||||
width: 80%;
|
||||
max-height: 80%;
|
||||
}
|
||||
|
||||
#gridHistorique {
|
||||
display: inline-grid;
|
||||
width: 100%;
|
||||
grid-template-columns: auto;
|
||||
column-gap: 2%;
|
||||
row-gap: 3%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#selectHistory {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
justify-self: start;
|
||||
width: 500px;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
#btnHistory {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
justify-self: start;
|
||||
margin-left: 0 !important;
|
||||
width: 100% !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.row.no-after::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
#selectHistory, #selectHistory ul, #selectHistory li, #selectHistory span {
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
#navParcoursSelect .active a {
|
||||
background: #26a69a;
|
||||
}
|
||||
|
||||
.border {
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
|
||||
.card-tarif-head {
|
||||
text-align: center;
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #ECF0F1;
|
||||
padding: 1em;
|
||||
border-radius: 7px 7px 0 0;
|
||||
}
|
||||
|
||||
.card-tarif-head-content .price {
|
||||
color: white;
|
||||
font-size: 30px !important;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.card .card-action:last-child {
|
||||
margin-bottom: 20px;
|
||||
border-radius: 0 0 20px 20px;
|
||||
}
|
||||
|
||||
.txt-color {
|
||||
color: darkblue !important;
|
||||
font-weight: bold;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.input-pourcent {
|
||||
background: #00008b21 !important;
|
||||
text-align: center !important;
|
||||
font-weight: bold !important;
|
||||
color: darkblue !important;
|
||||
width: 35% !important;
|
||||
}
|
||||
|
||||
.input-pourcent.set {
|
||||
background: white !important; /* Fond blanc lorsque l'input est set */
|
||||
}
|
||||
|
||||
#moyenTransport .typeTransport {
|
||||
display: flex;
|
||||
justify-content: center
|
||||
}
|
||||
|
||||
#moyenTransport .typeTransport i {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.select-chip {
|
||||
display: none;
|
||||
color: darkblue
|
||||
}
|
||||
|
||||
.subdiv {
|
||||
margin: 20px 0px 10px 0px;
|
||||
}
|
||||
|
||||
.infos-garanties {
|
||||
width: 30rem;
|
||||
}
|
||||
|
||||
.flex-adaptable {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex-adaptable div {
|
||||
width: 100%;
|
||||
padding: 0px 1rem
|
||||
}
|
||||
|
||||
.table-adaptable div {
|
||||
padding: 0px 0rem !important;
|
||||
margin: 0px 5px;
|
||||
}
|
||||
|
||||
.modulo {
|
||||
background-color: darkblue;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.modulo-resume {
|
||||
font-size: 20px;
|
||||
font-weight: normal;
|
||||
padding: 10px !important;
|
||||
}
|
||||
|
||||
.padding-bottom {
|
||||
padding-bottom: 4rem !important;
|
||||
}
|
||||
|
||||
#row-infosAssure .select-wrapper {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.grille-flex {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-evenly;
|
||||
margin-bottom: 20px;
|
||||
|
||||
}
|
||||
|
||||
.grille-flex>div {
|
||||
width: 30rem;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.tarif-card .card-content {
|
||||
display: flex;
|
||||
height: 8rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tarif-card .card-content .tarif-module {
|
||||
font-size: large;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.franchise-card .card-content {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.franchise-card .card-content p {
|
||||
font-size: 1.1rem;
|
||||
padding: 0.8rem 10px;
|
||||
}
|
||||
|
||||
.franchise-card .card-content p:nth-child(even) {
|
||||
background-color: #e2ebff;
|
||||
}
|
||||
|
||||
.franchise-card.selected-card {
|
||||
box-shadow: 0px 0px 54px 0px rgba(0, 0, 0, 0.44);
|
||||
outline: 3px solid darkblue;
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.garAdd .card-content{
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.garAdd .card-title {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
form h5 {
|
||||
margin : 0px 0px 30px 0px
|
||||
}
|
||||
|
||||
a.grille-garanties {
|
||||
width : 40px ;
|
||||
height : 40px ;
|
||||
padding: 2px 0 0 0;
|
||||
border-radius: 100%;
|
||||
background-color: #F44336;
|
||||
}
|
||||
|
||||
a.grille-garanties:hover{
|
||||
background-color: #be3026;
|
||||
}
|
||||
|
||||
.modalAlert .modal-content .modalRed {
|
||||
list-style-type: square;
|
||||
color : red;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.modalAlert .modal-content h4 {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.modalAlert .modal-footer a {
|
||||
color : white
|
||||
}
|
||||
|
||||
#modalTarifCom span.material-icons {
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
#modalTarifCom #qualitePrime {
|
||||
font-size: 1.4rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#modal-grille td {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
#rowExtensionsGarantie .dropdown-content.select-dropdown.multiple-select-dropdown{
|
||||
z-index: 20 !important;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
.flex-adaptable {
|
||||
flex-direction: column;
|
||||
|
||||
}
|
||||
|
||||
.flex-adaptable div {
|
||||
max-width: 100% !important
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.table-adaptable {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.table-adaptable div {
|
||||
padding: 0px 0rem !important;
|
||||
margin: 10px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
.brand-logo img {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
/* Historique Parcours */
|
||||
body>main>div>div.section.center-align {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
body>main>div>div.section.center-align>div.container {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
#historiqueParcours_wrapper {
|
||||
width: 100% !important;
|
||||
overflow-x: auto !important;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
table#historiqueParcours {
|
||||
width: 150%;
|
||||
border-collapse: collapse !important;
|
||||
border: 2px solid darkblue !important;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
#historiqueParcours th,
|
||||
#historiqueParcours td {
|
||||
color: black;
|
||||
text-align: left;
|
||||
font-size: 11px;
|
||||
max-width: 100px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
table.dataTable thead th {
|
||||
padding-right: 18px !important;
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
/* Div pour encapsuler le texte dans les cellules d'en-tête */
|
||||
table.dataTable thead th>div {
|
||||
position: absolute !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
}
|
||||
|
||||
#historiqueParcours_filter label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dataTables_wrapper .dataTables_filter input[type="search"] {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-bottom: 1px solid #26a69a;
|
||||
border-radius: 0;
|
||||
outline: none;
|
||||
height: 3rem;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
margin: 0 0 8px 0;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
box-sizing: content-box;
|
||||
transition: box-shadow .3s, border .3s, -webkit-box-shadow .3s;
|
||||
}
|
||||
|
||||
#historiqueParcours_length>label {
|
||||
font-size: 14px;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.dataTables_length select[name="historiqueParcours_length"] {
|
||||
display: block !important;
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
padding: 8px;
|
||||
margin: 0 0.5em;
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 5px;
|
||||
font-size: 16px;
|
||||
outline: none;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
#historiqueParcours>thead>tr:nth-child(2)>th>input {
|
||||
font-size: 13px !important;
|
||||
padding: 6px !important;
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* icone de tri sur les colonnes */
|
||||
table.dataTable thead .sorting,
|
||||
table.dataTable thead .sorting_asc,
|
||||
table.dataTable thead .sorting_desc {
|
||||
background-position: right center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
/* Ajouter un espacement entre le texte et l'icône */
|
||||
table.dataTable thead .sorting:before,
|
||||
table.dataTable thead .sorting_asc:before,
|
||||
table.dataTable thead .sorting_desc:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* boutons de navigationw */
|
||||
.dataTables_wrapper .dataTables_paginate .paginate_button {
|
||||
background-color: white !important;
|
||||
border: darkblue solid 1.5px !important;
|
||||
color: black !important;
|
||||
padding: 6px 12px !important;
|
||||
margin: 0 2px !important;
|
||||
cursor: pointer !important;
|
||||
border-radius: 4px !important;
|
||||
transition: background-color 0.3s, color 0.3s, border-color 0.3s !important;
|
||||
}
|
||||
|
||||
#historiqueParcours_paginate>span>a.paginate_button.current,
|
||||
.dataTables_wrapper .dataTables_paginate:hover .paginate_button:hover {
|
||||
background: none !important;
|
||||
background-color: darkblue !important;
|
||||
border-color: white !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
/* NC value */
|
||||
td.nc-value {
|
||||
color: lightgray !important;
|
||||
}
|
||||
|
||||
/* Les bouton pour le filtres et les extraction */
|
||||
#divBtnFilter {
|
||||
display: inline-grid;
|
||||
width: 100%;
|
||||
grid-template-columns: auto;
|
||||
column-gap: 3%;
|
||||
row-gap: 10%;
|
||||
}
|
||||
|
||||
#checkRegionAdmin {
|
||||
grid-column: 1 / span 3;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
#divToggleSearch {
|
||||
width: 300px;
|
||||
grid-column: 2;
|
||||
grid-row: 3;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
#divExtractAll {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
#divExtractFilter {
|
||||
grid-column: 3;
|
||||
grid-row: 1;
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
#divBtnFilter button {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(16, 0, 75, 0.2), 0 4px 8px rgba(16, 0, 75, 0.1);
|
||||
}
|
||||
|
||||
/* Bouton Reprendre */
|
||||
#btnReprendre,
|
||||
#btnGenerate {
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 0px 15px;
|
||||
font-size: 10px;
|
||||
cursor: pointer;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
#btnReprendre i {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* checkbox Filter Region Admin */
|
||||
#checkRegionAdmin {
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 10px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
[class^="checkbox-wrapper-"] {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
#checkRegionAdmin input[type="checkbox"] {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#checkRegionAdmin label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
@ -0,0 +1,521 @@
|
|||
Changelog
|
||||
=======
|
||||
Bolded styling surrounded by emojis indicates a breaking change.
|
||||
|
||||
## 1.0.0 (September 9th, 2018)
|
||||
- [Full Changelog here](https://github.com/Dogfalo/materialize/blob/v1-dev/v1-changelog.md)
|
||||
|
||||
## 1.0.0-rc.2 (June 23rd, 2018)
|
||||
- Autocomplete
|
||||
- Fixed bug where Autocomplete did not open properly in certain cases with keyboard focus
|
||||
|
||||
- Carousel
|
||||
- Fix noWrap option bug
|
||||
|
||||
- Collapsible
|
||||
- Now correctly removes all event listeners on destroy
|
||||
|
||||
- Materialbox
|
||||
- Destroy now removed wrapper element added during intialization
|
||||
|
||||
- Pushpin
|
||||
- Fixed bug on IE11 where class was not removed properly
|
||||
|
||||
- Select
|
||||
- No longer triggers onchange event when selecting the same option
|
||||
|
||||
- Sidenav
|
||||
- Destroy now reenables body scrolling if Sidenav was destroyed while it was open
|
||||
|
||||
- Tabs
|
||||
- Tab indicator no longer displays improperly when a scrollbar is present in the element
|
||||
|
||||
|
||||
## 1.0.0-rc.1 (May 1st, 2018)
|
||||
- Autocomplete
|
||||
- Added open and close methods
|
||||
- Fixed bug where Autocomplete would close on click
|
||||
|
||||
- Datepicker
|
||||
- Added autoClose option
|
||||
|
||||
- Modal
|
||||
- Fixed issue with focus with nested modals
|
||||
|
||||
- Select
|
||||
- Removed `active` class on option elements
|
||||
- Fixed bug where `selected` class was not properly removed on option elements
|
||||
|
||||
- Sidenav
|
||||
- Destroy method now correctly removes style property
|
||||
|
||||
- Text Input
|
||||
- Fixed bug where autofill on chrome overlapped text input
|
||||
|
||||
- Toast
|
||||
- Fixed bug where Toast did not respect inDuration option
|
||||
|
||||
- Tooltip
|
||||
- Fixed tooltip positioning bug in certain scenarios when html height was less than screen height
|
||||
- Fixed bug where tooltip stayed open on click
|
||||
|
||||
|
||||
## 1.0.0-beta (March 21st, 2018)
|
||||
- Autocomplete
|
||||
- Now uses dropdown
|
||||
|
||||
- Carousel
|
||||
- Added numVisible option
|
||||
|
||||
- Collapsible
|
||||
- Added keyboard support
|
||||
|
||||
- Dropdown
|
||||
- Fixed scrolling dropdown bug on touch devices
|
||||
|
||||
- Javascript Initialization
|
||||
- Added AutoInit function
|
||||
|
||||
- Modals
|
||||
- Focus now stays within open modal
|
||||
|
||||
- Pickers
|
||||
- Standardized action buttons to match those on android
|
||||
- Added support for date and time input types
|
||||
- **fromnow renamed to fromNow**
|
||||
|
||||
- Select
|
||||
- Dropdown scrolls to selected option
|
||||
|
||||
- Sidenav
|
||||
- Now detects vertical scrolling
|
||||
|
||||
- Tabs
|
||||
- Fixed tab preselection on swipeable tabs
|
||||
|
||||
- TapTarget
|
||||
- **Reverted name change from FeatureDiscovery**
|
||||
|
||||
- Tooltips
|
||||
- Added keyboard support
|
||||
|
||||
|
||||
## 1.0.0-alpha.4 (February 18th, 2018)
|
||||
- Badge
|
||||
- Fixed display issue when used in a table
|
||||
|
||||
- Chips
|
||||
- Fixed autocomplete initialization
|
||||
|
||||
- CSS
|
||||
- Added hide and show classes for extra large breakpoint
|
||||
- Added small buttons
|
||||
- Fixed input helper text alignment when using prefix
|
||||
|
||||
- Date Picker
|
||||
- Fixed month and year select overflow issues
|
||||
- Added additional date formatting options
|
||||
|
||||
- Dropdown
|
||||
- Added container option
|
||||
- Dropdown on mobile now correctly selects the right item
|
||||
- Dropdown now closes correctly on iOS devices
|
||||
|
||||
- Materialbox
|
||||
- Fixed error with photo caption
|
||||
- Fixed issues caused by width and height attributes
|
||||
- Fixed issues caused by max-width and max-height
|
||||
|
||||
- Modal
|
||||
- Improved support for nested modals
|
||||
|
||||
- Parallax
|
||||
- Fixed infinite loop bug
|
||||
|
||||
- Select
|
||||
- *Renamed plugin class to `FormSelect`*
|
||||
- *Renamed jQuery plugin to `formSelect`*
|
||||
|
||||
- Sidenav
|
||||
- Fixed issues with draggable option when used with fixed sidenav
|
||||
|
||||
- Time Picker
|
||||
- Changed i18n options to be more consistent with date picker
|
||||
- Fixed error with auto close option
|
||||
|
||||
|
||||
## 1.0.0-alpha.3 (December 29th, 2017)
|
||||
- *Initialization code for all components changed. E.g. Change`new M.Tooltip(el, options)` to `M.Tooltip.init(el, options)`*
|
||||
- This was done so that the same initialization code can be used to initialize single Elements as well as NodeLists and jQuery element objects
|
||||
|
||||
- Added in onOpen and OnClose callbacks for appropriate plugins
|
||||
|
||||
- Datepicker
|
||||
- Destroy function added
|
||||
|
||||
- Feature Discovery
|
||||
- Added open and close callbacks
|
||||
|
||||
- Materialbox
|
||||
- Added open and close callbacks
|
||||
|
||||
- Modal
|
||||
- Added open and close callbacks for consistency
|
||||
- Removed ready and complete callbacks
|
||||
|
||||
- Parallax
|
||||
- Has responsiveThreshold option
|
||||
- Destroy function added
|
||||
|
||||
- Pushpin
|
||||
- added onPositionChange callback
|
||||
|
||||
## 1.0.0-alpha.2 (November 30th)
|
||||
- Chips
|
||||
- Fixed and standardized chips callback parameters
|
||||
|
||||
- Datepicker
|
||||
- Fixed date format option
|
||||
- Scrollbar no longer unecessarily appears when using datepicker
|
||||
- Fixed bug where using month and year selectors didn't change date
|
||||
|
||||
- Dropdown
|
||||
- Removed automatic focus highlight on open
|
||||
|
||||
- Textarea
|
||||
- Fixed error where text was cut off on Firefox
|
||||
|
||||
- Tabs
|
||||
- Fixed error with pure JavaScript initialization
|
||||
- Fixed error where a tab with no content would break tabs
|
||||
|
||||
- Timepicker
|
||||
- Fixed error with twelveHour options
|
||||
|
||||
- Replaced velocity.js with anime.js
|
||||
|
||||
## 1.0.0-alpha.1 (November 10th)
|
||||
- Dropdown
|
||||
- rewritten with classes
|
||||
- Plugin is initialized on `.dropdown-content` instead of `.dropdown-button`
|
||||
- Renamed classes `.dropdown-button` to `.dropdown-trigger`
|
||||
- Renamed option `belowOrigin` to `coverTrigger`
|
||||
- Added callbacks onOpenStart, onOpenEnd, onCloseStart, onCloseEnd
|
||||
- Removed HTML attribute options
|
||||
- Removed stopPropagation option
|
||||
- Reworked animation
|
||||
|
||||
- Select
|
||||
- rewritten with classes
|
||||
- Plugin renamed from 'material_select' to 'select'
|
||||
- The select will no longer copy the class attribute on each <option> el to the <img> for the icon in the generated select.
|
||||
|
||||
- Tabs
|
||||
- rewritten with classes
|
||||
- added duration option
|
||||
- now requires plugin initialization
|
||||
- tabs 'select_tab' method renamed to 'select'
|
||||
|
||||
- Chips
|
||||
- rewritten with classes
|
||||
- changed events to callbacks
|
||||
- Added limit chips option
|
||||
|
||||
- Autocomplete
|
||||
- rewritten with classes
|
||||
- Added updateData method
|
||||
- Added sortFunction option
|
||||
|
||||
- Feature Discovery
|
||||
- rewritten with classes
|
||||
- Plugin renamed from 'tapTarget' to 'featureDiscovery'
|
||||
|
||||
- Forms
|
||||
- Added new helper text element
|
||||
- Moved validation messages 'data-error' and 'data-success' to Helper Text
|
||||
|
||||
- Pickatime
|
||||
- Renamed to Timepicker
|
||||
- Now opens on enter or click instead of focus
|
||||
- Added open animation
|
||||
- Reworked and simplified Timepicker HTML structure
|
||||
- Renamed internal classes to reflect Timpicker namechange and structure rework
|
||||
|
||||
- Floating Action Button
|
||||
- Converted to plugin
|
||||
- Added direction option
|
||||
- Added toolbar transition option
|
||||
|
||||
|
||||
## v0.100.1 (July 21st)
|
||||
- Fixed bug where modal triggers could not contain child elements
|
||||
- Fixed bug with right alignment option for dropdown
|
||||
- Allow select native browser validation error messages
|
||||
- Added fix for validation messages being mispositioned when input is empty
|
||||
|
||||
## v0.100.0 (July 19th)
|
||||
- :sparkles: **Rewrote Modal Plugin** :sparkles:
|
||||
- Modal open no longer initializes plugin
|
||||
- Fixed bug where modal open did not use initialized options
|
||||
- Modal-trigger class required for modal trigger elements
|
||||
- :sparkles: **Rewrote Toast Plugin** :sparkles:
|
||||
- Added class method to dismiss all toasts
|
||||
- Added instance method to remove specific toasts
|
||||
- Validation styling support added for many form components
|
||||
- Added ability to remove autocompelete data
|
||||
- Fixed waves persisting bug
|
||||
- Waves no longer throws error on svg elements
|
||||
- Fixed side nav callback bugs
|
||||
- Tab accessibility for date picker
|
||||
- Added container option for time picker
|
||||
- Fixed carousel image loading bug
|
||||
- Full width carousel now resizes height on resize
|
||||
- Added carousel destroy
|
||||
- Fixed multiple bugs with jQuery outerWidth on Linux
|
||||
- Fixed cursor blinking on select on iOS
|
||||
- Fixed search form styling in navbar
|
||||
- Fixed label animation on date picker
|
||||
- Added close on select option for date picker
|
||||
- Browser errors now show up on radio buttons and checkboxes
|
||||
|
||||
## v0.99.0 (June 22th)
|
||||
- Added support for jQuery 3
|
||||
- Fixed dynamic textarea resize bug
|
||||
- Added support for custom active elements in scrollspy
|
||||
- Added Time Picker
|
||||
- Updated styling for Date Picker
|
||||
- Added callbacks to side nav
|
||||
- Updated styling for switches
|
||||
|
||||
## v0.98.2 (April 14th)
|
||||
- :no_good: **Autocomplete: renamed and moved options to `autocompleteOptions`** :no_good:
|
||||
- Fixed collapsible preselect bug
|
||||
- Fixed dropdown event bubbling bug
|
||||
- Fixed range position inaccuracies
|
||||
- Fixed feature discovery mobile styles
|
||||
- Fixed carousel reinitialize bugs
|
||||
- Fixed grid offset bug
|
||||
|
||||
|
||||
## v0.98.1 (March 21st)
|
||||
- Fixed various select bugs on mobile devices
|
||||
- Fixed small sidenav overlay bugs
|
||||
- Fixed carousel resizing bug
|
||||
- Fixed materialbox callback bug
|
||||
- Range slider supports keyboard navigation
|
||||
- Added XL breakpoint
|
||||
- Added Pulse CSS effect
|
||||
- Added Feature Discovery component
|
||||
|
||||
|
||||
## v0.98.0 (January 25th)
|
||||
- :no_good: **Standardized plugin option naming to camelcase (please check your plugin calls to make sure all the options are camelcase)** :no_good:
|
||||
- Added FABs in image cards
|
||||
- Added swipeable tabs
|
||||
- Fixed carousel misalignment when switching quickly
|
||||
- Fixed carousel resize bug where slide widths wouldn't change when changing window size
|
||||
- Improved tabs compatibility with cards
|
||||
- Fixed bug where using backspace to delete chips would navigate back in certain browsers
|
||||
- Added autocomplete integration with chips
|
||||
- Upgraded noUiSlider to version 9 with support for vertical sliders
|
||||
|
||||
|
||||
## v0.97.8 (October 30th, 2016)
|
||||
- **Refactored Modal plugin**
|
||||
- Tabs now supported in navbar
|
||||
- Chips data can now be reinitiailized
|
||||
- Minor side nav fixes
|
||||
- FAB to toolbar component added
|
||||
- Fixed dropdown options bug
|
||||
|
||||
|
||||
## v0.97.7 (July 23rd, 2016)
|
||||
- Basic horizontal cards
|
||||
- Carousel bug fixes and new features
|
||||
- Updated sidenav styles and new component
|
||||
- Meteor package now supports Sass
|
||||
- Autocomplete form component
|
||||
- Chips jQuery plugin
|
||||
|
||||
|
||||
## v0.97.6 (April 1st, 2016)
|
||||
- **Removed deprecated material icons from project**
|
||||
- **Changed /font directory to /fonts**
|
||||
- Datepicker and ScrollSpy now compatible with jQuery 2.2.x
|
||||
- Responsive tables now work with empty cells
|
||||
- Added focus states to checkboxes, switches, and radio buttons
|
||||
- Sidenav and Modals no longer cause flicker with scrollbar
|
||||
- Materialbox overflow and z-index issues fixed
|
||||
- Added new option for Card actions within a Card reveal
|
||||
|
||||
|
||||
## v0.97.5 (December 21st, 2015)
|
||||
- Fixed Meteor package crash
|
||||
|
||||
|
||||
## v0.97.4 (Dec 20, 2015)
|
||||
- Added Jasmine testing with Travis CI
|
||||
- Select bugfixes
|
||||
- Grid Offset bugfix
|
||||
- Dropdown overflow bugfix
|
||||
- Range slider error bugfix
|
||||
|
||||
|
||||
## v0.97.3 (Nov 15, 2015)
|
||||
- Meteor font issues fixed
|
||||
- Select rendering issue fixed
|
||||
- Added Push and Pull to grid
|
||||
- Dynamic accordion appends fixed
|
||||
|
||||
|
||||
## v0.97.2 (Nov 8, 2015)
|
||||
- Image support inside select
|
||||
- Optgroup supported in select
|
||||
- Multiple select added
|
||||
- Card styling fixes
|
||||
- Breadcrumbs added
|
||||
- Scrollable tabs
|
||||
- Tooltips and dropdowns position themselves more intelligently inside the window
|
||||
- FAB menu is click-toggleable
|
||||
- Horizontal FAB support added
|
||||
|
||||
|
||||
## v0.97.1 (Sep 13, 2015)
|
||||
- Added new range slider with uses noUiSlider to provide powerful options
|
||||
- Added CSS for Chips
|
||||
- Toasts support adding of html elements
|
||||
- Fixed select destroy/creation bug
|
||||
- Bugfixes for dropdown, badges, collections, scrollfire
|
||||
- Added default preloader color variable
|
||||
- File input now supports multiple files and dynamically loaded elements
|
||||
|
||||
|
||||
## v0.97.0 (June 21, 2015)
|
||||
- **Documentation changed to use Official Google Icon web font**
|
||||
- **Input errors added**
|
||||
- Flicker on Firefox on dropdowns fixed
|
||||
- Pagination made more responsive
|
||||
- Modal now prevents scrolling
|
||||
- Modal animation added
|
||||
- Support for multiple modals added
|
||||
- Programmatic control of FAB to open/close added
|
||||
- Programmatic control of slider to play/pause added
|
||||
- Plus many more bug fixes
|
||||
|
||||
|
||||
## v0.96.0 (April 1, 2015)
|
||||
- Toasts, transitions, scrollfire added under Materialize namespace
|
||||
- Dropdown is now created as a child of its parent
|
||||
- Collapsibles supports nesting
|
||||
- Modal Bottom Sheet added
|
||||
- Indeterminate Checkboxes added
|
||||
- New Checkbox Style added
|
||||
- Text Inputs supports placeholder/readonly
|
||||
- Google Inbox-like Collapsible added
|
||||
- Text Character Counter added
|
||||
- Waves no longer breaks on SVGs
|
||||
|
||||
|
||||
## v0.95.3 (Feb 25, 2015)
|
||||
- Parallax image loading / responsiveness fixes
|
||||
- Date picker supports month/year as dropdown
|
||||
- Dismissable collection items
|
||||
- Avatar collection items
|
||||
- Pagination Added
|
||||
- ScrollFire fixes
|
||||
|
||||
|
||||
## v0.95.2 (Feb 10, 2015)
|
||||
- Switches added
|
||||
- Transition animation functions added
|
||||
- ScrollFire Plugin added (fires functions dependent on scroll position)
|
||||
- Responsive Video tag added
|
||||
- Custom File Input Button added
|
||||
- Modals has a fixed footer option
|
||||
- Sidenav implementation changed (needs 2 UL menus)
|
||||
- Slider Responsive Fixes
|
||||
|
||||
|
||||
## v0.95.1 (Jan 26, 2015)
|
||||
- Sidenav Fixes
|
||||
- Dropdown alignment/gutter options added
|
||||
- Parallax fixes
|
||||
- JavaScript Initialization no longer needed for many components
|
||||
- HTML options through data-attributes
|
||||
- Site colors can be defined through Primary and Secondary color in Sass
|
||||
- Tables no longer resonsive by default
|
||||
|
||||
|
||||
## v0.95.0 (Jan 17, 2015)
|
||||
- Drag Out Menu fixed with Touch Interactions
|
||||
- Toasts minor bugfix
|
||||
- OL element has default styling
|
||||
- Fullscreen Slider added
|
||||
- Footer requires page-footer class
|
||||
- Progress Bars added
|
||||
- Form autofill support added
|
||||
- Responsive Tables support added
|
||||
- Scrollspy Plugin released
|
||||
- Waves events are now delegated / behavior enhanced
|
||||
|
||||
|
||||
## v0.94.0 (Dec 30, 2014)
|
||||
- Sidenav supports right edge positioning
|
||||
- Responsive Embeds
|
||||
- Image Vertical align classes
|
||||
- border-box added
|
||||
- Variable file created
|
||||
- Pushpin added
|
||||
- Tooltips support all directions
|
||||
- Layout helper classes added
|
||||
- Materialbox Fixes
|
||||
- Form Element Enhancements
|
||||
- Navbar supports search bar
|
||||
- Waves fixes
|
||||
- Materialbox Captions
|
||||
- Image Slider Fixes
|
||||
|
||||
|
||||
## v0.93.1 (Dec 20, 2014)
|
||||
- Flexbox Sticky Footer removed due to IE incompatibility
|
||||
|
||||
|
||||
## v0.93.0 (Dec 19, 2014)
|
||||
- Card Reveal
|
||||
- Image Slider
|
||||
- Dynamically loaded forms work correctly
|
||||
- Badges added
|
||||
- Circular Image
|
||||
- Waves Fixes
|
||||
- Footer Added
|
||||
- Toast support Custom HTML
|
||||
- Modals support programmatic opening/closing
|
||||
- Responsive Image support
|
||||
|
||||
|
||||
## v0.92.1 (Dec 14, 2014)
|
||||
- Bower semver fix
|
||||
- Added new radio button style
|
||||
|
||||
|
||||
## v0.92.0 (Dec 13, 2014)
|
||||
- Clicking icon in dropdown in navbar no longer closes dropdown immediately
|
||||
- Multiple select inputs now work properly
|
||||
- Mobile navbar no longer extends past screen width
|
||||
- Parallax improved
|
||||
- Modal restructured / can be opened programmatically
|
||||
- Callbacks added to modals
|
||||
- Added dist folder to repo
|
||||
- Cards restructured
|
||||
|
||||
|
||||
## v0.91 (Dec 3, 2014)
|
||||
- bug fixes to forms
|
||||
- added waves color classes
|
||||
- toast thickened to look better on mobile
|
||||
- many other bug fixes
|
||||
|
||||
|
||||
## v0.9 (Nov 30, 2014)
|
||||
- Touch interactions added
|
||||
- tons more...
|
||||
|
|
@ -0,0 +1,672 @@
|
|||
module.exports = function(grunt) {
|
||||
let concatFile = 'temp/js/materialize_concat.js.map';
|
||||
|
||||
// configure the tasks
|
||||
let config = {
|
||||
// Jasmine
|
||||
jasmine: {
|
||||
components: {
|
||||
src: ['bin/materialize.js'],
|
||||
options: {
|
||||
vendor: [
|
||||
'node_modules/jquery/dist/jquery.min.js',
|
||||
'node_modules/jasmine-jquery/lib/jasmine-jquery.js'
|
||||
],
|
||||
styles: 'bin/materialize.css',
|
||||
specs: 'tests/spec/**/*Spec.js',
|
||||
helpers: 'tests/spec/helper.js',
|
||||
keepRunner: true
|
||||
//helpers: 'test/spec/*.js'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Sass
|
||||
sass: {
|
||||
// Task
|
||||
expanded: {
|
||||
// Target options
|
||||
options: {
|
||||
outputStyle: 'expanded',
|
||||
sourcemap: false
|
||||
},
|
||||
files: {
|
||||
'dist/css/materialize.css': 'sass/materialize.scss'
|
||||
}
|
||||
},
|
||||
|
||||
min: {
|
||||
options: {
|
||||
outputStyle: 'compressed',
|
||||
sourcemap: false
|
||||
},
|
||||
files: {
|
||||
'dist/css/materialize.min.css': 'sass/materialize.scss'
|
||||
}
|
||||
},
|
||||
|
||||
// Compile ghpages css
|
||||
gh: {
|
||||
options: {
|
||||
outputStyle: 'compressed',
|
||||
sourcemap: false
|
||||
},
|
||||
files: {
|
||||
'css/ghpages-materialize.css': 'sass/ghpages-materialize.scss'
|
||||
}
|
||||
},
|
||||
|
||||
// Compile bin css
|
||||
bin: {
|
||||
options: {
|
||||
outputStyle: 'expanded',
|
||||
sourcemap: false
|
||||
},
|
||||
files: {
|
||||
'bin/materialize.css': 'sass/materialize.scss'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// PostCss Autoprefixer
|
||||
postcss: {
|
||||
options: {
|
||||
processors: [
|
||||
require('autoprefixer')({
|
||||
browsers: [
|
||||
'last 2 versions',
|
||||
'Chrome >= 30',
|
||||
'Firefox >= 30',
|
||||
'ie >= 10',
|
||||
'Safari >= 8'
|
||||
]
|
||||
})
|
||||
]
|
||||
},
|
||||
expanded: {
|
||||
src: 'dist/css/materialize.css'
|
||||
},
|
||||
min: {
|
||||
src: 'dist/css/materialize.min.css'
|
||||
},
|
||||
gh: {
|
||||
src: 'css/ghpages-materialize.css'
|
||||
},
|
||||
bin: {
|
||||
src: 'bin/materialize.css'
|
||||
}
|
||||
},
|
||||
|
||||
babel: {
|
||||
options: {
|
||||
sourceMap: false,
|
||||
plugins: [
|
||||
'transform-es2015-arrow-functions',
|
||||
'transform-es2015-block-scoping',
|
||||
'transform-es2015-classes',
|
||||
'transform-es2015-template-literals',
|
||||
'transform-es2015-object-super'
|
||||
]
|
||||
},
|
||||
bin: {
|
||||
options: {
|
||||
sourceMap: true
|
||||
},
|
||||
files: {
|
||||
'bin/materialize.js': 'temp/js/materialize_concat.js'
|
||||
}
|
||||
},
|
||||
dist: {
|
||||
files: {
|
||||
'dist/js/materialize.js': 'temp/js/materialize.js'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Browser Sync integration
|
||||
browserSync: {
|
||||
bsFiles: ['bin/*', 'css/ghpages-materialize.css', '!**/node_modules/**/*'],
|
||||
options: {
|
||||
server: {
|
||||
baseDir: './' // make server from root dir
|
||||
},
|
||||
port: 8000,
|
||||
ui: {
|
||||
port: 8080,
|
||||
weinre: {
|
||||
port: 9090
|
||||
}
|
||||
},
|
||||
open: false
|
||||
}
|
||||
},
|
||||
|
||||
// Concat
|
||||
concat: {
|
||||
options: {
|
||||
separator: ';'
|
||||
},
|
||||
dist: {
|
||||
// the files to concatenate
|
||||
src: [
|
||||
'js/cash.js',
|
||||
'js/component.js',
|
||||
'js/global.js',
|
||||
'js/anime.min.js',
|
||||
'js/collapsible.js',
|
||||
'js/dropdown.js',
|
||||
'js/modal.js',
|
||||
'js/materialbox.js',
|
||||
'js/parallax.js',
|
||||
'js/tabs.js',
|
||||
'js/tooltip.js',
|
||||
'js/waves.js',
|
||||
'js/toasts.js',
|
||||
'js/sidenav.js',
|
||||
'js/scrollspy.js',
|
||||
'js/autocomplete.js',
|
||||
'js/forms.js',
|
||||
'js/slider.js',
|
||||
'js/cards.js',
|
||||
'js/chips.js',
|
||||
'js/pushpin.js',
|
||||
'js/buttons.js',
|
||||
'js/datepicker.js',
|
||||
'js/timepicker.js',
|
||||
'js/characterCounter.js',
|
||||
'js/carousel.js',
|
||||
'js/tapTarget.js',
|
||||
'js/select.js',
|
||||
'js/range.js'
|
||||
],
|
||||
// the location of the resulting JS file
|
||||
dest: 'temp/js/materialize.js'
|
||||
},
|
||||
temp: {
|
||||
// the files to concatenate
|
||||
options: {
|
||||
sourceMap: true,
|
||||
sourceMapStyle: 'link'
|
||||
},
|
||||
src: [
|
||||
'js/cash.js',
|
||||
'js/component.js',
|
||||
'js/global.js',
|
||||
'js/anime.min.js',
|
||||
'js/collapsible.js',
|
||||
'js/dropdown.js',
|
||||
'js/modal.js',
|
||||
'js/materialbox.js',
|
||||
'js/parallax.js',
|
||||
'js/tabs.js',
|
||||
'js/tooltip.js',
|
||||
'js/waves.js',
|
||||
'js/toasts.js',
|
||||
'js/sidenav.js',
|
||||
'js/scrollspy.js',
|
||||
'js/autocomplete.js',
|
||||
'js/forms.js',
|
||||
'js/slider.js',
|
||||
'js/cards.js',
|
||||
'js/chips.js',
|
||||
'js/pushpin.js',
|
||||
'js/buttons.js',
|
||||
'js/datepicker.js',
|
||||
'js/timepicker.js',
|
||||
'js/characterCounter.js',
|
||||
'js/carousel.js',
|
||||
'js/tapTarget.js',
|
||||
'js/select.js',
|
||||
'js/range.js'
|
||||
],
|
||||
// the location of the resulting JS file
|
||||
dest: 'temp/js/materialize_concat.js'
|
||||
}
|
||||
},
|
||||
|
||||
// Uglify
|
||||
uglify: {
|
||||
options: {
|
||||
// Use these options when debugging
|
||||
// mangle: false,
|
||||
// compress: false,
|
||||
// beautify: true
|
||||
},
|
||||
dist: {
|
||||
files: {
|
||||
'dist/js/materialize.min.js': ['dist/js/materialize.js']
|
||||
}
|
||||
},
|
||||
bin: {
|
||||
files: {
|
||||
'bin/materialize.min.js': ['bin/materialize.js']
|
||||
}
|
||||
},
|
||||
extras: {
|
||||
files: {
|
||||
'extras/noUiSlider/nouislider.min.js': ['extras/noUiSlider/nouislider.js']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Compress
|
||||
compress: {
|
||||
main: {
|
||||
options: {
|
||||
archive: 'bin/materialize.zip',
|
||||
level: 6
|
||||
},
|
||||
files: [
|
||||
{ expand: true, cwd: 'dist/', src: ['**/*'], dest: 'materialize/' },
|
||||
{ expand: true, cwd: './', src: ['LICENSE', 'README.md'], dest: 'materialize/' }
|
||||
]
|
||||
},
|
||||
|
||||
src: {
|
||||
options: {
|
||||
archive: 'bin/materialize-src.zip',
|
||||
level: 6
|
||||
},
|
||||
files: [
|
||||
{ expand: true, cwd: 'sass/', src: ['materialize.scss'], dest: 'materialize-src/sass/' },
|
||||
{ expand: true, cwd: 'sass/', src: ['components/**/*'], dest: 'materialize-src/sass/' },
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'js/',
|
||||
src: [
|
||||
'anime.min.js',
|
||||
'cash.js',
|
||||
'component.js',
|
||||
'global.js',
|
||||
'collapsible.js',
|
||||
'dropdown.js',
|
||||
'modal.js',
|
||||
'materialbox.js',
|
||||
'parallax.js',
|
||||
'tabs.js',
|
||||
'tooltip.js',
|
||||
'waves.js',
|
||||
'toasts.js',
|
||||
'sidenav.js',
|
||||
'scrollspy.js',
|
||||
'autocomplete.js',
|
||||
'forms.js',
|
||||
'slider.js',
|
||||
'cards.js',
|
||||
'chips.js',
|
||||
'pushpin.js',
|
||||
'buttons.js',
|
||||
'datepicker.js',
|
||||
'timepicker.js',
|
||||
'characterCounter.js',
|
||||
'carousel.js',
|
||||
'tapTarget.js',
|
||||
'select.js',
|
||||
'range.js'
|
||||
],
|
||||
dest: 'materialize-src/js/'
|
||||
},
|
||||
{ expand: true, cwd: 'dist/js/', src: ['**/*'], dest: 'materialize-src/js/bin/' },
|
||||
{ expand: true, cwd: './', src: ['LICENSE', 'README.md'], dest: 'materialize-src/' }
|
||||
]
|
||||
},
|
||||
|
||||
starter_template: {
|
||||
options: {
|
||||
archive: 'templates/starter-template.zip',
|
||||
level: 6
|
||||
},
|
||||
files: [
|
||||
{ expand: true, cwd: 'dist/', src: ['**/*'], dest: 'starter-template/' },
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'templates/starter-template/',
|
||||
src: ['index.html', 'LICENSE'],
|
||||
dest: 'starter-template/'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'templates/starter-template/css',
|
||||
src: ['style.css'],
|
||||
dest: 'starter-template/css'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'templates/starter-template/js',
|
||||
src: ['init.js'],
|
||||
dest: 'starter-template/js'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
parallax_template: {
|
||||
options: {
|
||||
archive: 'templates/parallax-template.zip',
|
||||
level: 6
|
||||
},
|
||||
files: [
|
||||
{ expand: true, cwd: 'dist/', src: ['**/*'], dest: 'parallax-template/' },
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'templates/parallax-template/',
|
||||
src: ['index.html', 'LICENSE', 'background1.jpg', 'background2.jpg', 'background3.jpg'],
|
||||
dest: 'parallax-template/'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'templates/parallax-template/css',
|
||||
src: ['style.css'],
|
||||
dest: 'parallax-template/css'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'templates/parallax-template/js',
|
||||
src: ['init.js'],
|
||||
dest: 'parallax-template/js'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Clean
|
||||
clean: {
|
||||
temp: {
|
||||
src: ['temp/']
|
||||
}
|
||||
},
|
||||
|
||||
// Jade
|
||||
jade: {
|
||||
compile: {
|
||||
options: {
|
||||
pretty: true,
|
||||
data: {
|
||||
debug: false
|
||||
}
|
||||
},
|
||||
files: {
|
||||
'index.html': 'jade/index.jade',
|
||||
'icons.html': 'jade/icons.jade',
|
||||
'about.html': 'jade/about.jade',
|
||||
'sass.html': 'jade/sass.jade',
|
||||
'getting-started.html': 'jade/getting-started.jade',
|
||||
'mobile.html': 'jade/mobile.jade',
|
||||
'showcase.html': 'jade/showcase.jade',
|
||||
'parallax.html': 'jade/parallax.jade',
|
||||
'parallax-demo.html': 'jade/parallax-demo.jade',
|
||||
'typography.html': 'jade/typography.jade',
|
||||
'color.html': 'jade/color.jade',
|
||||
'shadow.html': 'jade/shadow.jade',
|
||||
'grid.html': 'jade/grid.jade',
|
||||
'media-css.html': 'jade/media-css.jade',
|
||||
'table.html': 'jade/table.jade',
|
||||
'helpers.html': 'jade/helpers.jade',
|
||||
'buttons.html': 'jade/buttons.jade',
|
||||
'navbar.html': 'jade/navbar.jade',
|
||||
'cards.html': 'jade/cards.jade',
|
||||
'preloader.html': 'jade/preloader.jade',
|
||||
'collections.html': 'jade/collections.jade',
|
||||
'badges.html': 'jade/badges.jade',
|
||||
'footer.html': 'jade/footer.jade',
|
||||
'modals.html': 'jade/modals.jade',
|
||||
'dropdown.html': 'jade/dropdown.jade',
|
||||
'tabs.html': 'jade/tabs.jade',
|
||||
'toasts.html': 'jade/toasts.jade',
|
||||
'tooltips.html': 'jade/tooltips.jade',
|
||||
'sidenav.html': 'jade/sidenav.jade',
|
||||
'pushpin.html': 'jade/pushpin.jade',
|
||||
'waves.html': 'jade/waves.jade',
|
||||
'media.html': 'jade/media.jade',
|
||||
'collapsible.html': 'jade/collapsible.jade',
|
||||
'scrollspy.html': 'jade/scrollspy.jade',
|
||||
'fullscreen-slider-demo.html': 'jade/fullscreen-slider-demo.jade',
|
||||
'pagination.html': 'jade/pagination.jade',
|
||||
'breadcrumbs.html': 'jade/breadcrumbs.jade',
|
||||
'carousel.html': 'jade/carousel.jade',
|
||||
'feature-discovery.html': 'jade/feature-discovery.jade',
|
||||
'pulse.html': 'jade/pulse.jade',
|
||||
'pushpin-demo.html': 'jade/pushpin-demo.jade',
|
||||
'css-transitions.html': 'jade/css-transitions.jade',
|
||||
'themes.html': 'jade/themes.jade',
|
||||
'404.html': 'jade/404.jade',
|
||||
'autocomplete.html': 'jade/autocomplete.jade',
|
||||
'checkboxes.html': 'jade/checkboxes.jade',
|
||||
'chips.html': 'jade/chips.jade',
|
||||
'pickers.html': 'jade/pickers.jade',
|
||||
'radio-buttons.html': 'jade/radio-buttons.jade',
|
||||
'range.html': 'jade/range.jade',
|
||||
'select.html': 'jade/select.jade',
|
||||
'switches.html': 'jade/switches.jade',
|
||||
'text-inputs.html': 'jade/text-inputs.jade',
|
||||
'support-us.html': 'jade/support-us.jade',
|
||||
'floating-action-button.html': 'jade/floating-action-button.jade',
|
||||
'auto-init.html': 'jade/auto-init.jade'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Watch Files
|
||||
watch: {
|
||||
jade: {
|
||||
files: ['jade/**/*'],
|
||||
tasks: ['jade_compile'],
|
||||
options: {
|
||||
interrupt: false,
|
||||
spawn: false
|
||||
}
|
||||
},
|
||||
|
||||
js: {
|
||||
files: ['js/**/*', '!js/init.js'],
|
||||
tasks: ['js_compile'],
|
||||
options: {
|
||||
interrupt: false,
|
||||
spawn: false
|
||||
}
|
||||
},
|
||||
|
||||
sass: {
|
||||
files: ['sass/**/*'],
|
||||
tasks: ['sass_compile'],
|
||||
options: {
|
||||
interrupt: false,
|
||||
spawn: false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Concurrent
|
||||
concurrent: {
|
||||
options: {
|
||||
logConcurrentOutput: true,
|
||||
limit: 10
|
||||
},
|
||||
monitor: {
|
||||
tasks: [
|
||||
'jade_compile',
|
||||
'sass_compile',
|
||||
'js_compile',
|
||||
'watch:jade',
|
||||
'watch:js',
|
||||
'watch:sass',
|
||||
'notify:watching',
|
||||
'server'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Notifications
|
||||
notify: {
|
||||
watching: {
|
||||
options: {
|
||||
enabled: true,
|
||||
message: 'Watching Files!',
|
||||
title: 'Materialize', // defaults to the name in package.json, or will use project directory's name
|
||||
success: true, // whether successful grunt executions should be notified automatically
|
||||
duration: 1 // the duration of notification in seconds, for `notify-send only
|
||||
}
|
||||
},
|
||||
|
||||
sass_compile: {
|
||||
options: {
|
||||
enabled: true,
|
||||
message: 'Sass Compiled!',
|
||||
title: 'Materialize',
|
||||
success: true,
|
||||
duration: 1
|
||||
}
|
||||
},
|
||||
|
||||
js_compile: {
|
||||
options: {
|
||||
enabled: true,
|
||||
message: 'JS Compiled!',
|
||||
title: 'Materialize',
|
||||
success: true,
|
||||
duration: 1
|
||||
}
|
||||
},
|
||||
|
||||
jade_compile: {
|
||||
options: {
|
||||
enabled: true,
|
||||
message: 'Jade Compiled!',
|
||||
title: 'Materialize',
|
||||
success: true,
|
||||
duration: 1
|
||||
}
|
||||
},
|
||||
|
||||
server: {
|
||||
options: {
|
||||
enabled: true,
|
||||
message: 'Server Running!',
|
||||
title: 'Materialize',
|
||||
success: true,
|
||||
duration: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Text Replace
|
||||
replace: {
|
||||
version: {
|
||||
// Does not edit README.md
|
||||
src: ['bower.json', 'package.json', 'package.js', 'jade/**/*.html'],
|
||||
overwrite: true,
|
||||
replacements: [
|
||||
{
|
||||
from: grunt.option('oldver'),
|
||||
to: grunt.option('newver')
|
||||
}
|
||||
]
|
||||
},
|
||||
readme: {
|
||||
// Changes README.md
|
||||
src: ['README.md'],
|
||||
overwrite: true,
|
||||
replacements: [
|
||||
{
|
||||
from: 'Current Version : v' + grunt.option('oldver'),
|
||||
to: 'Current Version : v' + grunt.option('newver')
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Create Version Header for files
|
||||
usebanner: {
|
||||
release: {
|
||||
options: {
|
||||
position: 'top',
|
||||
banner:
|
||||
'/*!\n * Materialize v' +
|
||||
grunt.option('newver') +
|
||||
' (http://materializecss.com)\n * Copyright 2014-2017 Materialize\n * MIT License (https://raw.githubusercontent.com/Dogfalo/materialize/master/LICENSE)\n */',
|
||||
linebreak: true
|
||||
},
|
||||
files: {
|
||||
src: ['dist/css/*.css', 'dist/js/*.js']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Rename files
|
||||
rename: {
|
||||
rename_src: {
|
||||
src: 'bin/materialize-src' + '.zip',
|
||||
dest: 'bin/materialize-src-v' + grunt.option('newver') + '.zip',
|
||||
options: {
|
||||
ignore: true
|
||||
}
|
||||
},
|
||||
rename_compiled: {
|
||||
src: 'bin/materialize' + '.zip',
|
||||
dest: 'bin/materialize-v' + grunt.option('newver') + '.zip',
|
||||
options: {
|
||||
ignore: true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
grunt.initConfig(config);
|
||||
|
||||
// load the tasks
|
||||
// grunt.loadNpmTasks('grunt-gitinfo');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-sass');
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-jade');
|
||||
grunt.loadNpmTasks('grunt-concurrent');
|
||||
grunt.loadNpmTasks('grunt-notify');
|
||||
grunt.loadNpmTasks('grunt-text-replace');
|
||||
grunt.loadNpmTasks('grunt-banner');
|
||||
grunt.loadNpmTasks('grunt-rename-util');
|
||||
grunt.loadNpmTasks('grunt-browser-sync');
|
||||
grunt.loadNpmTasks('grunt-contrib-jasmine');
|
||||
grunt.loadNpmTasks('grunt-postcss');
|
||||
grunt.loadNpmTasks('grunt-babel');
|
||||
|
||||
// define the tasks
|
||||
grunt.registerTask('release', [
|
||||
'sass:expanded',
|
||||
'sass:min',
|
||||
'postcss:expanded',
|
||||
'postcss:min',
|
||||
'concat:dist',
|
||||
'babel:dist',
|
||||
'uglify:dist',
|
||||
'uglify:extras',
|
||||
'usebanner:release',
|
||||
'compress:main',
|
||||
'compress:src',
|
||||
'compress:starter_template',
|
||||
'compress:parallax_template',
|
||||
'replace:version',
|
||||
'replace:readme',
|
||||
'rename:rename_src',
|
||||
'rename:rename_compiled',
|
||||
'clean:temp'
|
||||
]);
|
||||
|
||||
grunt.task.registerTask('configureBabel', 'configures babel options', function() {
|
||||
config.babel.bin.options.inputSourceMap = grunt.file.readJSON(concatFile);
|
||||
});
|
||||
|
||||
grunt.registerTask('jade_compile', ['jade', 'notify:jade_compile']);
|
||||
grunt.registerTask('js_compile', ['concat:temp', 'configureBabel', 'babel:bin', 'clean:temp']);
|
||||
grunt.registerTask('sass_compile', [
|
||||
'sass:gh',
|
||||
'sass:bin',
|
||||
'postcss:gh',
|
||||
'postcss:bin',
|
||||
'notify:sass_compile'
|
||||
]);
|
||||
grunt.registerTask('server', ['browserSync', 'notify:server']);
|
||||
grunt.registerTask('monitor', ['concurrent:monitor']);
|
||||
grunt.registerTask('travis', ['js_compile', 'sass_compile', 'jasmine']);
|
||||
};
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2018 Materialize
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<p align="center">
|
||||
<a href="http://materializecss.com/">
|
||||
<img src="http://materializecss.com/res/materialize.svg" width="150">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h3 align="center">MaterializeCSS</h3>
|
||||
|
||||
<p align="center">
|
||||
Materialize, a CSS Framework based on material design.
|
||||
<br>
|
||||
<a href="http://materializecss.com/"><strong>-- Browse the docs --</strong></a>
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://travis-ci.org/Dogfalo/materialize">
|
||||
<img src="https://travis-ci.org/Dogfalo/materialize.svg?branch=master" alt="Travis CI badge">
|
||||
</a>
|
||||
<a href="https://badge.fury.io/js/materialize-css">
|
||||
<img src="https://badge.fury.io/js/materialize-css.svg" alt="npm version badge">
|
||||
</a>
|
||||
<a href="https://cdnjs.com/libraries/materialize">
|
||||
<img src="https://img.shields.io/cdnjs/v/materialize.svg" alt="CDNJS version badge">
|
||||
</a>
|
||||
<a href="https://david-dm.org/Dogfalo/materialize">
|
||||
<img src="https://david-dm.org/Dogfalo/materialize/status.svg" alt="dependencies Status badge">
|
||||
</a>
|
||||
<a href="https://david-dm.org/Dogfalo/materialize#info=devDependencies">
|
||||
<img src="https://david-dm.org/Dogfalo/materialize/dev-status.svg" alt="devDependency Status badge">
|
||||
</a>
|
||||
<a href="https://gitter.im/Dogfalo/materialize">
|
||||
<img src="https://badges.gitter.im/Join%20Chat.svg" alt="Gitter badge">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Table of Contents
|
||||
- [Quickstart](#quickstart)
|
||||
- [Documentation](#documentation)
|
||||
- [Supported Browsers](#supported-browsers)
|
||||
- [Changelog](#changelog)
|
||||
- [Testing](#testing)
|
||||
- [Contributing](#contributing)
|
||||
- [Copyright and license](#copyright-and-license)
|
||||
|
||||
## Quickstart:
|
||||
Read the [getting started guide](http://materializecss.com/getting-started.html) for more information on how to use materialize.
|
||||
|
||||
- [Download the latest release](https://github.com/Dogfalo/materialize/releases/latest) of materialize directly from GitHub. ([Beta](https://github.com/Dogfalo/materialize/releases/))
|
||||
- Clone the repo: `git clone https://github.com/Dogfalo/materialize.git` (Beta: `git clone -b v1-dev https://github.com/Dogfalo/materialize.git`)
|
||||
- Include the files via [cdnjs](https://cdnjs.com/libraries/materialize). More [here](http://materializecss.com/getting-started.html). ([Beta](https://cdnjs.com/libraries/materialize/1.0.0-beta))
|
||||
- Install with [npm](https://www.npmjs.com): `npm install materialize-css` (Beta: `npm install materialize-css@next`)
|
||||
- Install with [Bower](https://bower.io): `bower install materialize` ([DEPRECATED](https://bower.io/blog/2017/how-to-migrate-away-from-bower/))
|
||||
- Install with [Atmosphere](https://atmospherejs.com): `meteor add materialize:materialize` (Beta: `meteor add materialize:materialize@=1.0.0-beta`)
|
||||
|
||||
## Documentation
|
||||
The documentation can be found at <http://materializecss.com>. To run the documentation locally on your machine, you need [Node.js](https://nodejs.org/en/) installed on your computer.
|
||||
|
||||
### Running documentation locally
|
||||
Run these commands to set up the documentation:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Dogfalo/materialize
|
||||
cd materialize
|
||||
npm install
|
||||
```
|
||||
|
||||
Then run `grunt monitor` to compile the documentation. When it finishes, open a new browser window and navigate to `localhost:8000`. We use [BrowserSync](https://www.browsersync.io/) to display the documentation.
|
||||
|
||||
### Documentation for previous releases
|
||||
Previous releases and their documentation are available for [download](https://github.com/Dogfalo/materialize/releases).
|
||||
|
||||
## Supported Browsers:
|
||||
Materialize is compatible with:
|
||||
|
||||
- Chrome 35+
|
||||
- Firefox 31+
|
||||
- Safari 9+
|
||||
- Opera
|
||||
- Edge
|
||||
- IE 11+
|
||||
|
||||
## Changelog
|
||||
For changelogs, check out [the Releases section of materialize](https://github.com/Dogfalo/materialize/releases) or the [CHANGELOG.md](CHANGELOG.md).
|
||||
|
||||
## Testing
|
||||
We use Jasmine as our testing framework and we're trying to write a robust test suite for our components. If you want to help, [here's a starting guide on how to write tests in Jasmine](CONTRIBUTING.md#jasmine-testing-guide).
|
||||
|
||||
## Contributing
|
||||
Check out the [CONTRIBUTING document](CONTRIBUTING.md) in the root of the repository to learn how you can contribute. You can also browse the [help-wanted](https://github.com/Dogfalo/materialize/labels/help-wanted) tag in our issue tracker to find things to do.
|
||||
|
||||
## Copyright and license
|
||||
Code Copyright 2018 Materialize. Code released under the MIT license.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
|
|
@ -0,0 +1,406 @@
|
|||
/*!
|
||||
* Materialize 1.0.0 (http://materializecss.com)
|
||||
* Copyright 2014-2015 Materialize
|
||||
* MIT License (https://raw.githubusercontent.com/Dogfalo/materialize/master/LICENSE)
|
||||
*/
|
||||
|
||||
/*! nouislider - 9.1.0 - 2016-12-10 16:00:32 */
|
||||
|
||||
|
||||
/* Functional styling;
|
||||
* These styles are required for noUiSlider to function.
|
||||
* You don't need to change these rules to apply your design.
|
||||
*/
|
||||
.noUi-target,
|
||||
.noUi-target * {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
-webkit-user-select: none;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
-ms-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.noUi-target {
|
||||
position: relative;
|
||||
direction: ltr;
|
||||
}
|
||||
.noUi-base {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 1; /* Fix 401 */
|
||||
}
|
||||
.noUi-connect {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.noUi-origin {
|
||||
position: absolute;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
.noUi-handle {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.noUi-state-tap .noUi-connect,
|
||||
.noUi-state-tap .noUi-origin {
|
||||
-webkit-transition: top 0.25s, right 0.25s, bottom 0.25s, left 0.25s;
|
||||
transition: top 0.25s, right 0.25s, bottom 0.25s, left 0.25s;
|
||||
}
|
||||
.noUi-state-drag * {
|
||||
cursor: inherit !important;
|
||||
}
|
||||
|
||||
.noUi-handle-touch-area{
|
||||
position: relative;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
left: -15px;
|
||||
top: -15px;
|
||||
}
|
||||
/* Painting and performance;
|
||||
* Browsers can paint handles in their own layer.
|
||||
*/
|
||||
.noUi-base,
|
||||
.noUi-handle {
|
||||
-webkit-transform: translate3d(0,0,0);
|
||||
transform: translate3d(0,0,0);
|
||||
}
|
||||
|
||||
/* Slider size and handle placement;
|
||||
*/
|
||||
.noUi-horizontal {
|
||||
height: 18px;
|
||||
}
|
||||
.noUi-horizontal .noUi-handle {
|
||||
width: 34px;
|
||||
height: 28px;
|
||||
left: -17px;
|
||||
top: -6px;
|
||||
}
|
||||
.noUi-vertical {
|
||||
width: 18px;
|
||||
}
|
||||
.noUi-vertical .noUi-handle {
|
||||
width: 28px;
|
||||
height: 34px;
|
||||
left: -6px;
|
||||
top: -17px;
|
||||
}
|
||||
|
||||
/* Styling;
|
||||
*/
|
||||
.noUi-target {
|
||||
background: #cdcdcd;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.noUi-connect {
|
||||
background: #26A69A;
|
||||
-webkit-transition: background 450ms;
|
||||
transition: background 450ms;
|
||||
}
|
||||
|
||||
/* Handles and cursors;
|
||||
*/
|
||||
.noUi-draggable {
|
||||
cursor: ew-resize;
|
||||
}
|
||||
.noUi-vertical .noUi-draggable {
|
||||
cursor: ns-resize;
|
||||
}
|
||||
.noUi-handle {
|
||||
border: 1px solid #D9D9D9;
|
||||
border-radius: 3px;
|
||||
background: #FFF;
|
||||
cursor: default;
|
||||
box-shadow: inset 0 0 1px #FFF,
|
||||
inset 0 1px 7px #EBEBEB,
|
||||
0 3px 6px -3px #BBB;
|
||||
}
|
||||
.noUi-active {
|
||||
box-shadow: inset 0 0 1px #FFF,
|
||||
inset 0 1px 7px #DDD,
|
||||
0 3px 6px -3px #BBB;
|
||||
}
|
||||
|
||||
/* Handle stripes
|
||||
*/
|
||||
.noUi-handle:before,
|
||||
.noUi-handle:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 14px;
|
||||
width: 1px;
|
||||
background: #E8E7E6;
|
||||
left: 14px;
|
||||
top: 6px;
|
||||
}
|
||||
.noUi-handle:after {
|
||||
left: 17px;
|
||||
}
|
||||
.noUi-vertical .noUi-handle:before,
|
||||
.noUi-vertical .noUi-handle:after {
|
||||
width: 14px;
|
||||
height: 1px;
|
||||
left: 6px;
|
||||
top: 14px;
|
||||
}
|
||||
.noUi-vertical .noUi-handle:after {
|
||||
top: 17px;
|
||||
}
|
||||
|
||||
/* Disabled state;
|
||||
*/
|
||||
|
||||
[disabled] .noUi-connect {
|
||||
background: #B8B8B8;
|
||||
}
|
||||
[disabled].noUi-target,
|
||||
[disabled].noUi-handle,
|
||||
[disabled] .noUi-handle {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
/* Base;
|
||||
*
|
||||
*/
|
||||
.noUi-pips,
|
||||
.noUi-pips * {
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.noUi-pips {
|
||||
position: absolute;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* Values;
|
||||
*
|
||||
*/
|
||||
.noUi-value {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
}
|
||||
.noUi-value-sub {
|
||||
color: #ccc;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* Markings;
|
||||
*
|
||||
*/
|
||||
.noUi-marker {
|
||||
position: absolute;
|
||||
background: #CCC;
|
||||
}
|
||||
.noUi-marker-sub {
|
||||
background: #AAA;
|
||||
}
|
||||
.noUi-marker-large {
|
||||
background: #AAA;
|
||||
}
|
||||
|
||||
/* Horizontal layout;
|
||||
*
|
||||
*/
|
||||
.noUi-pips-horizontal {
|
||||
padding: 10px 0;
|
||||
height: 80px;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.noUi-value-horizontal {
|
||||
-webkit-transform: translate3d(-50%,50%,0);
|
||||
transform: translate3d(-50%,50%,0);
|
||||
}
|
||||
|
||||
.noUi-marker-horizontal.noUi-marker {
|
||||
margin-left: -1px;
|
||||
width: 2px;
|
||||
height: 5px;
|
||||
}
|
||||
.noUi-marker-horizontal.noUi-marker-sub {
|
||||
height: 10px;
|
||||
}
|
||||
.noUi-marker-horizontal.noUi-marker-large {
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
/* Vertical layout;
|
||||
*
|
||||
*/
|
||||
.noUi-pips-vertical {
|
||||
padding: 0 10px;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 100%;
|
||||
}
|
||||
.noUi-value-vertical {
|
||||
-webkit-transform: translate3d(0,50%,0);
|
||||
transform: translate3d(0,50%,0);
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.noUi-marker-vertical.noUi-marker {
|
||||
width: 5px;
|
||||
height: 2px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
.noUi-marker-vertical.noUi-marker-sub {
|
||||
width: 10px;
|
||||
}
|
||||
.noUi-marker-vertical.noUi-marker-large {
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.noUi-tooltip {
|
||||
display: block;
|
||||
position: absolute;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
.noUi-horizontal .noUi-tooltip {
|
||||
-webkit-transform: translate(-50%, 0);
|
||||
transform: translate(-50%, 0);
|
||||
left: 50%;
|
||||
bottom: 120%;
|
||||
}
|
||||
.noUi-vertical .noUi-tooltip {
|
||||
-webkit-transform: translate(0, -50%);
|
||||
transform: translate(0, -50%);
|
||||
top: 50%;
|
||||
right: 120%;
|
||||
}
|
||||
|
||||
/* Materialize Styles */
|
||||
.noUi-target {
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.noUi-horizontal {
|
||||
height: 3px;
|
||||
}
|
||||
|
||||
.noUi-vertical {
|
||||
height: 100%;
|
||||
width: 3px;
|
||||
}
|
||||
|
||||
.noUi-horizontal .noUi-handle,
|
||||
.noUi-vertical .noUi-handle {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
box-shadow: none;
|
||||
background-color: #26A69A;
|
||||
border: none;
|
||||
left: -5px;
|
||||
top: -6px;
|
||||
transition: width .2s cubic-bezier(0.215, 0.610, 0.355, 1.000),
|
||||
height .2s cubic-bezier(0.215, 0.610, 0.355, 1.000),
|
||||
left .2s cubic-bezier(0.215, 0.610, 0.355, 1.000),
|
||||
top .2s cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
}
|
||||
.noUi-handle:before {
|
||||
content: none;
|
||||
}
|
||||
.noUi-handle:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.noUi-target .noUi-active.noUi-handle {
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.noUi-target.noUi-horizontal .noUi-tooltip {
|
||||
position: absolute;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
top: -17px;
|
||||
left: -2px;
|
||||
background-color: #26A69A;
|
||||
border-radius: 50%;
|
||||
transition: border-radius .25s cubic-bezier(0.215, 0.610, 0.355, 1.000),
|
||||
transform .25s cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
transform: scale(.5) rotate(-45deg);
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
.noUi-target.noUi-horizontal .noUi-active .noUi-tooltip {
|
||||
border-radius: 15px 15px 15px 0;
|
||||
transform: rotate(-45deg) translate(23px, -25px);
|
||||
}
|
||||
|
||||
.noUi-tooltip span {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: -1px;
|
||||
transition: opacity .25s cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
}
|
||||
|
||||
.noUi-horizontal .noUi-tooltip span {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.noUi-vertical .noUi-tooltip span {
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
|
||||
|
||||
.noUi-target.noUi-vertical .noUi-tooltip {
|
||||
position: absolute;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
top: -17px;
|
||||
left: -2px;
|
||||
background-color: #26A69A;
|
||||
border-radius: 50%;
|
||||
transition: border-radius .25s cubic-bezier(0.215, 0.610, 0.355, 1.000),
|
||||
transform .25s cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
transform: scale(.5) rotate(-45deg);
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
.noUi-target.noUi-vertical .noUi-active .noUi-tooltip {
|
||||
border-radius: 15px 15px 15px 0;
|
||||
transform: rotate(-135deg) translate(35px, -10px);
|
||||
}
|
||||
.noUi-vertical .noUi-tooltip span {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
transform: rotate(135deg);
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
left: -1px;
|
||||
transition: opacity .25s cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
}
|
||||
|
||||
.noUi-horizontal .noUi-active .noUi-tooltip span,
|
||||
.noUi-vertical .noUi-active .noUi-tooltip span {
|
||||
opacity: 1;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
v2.2.0
|
||||
2017 Julian Garnier
|
||||
Released under the MIT license
|
||||
*/
|
||||
var $jscomp={scope:{}};$jscomp.defineProperty="function"==typeof Object.defineProperties?Object.defineProperty:function(e,r,p){if(p.get||p.set)throw new TypeError("ES3 does not support getters and setters.");e!=Array.prototype&&e!=Object.prototype&&(e[r]=p.value)};$jscomp.getGlobal=function(e){return"undefined"!=typeof window&&window===e?e:"undefined"!=typeof global&&null!=global?global:e};$jscomp.global=$jscomp.getGlobal(this);$jscomp.SYMBOL_PREFIX="jscomp_symbol_";
|
||||
$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.symbolCounter_=0;$jscomp.Symbol=function(e){return $jscomp.SYMBOL_PREFIX+(e||"")+$jscomp.symbolCounter_++};
|
||||
$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var e=$jscomp.global.Symbol.iterator;e||(e=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[e]&&$jscomp.defineProperty(Array.prototype,e,{configurable:!0,writable:!0,value:function(){return $jscomp.arrayIterator(this)}});$jscomp.initSymbolIterator=function(){}};$jscomp.arrayIterator=function(e){var r=0;return $jscomp.iteratorPrototype(function(){return r<e.length?{done:!1,value:e[r++]}:{done:!0}})};
|
||||
$jscomp.iteratorPrototype=function(e){$jscomp.initSymbolIterator();e={next:e};e[$jscomp.global.Symbol.iterator]=function(){return this};return e};$jscomp.array=$jscomp.array||{};$jscomp.iteratorFromArray=function(e,r){$jscomp.initSymbolIterator();e instanceof String&&(e+="");var p=0,m={next:function(){if(p<e.length){var u=p++;return{value:r(u,e[u]),done:!1}}m.next=function(){return{done:!0,value:void 0}};return m.next()}};m[Symbol.iterator]=function(){return m};return m};
|
||||
$jscomp.polyfill=function(e,r,p,m){if(r){p=$jscomp.global;e=e.split(".");for(m=0;m<e.length-1;m++){var u=e[m];u in p||(p[u]={});p=p[u]}e=e[e.length-1];m=p[e];r=r(m);r!=m&&null!=r&&$jscomp.defineProperty(p,e,{configurable:!0,writable:!0,value:r})}};$jscomp.polyfill("Array.prototype.keys",function(e){return e?e:function(){return $jscomp.iteratorFromArray(this,function(e){return e})}},"es6-impl","es3");var $jscomp$this=this;
|
||||
(function(r){M.anime=r()})(function(){function e(a){if(!h.col(a))try{return document.querySelectorAll(a)}catch(c){}}function r(a,c){for(var d=a.length,b=2<=arguments.length?arguments[1]:void 0,f=[],n=0;n<d;n++)if(n in a){var k=a[n];c.call(b,k,n,a)&&f.push(k)}return f}function p(a){return a.reduce(function(a,d){return a.concat(h.arr(d)?p(d):d)},[])}function m(a){if(h.arr(a))return a;
|
||||
h.str(a)&&(a=e(a)||a);return a instanceof NodeList||a instanceof HTMLCollection?[].slice.call(a):[a]}function u(a,c){return a.some(function(a){return a===c})}function C(a){var c={},d;for(d in a)c[d]=a[d];return c}function D(a,c){var d=C(a),b;for(b in a)d[b]=c.hasOwnProperty(b)?c[b]:a[b];return d}function z(a,c){var d=C(a),b;for(b in c)d[b]=h.und(a[b])?c[b]:a[b];return d}function T(a){a=a.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,function(a,c,d,k){return c+c+d+d+k+k});var c=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(a);
|
||||
a=parseInt(c[1],16);var d=parseInt(c[2],16),c=parseInt(c[3],16);return"rgba("+a+","+d+","+c+",1)"}function U(a){function c(a,c,b){0>b&&(b+=1);1<b&&--b;return b<1/6?a+6*(c-a)*b:.5>b?c:b<2/3?a+(c-a)*(2/3-b)*6:a}var d=/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(a)||/hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(a);a=parseInt(d[1])/360;var b=parseInt(d[2])/100,f=parseInt(d[3])/100,d=d[4]||1;if(0==b)f=b=a=f;else{var n=.5>f?f*(1+b):f+b-f*b,k=2*f-n,f=c(k,n,a+1/3),b=c(k,n,a);a=c(k,n,a-1/3)}return"rgba("+
|
||||
255*f+","+255*b+","+255*a+","+d+")"}function y(a){if(a=/([\+\-]?[0-9#\.]+)(%|px|pt|em|rem|in|cm|mm|ex|ch|pc|vw|vh|vmin|vmax|deg|rad|turn)?$/.exec(a))return a[2]}function V(a){if(-1<a.indexOf("translate")||"perspective"===a)return"px";if(-1<a.indexOf("rotate")||-1<a.indexOf("skew"))return"deg"}function I(a,c){return h.fnc(a)?a(c.target,c.id,c.total):a}function E(a,c){if(c in a.style)return getComputedStyle(a).getPropertyValue(c.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase())||"0"}function J(a,c){if(h.dom(a)&&
|
||||
u(W,c))return"transform";if(h.dom(a)&&(a.getAttribute(c)||h.svg(a)&&a[c]))return"attribute";if(h.dom(a)&&"transform"!==c&&E(a,c))return"css";if(null!=a[c])return"object"}function X(a,c){var d=V(c),d=-1<c.indexOf("scale")?1:0+d;a=a.style.transform;if(!a)return d;for(var b=[],f=[],n=[],k=/(\w+)\((.+?)\)/g;b=k.exec(a);)f.push(b[1]),n.push(b[2]);a=r(n,function(a,b){return f[b]===c});return a.length?a[0]:d}function K(a,c){switch(J(a,c)){case "transform":return X(a,c);case "css":return E(a,c);case "attribute":return a.getAttribute(c)}return a[c]||
|
||||
0}function L(a,c){var d=/^(\*=|\+=|-=)/.exec(a);if(!d)return a;var b=y(a)||0;c=parseFloat(c);a=parseFloat(a.replace(d[0],""));switch(d[0][0]){case "+":return c+a+b;case "-":return c-a+b;case "*":return c*a+b}}function F(a,c){return Math.sqrt(Math.pow(c.x-a.x,2)+Math.pow(c.y-a.y,2))}function M(a){a=a.points;for(var c=0,d,b=0;b<a.numberOfItems;b++){var f=a.getItem(b);0<b&&(c+=F(d,f));d=f}return c}function N(a){if(a.getTotalLength)return a.getTotalLength();switch(a.tagName.toLowerCase()){case "circle":return 2*
|
||||
Math.PI*a.getAttribute("r");case "rect":return 2*a.getAttribute("width")+2*a.getAttribute("height");case "line":return F({x:a.getAttribute("x1"),y:a.getAttribute("y1")},{x:a.getAttribute("x2"),y:a.getAttribute("y2")});case "polyline":return M(a);case "polygon":var c=a.points;return M(a)+F(c.getItem(c.numberOfItems-1),c.getItem(0))}}function Y(a,c){function d(b){b=void 0===b?0:b;return a.el.getPointAtLength(1<=c+b?c+b:0)}var b=d(),f=d(-1),n=d(1);switch(a.property){case "x":return b.x;case "y":return b.y;
|
||||
case "angle":return 180*Math.atan2(n.y-f.y,n.x-f.x)/Math.PI}}function O(a,c){var d=/-?\d*\.?\d+/g,b;b=h.pth(a)?a.totalLength:a;if(h.col(b))if(h.rgb(b)){var f=/rgb\((\d+,\s*[\d]+,\s*[\d]+)\)/g.exec(b);b=f?"rgba("+f[1]+",1)":b}else b=h.hex(b)?T(b):h.hsl(b)?U(b):void 0;else f=(f=y(b))?b.substr(0,b.length-f.length):b,b=c&&!/\s/g.test(b)?f+c:f;b+="";return{original:b,numbers:b.match(d)?b.match(d).map(Number):[0],strings:h.str(a)||c?b.split(d):[]}}function P(a){a=a?p(h.arr(a)?a.map(m):m(a)):[];return r(a,
|
||||
function(a,d,b){return b.indexOf(a)===d})}function Z(a){var c=P(a);return c.map(function(a,b){return{target:a,id:b,total:c.length}})}function aa(a,c){var d=C(c);if(h.arr(a)){var b=a.length;2!==b||h.obj(a[0])?h.fnc(c.duration)||(d.duration=c.duration/b):a={value:a}}return m(a).map(function(a,b){b=b?0:c.delay;a=h.obj(a)&&!h.pth(a)?a:{value:a};h.und(a.delay)&&(a.delay=b);return a}).map(function(a){return z(a,d)})}function ba(a,c){var d={},b;for(b in a){var f=I(a[b],c);h.arr(f)&&(f=f.map(function(a){return I(a,
|
||||
c)}),1===f.length&&(f=f[0]));d[b]=f}d.duration=parseFloat(d.duration);d.delay=parseFloat(d.delay);return d}function ca(a){return h.arr(a)?A.apply(this,a):Q[a]}function da(a,c){var d;return a.tweens.map(function(b){b=ba(b,c);var f=b.value,e=K(c.target,a.name),k=d?d.to.original:e,k=h.arr(f)?f[0]:k,w=L(h.arr(f)?f[1]:f,k),e=y(w)||y(k)||y(e);b.from=O(k,e);b.to=O(w,e);b.start=d?d.end:a.offset;b.end=b.start+b.delay+b.duration;b.easing=ca(b.easing);b.elasticity=(1E3-Math.min(Math.max(b.elasticity,1),999))/
|
||||
1E3;b.isPath=h.pth(f);b.isColor=h.col(b.from.original);b.isColor&&(b.round=1);return d=b})}function ea(a,c){return r(p(a.map(function(a){return c.map(function(b){var c=J(a.target,b.name);if(c){var d=da(b,a);b={type:c,property:b.name,animatable:a,tweens:d,duration:d[d.length-1].end,delay:d[0].delay}}else b=void 0;return b})})),function(a){return!h.und(a)})}function R(a,c,d,b){var f="delay"===a;return c.length?(f?Math.min:Math.max).apply(Math,c.map(function(b){return b[a]})):f?b.delay:d.offset+b.delay+
|
||||
b.duration}function fa(a){var c=D(ga,a),d=D(S,a),b=Z(a.targets),f=[],e=z(c,d),k;for(k in a)e.hasOwnProperty(k)||"targets"===k||f.push({name:k,offset:e.offset,tweens:aa(a[k],d)});a=ea(b,f);return z(c,{children:[],animatables:b,animations:a,duration:R("duration",a,c,d),delay:R("delay",a,c,d)})}function q(a){function c(){return window.Promise&&new Promise(function(a){return p=a})}function d(a){return g.reversed?g.duration-a:a}function b(a){for(var b=0,c={},d=g.animations,f=d.length;b<f;){var e=d[b],
|
||||
k=e.animatable,h=e.tweens,n=h.length-1,l=h[n];n&&(l=r(h,function(b){return a<b.end})[0]||l);for(var h=Math.min(Math.max(a-l.start-l.delay,0),l.duration)/l.duration,w=isNaN(h)?1:l.easing(h,l.elasticity),h=l.to.strings,p=l.round,n=[],m=void 0,m=l.to.numbers.length,t=0;t<m;t++){var x=void 0,x=l.to.numbers[t],q=l.from.numbers[t],x=l.isPath?Y(l.value,w*x):q+w*(x-q);p&&(l.isColor&&2<t||(x=Math.round(x*p)/p));n.push(x)}if(l=h.length)for(m=h[0],w=0;w<l;w++)p=h[w+1],t=n[w],isNaN(t)||(m=p?m+(t+p):m+(t+" "));
|
||||
else m=n[0];ha[e.type](k.target,e.property,m,c,k.id);e.currentValue=m;b++}if(b=Object.keys(c).length)for(d=0;d<b;d++)H||(H=E(document.body,"transform")?"transform":"-webkit-transform"),g.animatables[d].target.style[H]=c[d].join(" ");g.currentTime=a;g.progress=a/g.duration*100}function f(a){if(g[a])g[a](g)}function e(){g.remaining&&!0!==g.remaining&&g.remaining--}function k(a){var k=g.duration,n=g.offset,w=n+g.delay,r=g.currentTime,x=g.reversed,q=d(a);if(g.children.length){var u=g.children,v=u.length;
|
||||
if(q>=g.currentTime)for(var G=0;G<v;G++)u[G].seek(q);else for(;v--;)u[v].seek(q)}if(q>=w||!k)g.began||(g.began=!0,f("begin")),f("run");if(q>n&&q<k)b(q);else if(q<=n&&0!==r&&(b(0),x&&e()),q>=k&&r!==k||!k)b(k),x||e();f("update");a>=k&&(g.remaining?(t=h,"alternate"===g.direction&&(g.reversed=!g.reversed)):(g.pause(),g.completed||(g.completed=!0,f("complete"),"Promise"in window&&(p(),m=c()))),l=0)}a=void 0===a?{}:a;var h,t,l=0,p=null,m=c(),g=fa(a);g.reset=function(){var a=g.direction,c=g.loop;g.currentTime=
|
||||
0;g.progress=0;g.paused=!0;g.began=!1;g.completed=!1;g.reversed="reverse"===a;g.remaining="alternate"===a&&1===c?2:c;b(0);for(a=g.children.length;a--;)g.children[a].reset()};g.tick=function(a){h=a;t||(t=h);k((l+h-t)*q.speed)};g.seek=function(a){k(d(a))};g.pause=function(){var a=v.indexOf(g);-1<a&&v.splice(a,1);g.paused=!0};g.play=function(){g.paused&&(g.paused=!1,t=0,l=d(g.currentTime),v.push(g),B||ia())};g.reverse=function(){g.reversed=!g.reversed;t=0;l=d(g.currentTime)};g.restart=function(){g.pause();
|
||||
g.reset();g.play()};g.finished=m;g.reset();g.autoplay&&g.play();return g}var ga={update:void 0,begin:void 0,run:void 0,complete:void 0,loop:1,direction:"normal",autoplay:!0,offset:0},S={duration:1E3,delay:0,easing:"easeOutElastic",elasticity:500,round:0},W="translateX translateY translateZ rotate rotateX rotateY rotateZ scale scaleX scaleY scaleZ skewX skewY perspective".split(" "),H,h={arr:function(a){return Array.isArray(a)},obj:function(a){return-1<Object.prototype.toString.call(a).indexOf("Object")},
|
||||
pth:function(a){return h.obj(a)&&a.hasOwnProperty("totalLength")},svg:function(a){return a instanceof SVGElement},dom:function(a){return a.nodeType||h.svg(a)},str:function(a){return"string"===typeof a},fnc:function(a){return"function"===typeof a},und:function(a){return"undefined"===typeof a},hex:function(a){return/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a)},rgb:function(a){return/^rgb/.test(a)},hsl:function(a){return/^hsl/.test(a)},col:function(a){return h.hex(a)||h.rgb(a)||h.hsl(a)}},A=function(){function a(a,
|
||||
d,b){return(((1-3*b+3*d)*a+(3*b-6*d))*a+3*d)*a}return function(c,d,b,f){if(0<=c&&1>=c&&0<=b&&1>=b){var e=new Float32Array(11);if(c!==d||b!==f)for(var k=0;11>k;++k)e[k]=a(.1*k,c,b);return function(k){if(c===d&&b===f)return k;if(0===k)return 0;if(1===k)return 1;for(var h=0,l=1;10!==l&&e[l]<=k;++l)h+=.1;--l;var l=h+(k-e[l])/(e[l+1]-e[l])*.1,n=3*(1-3*b+3*c)*l*l+2*(3*b-6*c)*l+3*c;if(.001<=n){for(h=0;4>h;++h){n=3*(1-3*b+3*c)*l*l+2*(3*b-6*c)*l+3*c;if(0===n)break;var m=a(l,c,b)-k,l=l-m/n}k=l}else if(0===
|
||||
n)k=l;else{var l=h,h=h+.1,g=0;do m=l+(h-l)/2,n=a(m,c,b)-k,0<n?h=m:l=m;while(1e-7<Math.abs(n)&&10>++g);k=m}return a(k,d,f)}}}}(),Q=function(){function a(a,b){return 0===a||1===a?a:-Math.pow(2,10*(a-1))*Math.sin(2*(a-1-b/(2*Math.PI)*Math.asin(1))*Math.PI/b)}var c="Quad Cubic Quart Quint Sine Expo Circ Back Elastic".split(" "),d={In:[[.55,.085,.68,.53],[.55,.055,.675,.19],[.895,.03,.685,.22],[.755,.05,.855,.06],[.47,0,.745,.715],[.95,.05,.795,.035],[.6,.04,.98,.335],[.6,-.28,.735,.045],a],Out:[[.25,
|
||||
.46,.45,.94],[.215,.61,.355,1],[.165,.84,.44,1],[.23,1,.32,1],[.39,.575,.565,1],[.19,1,.22,1],[.075,.82,.165,1],[.175,.885,.32,1.275],function(b,c){return 1-a(1-b,c)}],InOut:[[.455,.03,.515,.955],[.645,.045,.355,1],[.77,0,.175,1],[.86,0,.07,1],[.445,.05,.55,.95],[1,0,0,1],[.785,.135,.15,.86],[.68,-.55,.265,1.55],function(b,c){return.5>b?a(2*b,c)/2:1-a(-2*b+2,c)/2}]},b={linear:A(.25,.25,.75,.75)},f={},e;for(e in d)f.type=e,d[f.type].forEach(function(a){return function(d,f){b["ease"+a.type+c[f]]=h.fnc(d)?
|
||||
d:A.apply($jscomp$this,d)}}(f)),f={type:f.type};return b}(),ha={css:function(a,c,d){return a.style[c]=d},attribute:function(a,c,d){return a.setAttribute(c,d)},object:function(a,c,d){return a[c]=d},transform:function(a,c,d,b,f){b[f]||(b[f]=[]);b[f].push(c+"("+d+")")}},v=[],B=0,ia=function(){function a(){B=requestAnimationFrame(c)}function c(c){var b=v.length;if(b){for(var d=0;d<b;)v[d]&&v[d].tick(c),d++;a()}else cancelAnimationFrame(B),B=0}return a}();q.version="2.2.0";q.speed=1;q.running=v;q.remove=
|
||||
function(a){a=P(a);for(var c=v.length;c--;)for(var d=v[c],b=d.animations,f=b.length;f--;)u(a,b[f].animatable.target)&&(b.splice(f,1),b.length||d.pause())};q.getValue=K;q.path=function(a,c){var d=h.str(a)?e(a)[0]:a,b=c||100;return function(a){return{el:d,property:a,totalLength:N(d)*(b/100)}}};q.setDashoffset=function(a){var c=N(a);a.setAttribute("stroke-dasharray",c);return c};q.bezier=A;q.easings=Q;q.timeline=function(a){var c=q(a);c.pause();c.duration=0;c.add=function(d){c.children.forEach(function(a){a.began=
|
||||
!0;a.completed=!0});m(d).forEach(function(b){var d=z(b,D(S,a||{}));d.targets=d.targets||a.targets;b=c.duration;var e=d.offset;d.autoplay=!1;d.direction=c.direction;d.offset=h.und(e)?b:L(e,b);c.began=!0;c.completed=!0;c.seek(d.offset);d=q(d);d.began=!0;d.completed=!0;d.duration>b&&(c.duration=d.duration);c.children.push(d)});c.seek(0);c.reset();c.autoplay&&c.restart();return c};return c};q.random=function(a,c){return Math.floor(Math.random()*(c-a+1))+a};return q});
|
||||
|
|
@ -0,0 +1,450 @@
|
|||
(function($) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
data: {}, // Autocomplete data set
|
||||
limit: Infinity, // Limit of results the autocomplete shows
|
||||
onAutocomplete: null, // Callback for when autocompleted
|
||||
minLength: 1, // Min characters before autocomplete starts
|
||||
sortFunction: function(a, b, inputString) {
|
||||
// Sort function for sorting autocomplete results
|
||||
return a.indexOf(inputString) - b.indexOf(inputString);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class Autocomplete extends Component {
|
||||
/**
|
||||
* Construct Autocomplete instance
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Autocomplete, el, options);
|
||||
|
||||
this.el.M_Autocomplete = this;
|
||||
|
||||
/**
|
||||
* Options for the autocomplete
|
||||
* @member Autocomplete#options
|
||||
* @prop {Number} duration
|
||||
* @prop {Number} dist
|
||||
* @prop {number} shift
|
||||
* @prop {number} padding
|
||||
* @prop {Boolean} fullWidth
|
||||
* @prop {Boolean} indicators
|
||||
* @prop {Boolean} noWrap
|
||||
* @prop {Function} onCycleTo
|
||||
*/
|
||||
this.options = $.extend({}, Autocomplete.defaults, options);
|
||||
|
||||
// Setup
|
||||
this.isOpen = false;
|
||||
this.count = 0;
|
||||
this.activeIndex = -1;
|
||||
this.oldVal;
|
||||
this.$inputField = this.$el.closest('.input-field');
|
||||
this.$active = $();
|
||||
this._mousedown = false;
|
||||
this._setupDropdown();
|
||||
|
||||
this._setupEventHandlers();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Autocomplete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this._removeDropdown();
|
||||
this.el.M_Autocomplete = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleInputBlurBound = this._handleInputBlur.bind(this);
|
||||
this._handleInputKeyupAndFocusBound = this._handleInputKeyupAndFocus.bind(this);
|
||||
this._handleInputKeydownBound = this._handleInputKeydown.bind(this);
|
||||
this._handleInputClickBound = this._handleInputClick.bind(this);
|
||||
this._handleContainerMousedownAndTouchstartBound = this._handleContainerMousedownAndTouchstart.bind(
|
||||
this
|
||||
);
|
||||
this._handleContainerMouseupAndTouchendBound = this._handleContainerMouseupAndTouchend.bind(
|
||||
this
|
||||
);
|
||||
|
||||
this.el.addEventListener('blur', this._handleInputBlurBound);
|
||||
this.el.addEventListener('keyup', this._handleInputKeyupAndFocusBound);
|
||||
this.el.addEventListener('focus', this._handleInputKeyupAndFocusBound);
|
||||
this.el.addEventListener('keydown', this._handleInputKeydownBound);
|
||||
this.el.addEventListener('click', this._handleInputClickBound);
|
||||
this.container.addEventListener(
|
||||
'mousedown',
|
||||
this._handleContainerMousedownAndTouchstartBound
|
||||
);
|
||||
this.container.addEventListener('mouseup', this._handleContainerMouseupAndTouchendBound);
|
||||
|
||||
if (typeof window.ontouchstart !== 'undefined') {
|
||||
this.container.addEventListener(
|
||||
'touchstart',
|
||||
this._handleContainerMousedownAndTouchstartBound
|
||||
);
|
||||
this.container.addEventListener('touchend', this._handleContainerMouseupAndTouchendBound);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
this.el.removeEventListener('blur', this._handleInputBlurBound);
|
||||
this.el.removeEventListener('keyup', this._handleInputKeyupAndFocusBound);
|
||||
this.el.removeEventListener('focus', this._handleInputKeyupAndFocusBound);
|
||||
this.el.removeEventListener('keydown', this._handleInputKeydownBound);
|
||||
this.el.removeEventListener('click', this._handleInputClickBound);
|
||||
this.container.removeEventListener(
|
||||
'mousedown',
|
||||
this._handleContainerMousedownAndTouchstartBound
|
||||
);
|
||||
this.container.removeEventListener('mouseup', this._handleContainerMouseupAndTouchendBound);
|
||||
|
||||
if (typeof window.ontouchstart !== 'undefined') {
|
||||
this.container.removeEventListener(
|
||||
'touchstart',
|
||||
this._handleContainerMousedownAndTouchstartBound
|
||||
);
|
||||
this.container.removeEventListener(
|
||||
'touchend',
|
||||
this._handleContainerMouseupAndTouchendBound
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup dropdown
|
||||
*/
|
||||
_setupDropdown() {
|
||||
this.container = document.createElement('ul');
|
||||
this.container.id = `autocomplete-options-${M.guid()}`;
|
||||
$(this.container).addClass('autocomplete-content dropdown-content');
|
||||
this.$inputField.append(this.container);
|
||||
this.el.setAttribute('data-target', this.container.id);
|
||||
|
||||
this.dropdown = M.Dropdown.init(this.el, {
|
||||
autoFocus: false,
|
||||
closeOnClick: false,
|
||||
coverTrigger: false,
|
||||
onItemClick: (itemEl) => {
|
||||
this.selectOption($(itemEl));
|
||||
}
|
||||
});
|
||||
|
||||
// Sketchy removal of dropdown click handler
|
||||
this.el.removeEventListener('click', this.dropdown._handleClickBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove dropdown
|
||||
*/
|
||||
_removeDropdown() {
|
||||
this.container.parentNode.removeChild(this.container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Input Blur
|
||||
*/
|
||||
_handleInputBlur() {
|
||||
if (!this._mousedown) {
|
||||
this.close();
|
||||
this._resetAutocomplete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Input Keyup and Focus
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleInputKeyupAndFocus(e) {
|
||||
if (e.type === 'keyup') {
|
||||
Autocomplete._keydown = false;
|
||||
}
|
||||
|
||||
this.count = 0;
|
||||
let val = this.el.value.toLowerCase();
|
||||
|
||||
// Don't capture enter or arrow key usage.
|
||||
if (e.keyCode === 13 || e.keyCode === 38 || e.keyCode === 40) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the input isn't empty
|
||||
// Check if focus triggered by tab
|
||||
if (this.oldVal !== val && (M.tabPressed || e.type !== 'focus')) {
|
||||
this.open();
|
||||
}
|
||||
|
||||
// Update oldVal
|
||||
this.oldVal = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Input Keydown
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleInputKeydown(e) {
|
||||
Autocomplete._keydown = true;
|
||||
|
||||
// Arrow keys and enter key usage
|
||||
let keyCode = e.keyCode,
|
||||
liElement,
|
||||
numItems = $(this.container).children('li').length;
|
||||
|
||||
// select element on Enter
|
||||
if (keyCode === M.keys.ENTER && this.activeIndex >= 0) {
|
||||
liElement = $(this.container)
|
||||
.children('li')
|
||||
.eq(this.activeIndex);
|
||||
if (liElement.length) {
|
||||
this.selectOption(liElement);
|
||||
e.preventDefault();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Capture up and down key
|
||||
if (keyCode === M.keys.ARROW_UP || keyCode === M.keys.ARROW_DOWN) {
|
||||
e.preventDefault();
|
||||
|
||||
if (keyCode === M.keys.ARROW_UP && this.activeIndex > 0) {
|
||||
this.activeIndex--;
|
||||
}
|
||||
|
||||
if (keyCode === M.keys.ARROW_DOWN && this.activeIndex < numItems - 1) {
|
||||
this.activeIndex++;
|
||||
}
|
||||
|
||||
this.$active.removeClass('active');
|
||||
if (this.activeIndex >= 0) {
|
||||
this.$active = $(this.container)
|
||||
.children('li')
|
||||
.eq(this.activeIndex);
|
||||
this.$active.addClass('active');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Input Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleInputClick(e) {
|
||||
this.open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Container Mousedown and Touchstart
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleContainerMousedownAndTouchstart(e) {
|
||||
this._mousedown = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Container Mouseup and Touchend
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleContainerMouseupAndTouchend(e) {
|
||||
this._mousedown = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight partial match
|
||||
*/
|
||||
_highlight(string, $el) {
|
||||
let img = $el.find('img');
|
||||
let matchStart = $el
|
||||
.text()
|
||||
.toLowerCase()
|
||||
.indexOf('' + string.toLowerCase() + ''),
|
||||
matchEnd = matchStart + string.length - 1,
|
||||
beforeMatch = $el.text().slice(0, matchStart),
|
||||
matchText = $el.text().slice(matchStart, matchEnd + 1),
|
||||
afterMatch = $el.text().slice(matchEnd + 1);
|
||||
$el.html(
|
||||
`<span>${beforeMatch}<span class='highlight'>${matchText}</span>${afterMatch}</span>`
|
||||
);
|
||||
if (img.length) {
|
||||
$el.prepend(img);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset current element position
|
||||
*/
|
||||
_resetCurrentElement() {
|
||||
this.activeIndex = -1;
|
||||
this.$active.removeClass('active');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset autocomplete elements
|
||||
*/
|
||||
_resetAutocomplete() {
|
||||
$(this.container).empty();
|
||||
this._resetCurrentElement();
|
||||
this.oldVal = null;
|
||||
this.isOpen = false;
|
||||
this._mousedown = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select autocomplete option
|
||||
* @param {Element} el Autocomplete option list item element
|
||||
*/
|
||||
selectOption(el) {
|
||||
let text = el.text().trim();
|
||||
this.el.value = text;
|
||||
this.$el.trigger('change');
|
||||
this._resetAutocomplete();
|
||||
this.close();
|
||||
|
||||
// Handle onAutocomplete callback.
|
||||
if (typeof this.options.onAutocomplete === 'function') {
|
||||
this.options.onAutocomplete.call(this, text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render dropdown content
|
||||
* @param {Object} data data set
|
||||
* @param {String} val current input value
|
||||
*/
|
||||
_renderDropdown(data, val) {
|
||||
this._resetAutocomplete();
|
||||
|
||||
let matchingData = [];
|
||||
|
||||
// Gather all matching data
|
||||
for (let key in data) {
|
||||
if (data.hasOwnProperty(key) && key.toLowerCase().indexOf(val) !== -1) {
|
||||
// Break if past limit
|
||||
if (this.count >= this.options.limit) {
|
||||
break;
|
||||
}
|
||||
|
||||
let entry = {
|
||||
data: data[key],
|
||||
key: key
|
||||
};
|
||||
matchingData.push(entry);
|
||||
|
||||
this.count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort
|
||||
if (this.options.sortFunction) {
|
||||
let sortFunctionBound = (a, b) => {
|
||||
return this.options.sortFunction(
|
||||
a.key.toLowerCase(),
|
||||
b.key.toLowerCase(),
|
||||
val.toLowerCase()
|
||||
);
|
||||
};
|
||||
matchingData.sort(sortFunctionBound);
|
||||
}
|
||||
|
||||
// Render
|
||||
for (let i = 0; i < matchingData.length; i++) {
|
||||
let entry = matchingData[i];
|
||||
let $autocompleteOption = $('<li></li>');
|
||||
if (!!entry.data) {
|
||||
$autocompleteOption.append(
|
||||
`<img src="${entry.data}" class="right circle"><span>${entry.key}</span>`
|
||||
);
|
||||
} else {
|
||||
$autocompleteOption.append('<span>' + entry.key + '</span>');
|
||||
}
|
||||
|
||||
$(this.container).append($autocompleteOption);
|
||||
this._highlight(val, $autocompleteOption);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open Autocomplete Dropdown
|
||||
*/
|
||||
open() {
|
||||
let val = this.el.value.toLowerCase();
|
||||
|
||||
this._resetAutocomplete();
|
||||
|
||||
if (val.length >= this.options.minLength) {
|
||||
this.isOpen = true;
|
||||
this._renderDropdown(this.options.data, val);
|
||||
}
|
||||
|
||||
// Open dropdown
|
||||
if (!this.dropdown.isOpen) {
|
||||
this.dropdown.open();
|
||||
} else {
|
||||
// Recalculate dropdown when its already open
|
||||
this.dropdown.recalculateDimensions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close Autocomplete Dropdown
|
||||
*/
|
||||
close() {
|
||||
this.dropdown.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Data
|
||||
* @param {Object} data
|
||||
*/
|
||||
updateData(data) {
|
||||
let val = this.el.value.toLowerCase();
|
||||
this.options.data = data;
|
||||
|
||||
if (this.isOpen) {
|
||||
this._renderDropdown(data, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof Autocomplete
|
||||
*/
|
||||
Autocomplete._keydown = false;
|
||||
|
||||
M.Autocomplete = Autocomplete;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Autocomplete, 'autocomplete', 'M_Autocomplete');
|
||||
}
|
||||
})(cash);
|
||||
|
|
@ -0,0 +1,354 @@
|
|||
(function($, anim) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
direction: 'top',
|
||||
hoverEnabled: true,
|
||||
toolbarEnabled: false
|
||||
};
|
||||
|
||||
$.fn.reverse = [].reverse;
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class FloatingActionButton extends Component {
|
||||
/**
|
||||
* Construct FloatingActionButton instance
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(FloatingActionButton, el, options);
|
||||
|
||||
this.el.M_FloatingActionButton = this;
|
||||
|
||||
/**
|
||||
* Options for the fab
|
||||
* @member FloatingActionButton#options
|
||||
* @prop {Boolean} [direction] - Direction fab menu opens
|
||||
* @prop {Boolean} [hoverEnabled=true] - Enable hover vs click
|
||||
* @prop {Boolean} [toolbarEnabled=false] - Enable toolbar transition
|
||||
*/
|
||||
this.options = $.extend({}, FloatingActionButton.defaults, options);
|
||||
|
||||
this.isOpen = false;
|
||||
this.$anchor = this.$el.children('a').first();
|
||||
this.$menu = this.$el.children('ul').first();
|
||||
this.$floatingBtns = this.$el.find('ul .btn-floating');
|
||||
this.$floatingBtnsReverse = this.$el.find('ul .btn-floating').reverse();
|
||||
this.offsetY = 0;
|
||||
this.offsetX = 0;
|
||||
|
||||
this.$el.addClass(`direction-${this.options.direction}`);
|
||||
if (this.options.direction === 'top') {
|
||||
this.offsetY = 40;
|
||||
} else if (this.options.direction === 'right') {
|
||||
this.offsetX = -40;
|
||||
} else if (this.options.direction === 'bottom') {
|
||||
this.offsetY = -40;
|
||||
} else {
|
||||
this.offsetX = 40;
|
||||
}
|
||||
this._setupEventHandlers();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_FloatingActionButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this.el.M_FloatingActionButton = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleFABClickBound = this._handleFABClick.bind(this);
|
||||
this._handleOpenBound = this.open.bind(this);
|
||||
this._handleCloseBound = this.close.bind(this);
|
||||
|
||||
if (this.options.hoverEnabled && !this.options.toolbarEnabled) {
|
||||
this.el.addEventListener('mouseenter', this._handleOpenBound);
|
||||
this.el.addEventListener('mouseleave', this._handleCloseBound);
|
||||
} else {
|
||||
this.el.addEventListener('click', this._handleFABClickBound);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
if (this.options.hoverEnabled && !this.options.toolbarEnabled) {
|
||||
this.el.removeEventListener('mouseenter', this._handleOpenBound);
|
||||
this.el.removeEventListener('mouseleave', this._handleCloseBound);
|
||||
} else {
|
||||
this.el.removeEventListener('click', this._handleFABClickBound);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle FAB Click
|
||||
*/
|
||||
_handleFABClick() {
|
||||
if (this.isOpen) {
|
||||
this.close();
|
||||
} else {
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Document Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleDocumentClick(e) {
|
||||
if (!$(e.target).closest(this.$menu).length) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open FAB
|
||||
*/
|
||||
open() {
|
||||
if (this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.options.toolbarEnabled) {
|
||||
this._animateInToolbar();
|
||||
} else {
|
||||
this._animateInFAB();
|
||||
}
|
||||
this.isOpen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close FAB
|
||||
*/
|
||||
close() {
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.options.toolbarEnabled) {
|
||||
window.removeEventListener('scroll', this._handleCloseBound, true);
|
||||
document.body.removeEventListener('click', this._handleDocumentClickBound, true);
|
||||
this._animateOutToolbar();
|
||||
} else {
|
||||
this._animateOutFAB();
|
||||
}
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Classic FAB Menu open
|
||||
*/
|
||||
_animateInFAB() {
|
||||
this.$el.addClass('active');
|
||||
|
||||
let time = 0;
|
||||
this.$floatingBtnsReverse.each((el) => {
|
||||
anim({
|
||||
targets: el,
|
||||
opacity: 1,
|
||||
scale: [0.4, 1],
|
||||
translateY: [this.offsetY, 0],
|
||||
translateX: [this.offsetX, 0],
|
||||
duration: 275,
|
||||
delay: time,
|
||||
easing: 'easeInOutQuad'
|
||||
});
|
||||
time += 40;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Classic FAB Menu close
|
||||
*/
|
||||
_animateOutFAB() {
|
||||
this.$floatingBtnsReverse.each((el) => {
|
||||
anim.remove(el);
|
||||
anim({
|
||||
targets: el,
|
||||
opacity: 0,
|
||||
scale: 0.4,
|
||||
translateY: this.offsetY,
|
||||
translateX: this.offsetX,
|
||||
duration: 175,
|
||||
easing: 'easeOutQuad',
|
||||
complete: () => {
|
||||
this.$el.removeClass('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar transition Menu open
|
||||
*/
|
||||
_animateInToolbar() {
|
||||
let scaleFactor;
|
||||
let windowWidth = window.innerWidth;
|
||||
let windowHeight = window.innerHeight;
|
||||
let btnRect = this.el.getBoundingClientRect();
|
||||
let backdrop = $('<div class="fab-backdrop"></div>');
|
||||
let fabColor = this.$anchor.css('background-color');
|
||||
this.$anchor.append(backdrop);
|
||||
|
||||
this.offsetX = btnRect.left - windowWidth / 2 + btnRect.width / 2;
|
||||
this.offsetY = windowHeight - btnRect.bottom;
|
||||
scaleFactor = windowWidth / backdrop[0].clientWidth;
|
||||
this.btnBottom = btnRect.bottom;
|
||||
this.btnLeft = btnRect.left;
|
||||
this.btnWidth = btnRect.width;
|
||||
|
||||
// Set initial state
|
||||
this.$el.addClass('active');
|
||||
this.$el.css({
|
||||
'text-align': 'center',
|
||||
width: '100%',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
transform: 'translateX(' + this.offsetX + 'px)',
|
||||
transition: 'none'
|
||||
});
|
||||
this.$anchor.css({
|
||||
transform: 'translateY(' + -this.offsetY + 'px)',
|
||||
transition: 'none'
|
||||
});
|
||||
backdrop.css({
|
||||
'background-color': fabColor
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.$el.css({
|
||||
transform: '',
|
||||
transition:
|
||||
'transform .2s cubic-bezier(0.550, 0.085, 0.680, 0.530), background-color 0s linear .2s'
|
||||
});
|
||||
this.$anchor.css({
|
||||
overflow: 'visible',
|
||||
transform: '',
|
||||
transition: 'transform .2s'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.$el.css({
|
||||
overflow: 'hidden',
|
||||
'background-color': fabColor
|
||||
});
|
||||
backdrop.css({
|
||||
transform: 'scale(' + scaleFactor + ')',
|
||||
transition: 'transform .2s cubic-bezier(0.550, 0.055, 0.675, 0.190)'
|
||||
});
|
||||
this.$menu
|
||||
.children('li')
|
||||
.children('a')
|
||||
.css({
|
||||
opacity: 1
|
||||
});
|
||||
|
||||
// Scroll to close.
|
||||
this._handleDocumentClickBound = this._handleDocumentClick.bind(this);
|
||||
window.addEventListener('scroll', this._handleCloseBound, true);
|
||||
document.body.addEventListener('click', this._handleDocumentClickBound, true);
|
||||
}, 100);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar transition Menu close
|
||||
*/
|
||||
_animateOutToolbar() {
|
||||
let windowWidth = window.innerWidth;
|
||||
let windowHeight = window.innerHeight;
|
||||
let backdrop = this.$el.find('.fab-backdrop');
|
||||
let fabColor = this.$anchor.css('background-color');
|
||||
|
||||
this.offsetX = this.btnLeft - windowWidth / 2 + this.btnWidth / 2;
|
||||
this.offsetY = windowHeight - this.btnBottom;
|
||||
|
||||
// Hide backdrop
|
||||
this.$el.removeClass('active');
|
||||
this.$el.css({
|
||||
'background-color': 'transparent',
|
||||
transition: 'none'
|
||||
});
|
||||
this.$anchor.css({
|
||||
transition: 'none'
|
||||
});
|
||||
backdrop.css({
|
||||
transform: 'scale(0)',
|
||||
'background-color': fabColor
|
||||
});
|
||||
this.$menu
|
||||
.children('li')
|
||||
.children('a')
|
||||
.css({
|
||||
opacity: ''
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
backdrop.remove();
|
||||
|
||||
// Set initial state.
|
||||
this.$el.css({
|
||||
'text-align': '',
|
||||
width: '',
|
||||
bottom: '',
|
||||
left: '',
|
||||
overflow: '',
|
||||
'background-color': '',
|
||||
transform: 'translate3d(' + -this.offsetX + 'px,0,0)'
|
||||
});
|
||||
this.$anchor.css({
|
||||
overflow: '',
|
||||
transform: 'translate3d(0,' + this.offsetY + 'px,0)'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.$el.css({
|
||||
transform: 'translate3d(0,0,0)',
|
||||
transition: 'transform .2s'
|
||||
});
|
||||
this.$anchor.css({
|
||||
transform: 'translate3d(0,0,0)',
|
||||
transition: 'transform .2s cubic-bezier(0.550, 0.055, 0.675, 0.190)'
|
||||
});
|
||||
}, 20);
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
M.FloatingActionButton = FloatingActionButton;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(
|
||||
FloatingActionButton,
|
||||
'floatingActionButton',
|
||||
'M_FloatingActionButton'
|
||||
);
|
||||
}
|
||||
})(cash, M.anime);
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
(function($, anim) {
|
||||
$(document).on('click', '.card', function(e) {
|
||||
if ($(this).children('.card-reveal').length) {
|
||||
var $card = $(e.target).closest('.card');
|
||||
if ($card.data('initialOverflow') === undefined) {
|
||||
$card.data(
|
||||
'initialOverflow',
|
||||
$card.css('overflow') === undefined ? '' : $card.css('overflow')
|
||||
);
|
||||
}
|
||||
let $cardReveal = $(this).find('.card-reveal');
|
||||
if (
|
||||
$(e.target).is($('.card-reveal .card-title')) ||
|
||||
$(e.target).is($('.card-reveal .card-title i'))
|
||||
) {
|
||||
// Make Reveal animate down and display none
|
||||
anim({
|
||||
targets: $cardReveal[0],
|
||||
translateY: 0,
|
||||
duration: 225,
|
||||
easing: 'easeInOutQuad',
|
||||
complete: function(anim) {
|
||||
let el = anim.animatables[0].target;
|
||||
$(el).css({ display: 'none' });
|
||||
$card.css('overflow', $card.data('initialOverflow'));
|
||||
}
|
||||
});
|
||||
} else if ($(e.target).is($('.card .activator')) || $(e.target).is($('.card .activator i'))) {
|
||||
$card.css('overflow', 'hidden');
|
||||
$cardReveal.css({ display: 'block' });
|
||||
anim({
|
||||
targets: $cardReveal[0],
|
||||
translateY: '-100%',
|
||||
duration: 300,
|
||||
easing: 'easeInOutQuad'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
})(cash, M.anime);
|
||||
|
|
@ -0,0 +1,717 @@
|
|||
(function($) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
duration: 200, // ms
|
||||
dist: -100, // zoom scale TODO: make this more intuitive as an option
|
||||
shift: 0, // spacing for center image
|
||||
padding: 0, // Padding between non center items
|
||||
numVisible: 5, // Number of visible items in carousel
|
||||
fullWidth: false, // Change to full width styles
|
||||
indicators: false, // Toggle indicators
|
||||
noWrap: false, // Don't wrap around and cycle through items.
|
||||
onCycleTo: null // Callback for when a new slide is cycled to.
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class Carousel extends Component {
|
||||
/**
|
||||
* Construct Carousel instance
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Carousel, el, options);
|
||||
|
||||
this.el.M_Carousel = this;
|
||||
|
||||
/**
|
||||
* Options for the carousel
|
||||
* @member Carousel#options
|
||||
* @prop {Number} duration
|
||||
* @prop {Number} dist
|
||||
* @prop {Number} shift
|
||||
* @prop {Number} padding
|
||||
* @prop {Number} numVisible
|
||||
* @prop {Boolean} fullWidth
|
||||
* @prop {Boolean} indicators
|
||||
* @prop {Boolean} noWrap
|
||||
* @prop {Function} onCycleTo
|
||||
*/
|
||||
this.options = $.extend({}, Carousel.defaults, options);
|
||||
|
||||
// Setup
|
||||
this.hasMultipleSlides = this.$el.find('.carousel-item').length > 1;
|
||||
this.showIndicators = this.options.indicators && this.hasMultipleSlides;
|
||||
this.noWrap = this.options.noWrap || !this.hasMultipleSlides;
|
||||
this.pressed = false;
|
||||
this.dragged = false;
|
||||
this.offset = this.target = 0;
|
||||
this.images = [];
|
||||
this.itemWidth = this.$el
|
||||
.find('.carousel-item')
|
||||
.first()
|
||||
.innerWidth();
|
||||
this.itemHeight = this.$el
|
||||
.find('.carousel-item')
|
||||
.first()
|
||||
.innerHeight();
|
||||
this.dim = this.itemWidth * 2 + this.options.padding || 1; // Make sure dim is non zero for divisions.
|
||||
this._autoScrollBound = this._autoScroll.bind(this);
|
||||
this._trackBound = this._track.bind(this);
|
||||
|
||||
// Full Width carousel setup
|
||||
if (this.options.fullWidth) {
|
||||
this.options.dist = 0;
|
||||
this._setCarouselHeight();
|
||||
|
||||
// Offset fixed items when indicators.
|
||||
if (this.showIndicators) {
|
||||
this.$el.find('.carousel-fixed-item').addClass('with-indicators');
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through slides
|
||||
this.$indicators = $('<ul class="indicators"></ul>');
|
||||
this.$el.find('.carousel-item').each((el, i) => {
|
||||
this.images.push(el);
|
||||
if (this.showIndicators) {
|
||||
let $indicator = $('<li class="indicator-item"></li>');
|
||||
|
||||
// Add active to first by default.
|
||||
if (i === 0) {
|
||||
$indicator[0].classList.add('active');
|
||||
}
|
||||
|
||||
this.$indicators.append($indicator);
|
||||
}
|
||||
});
|
||||
if (this.showIndicators) {
|
||||
this.$el.append(this.$indicators);
|
||||
}
|
||||
this.count = this.images.length;
|
||||
|
||||
// Cap numVisible at count
|
||||
this.options.numVisible = Math.min(this.count, this.options.numVisible);
|
||||
|
||||
// Setup cross browser string
|
||||
this.xform = 'transform';
|
||||
['webkit', 'Moz', 'O', 'ms'].every((prefix) => {
|
||||
var e = prefix + 'Transform';
|
||||
if (typeof document.body.style[e] !== 'undefined') {
|
||||
this.xform = e;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
this._setupEventHandlers();
|
||||
this._scroll(this.offset);
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Carousel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this.el.M_Carousel = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleCarouselTapBound = this._handleCarouselTap.bind(this);
|
||||
this._handleCarouselDragBound = this._handleCarouselDrag.bind(this);
|
||||
this._handleCarouselReleaseBound = this._handleCarouselRelease.bind(this);
|
||||
this._handleCarouselClickBound = this._handleCarouselClick.bind(this);
|
||||
|
||||
if (typeof window.ontouchstart !== 'undefined') {
|
||||
this.el.addEventListener('touchstart', this._handleCarouselTapBound);
|
||||
this.el.addEventListener('touchmove', this._handleCarouselDragBound);
|
||||
this.el.addEventListener('touchend', this._handleCarouselReleaseBound);
|
||||
}
|
||||
|
||||
this.el.addEventListener('mousedown', this._handleCarouselTapBound);
|
||||
this.el.addEventListener('mousemove', this._handleCarouselDragBound);
|
||||
this.el.addEventListener('mouseup', this._handleCarouselReleaseBound);
|
||||
this.el.addEventListener('mouseleave', this._handleCarouselReleaseBound);
|
||||
this.el.addEventListener('click', this._handleCarouselClickBound);
|
||||
|
||||
if (this.showIndicators && this.$indicators) {
|
||||
this._handleIndicatorClickBound = this._handleIndicatorClick.bind(this);
|
||||
this.$indicators.find('.indicator-item').each((el, i) => {
|
||||
el.addEventListener('click', this._handleIndicatorClickBound);
|
||||
});
|
||||
}
|
||||
|
||||
// Resize
|
||||
let throttledResize = M.throttle(this._handleResize, 200);
|
||||
this._handleThrottledResizeBound = throttledResize.bind(this);
|
||||
|
||||
window.addEventListener('resize', this._handleThrottledResizeBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
if (typeof window.ontouchstart !== 'undefined') {
|
||||
this.el.removeEventListener('touchstart', this._handleCarouselTapBound);
|
||||
this.el.removeEventListener('touchmove', this._handleCarouselDragBound);
|
||||
this.el.removeEventListener('touchend', this._handleCarouselReleaseBound);
|
||||
}
|
||||
this.el.removeEventListener('mousedown', this._handleCarouselTapBound);
|
||||
this.el.removeEventListener('mousemove', this._handleCarouselDragBound);
|
||||
this.el.removeEventListener('mouseup', this._handleCarouselReleaseBound);
|
||||
this.el.removeEventListener('mouseleave', this._handleCarouselReleaseBound);
|
||||
this.el.removeEventListener('click', this._handleCarouselClickBound);
|
||||
|
||||
if (this.showIndicators && this.$indicators) {
|
||||
this.$indicators.find('.indicator-item').each((el, i) => {
|
||||
el.removeEventListener('click', this._handleIndicatorClickBound);
|
||||
});
|
||||
}
|
||||
|
||||
window.removeEventListener('resize', this._handleThrottledResizeBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Carousel Tap
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleCarouselTap(e) {
|
||||
// Fixes firefox draggable image bug
|
||||
if (e.type === 'mousedown' && $(e.target).is('img')) {
|
||||
e.preventDefault();
|
||||
}
|
||||
this.pressed = true;
|
||||
this.dragged = false;
|
||||
this.verticalDragged = false;
|
||||
this.reference = this._xpos(e);
|
||||
this.referenceY = this._ypos(e);
|
||||
|
||||
this.velocity = this.amplitude = 0;
|
||||
this.frame = this.offset;
|
||||
this.timestamp = Date.now();
|
||||
clearInterval(this.ticker);
|
||||
this.ticker = setInterval(this._trackBound, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Carousel Drag
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleCarouselDrag(e) {
|
||||
let x, y, delta, deltaY;
|
||||
if (this.pressed) {
|
||||
x = this._xpos(e);
|
||||
y = this._ypos(e);
|
||||
delta = this.reference - x;
|
||||
deltaY = Math.abs(this.referenceY - y);
|
||||
if (deltaY < 30 && !this.verticalDragged) {
|
||||
// If vertical scrolling don't allow dragging.
|
||||
if (delta > 2 || delta < -2) {
|
||||
this.dragged = true;
|
||||
this.reference = x;
|
||||
this._scroll(this.offset + delta);
|
||||
}
|
||||
} else if (this.dragged) {
|
||||
// If dragging don't allow vertical scroll.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
} else {
|
||||
// Vertical scrolling.
|
||||
this.verticalDragged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.dragged) {
|
||||
// If dragging don't allow vertical scroll.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Carousel Release
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleCarouselRelease(e) {
|
||||
if (this.pressed) {
|
||||
this.pressed = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(this.ticker);
|
||||
this.target = this.offset;
|
||||
if (this.velocity > 10 || this.velocity < -10) {
|
||||
this.amplitude = 0.9 * this.velocity;
|
||||
this.target = this.offset + this.amplitude;
|
||||
}
|
||||
this.target = Math.round(this.target / this.dim) * this.dim;
|
||||
|
||||
// No wrap of items.
|
||||
if (this.noWrap) {
|
||||
if (this.target >= this.dim * (this.count - 1)) {
|
||||
this.target = this.dim * (this.count - 1);
|
||||
} else if (this.target < 0) {
|
||||
this.target = 0;
|
||||
}
|
||||
}
|
||||
this.amplitude = this.target - this.offset;
|
||||
this.timestamp = Date.now();
|
||||
requestAnimationFrame(this._autoScrollBound);
|
||||
|
||||
if (this.dragged) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Carousel CLick
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleCarouselClick(e) {
|
||||
// Disable clicks if carousel was dragged.
|
||||
if (this.dragged) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
} else if (!this.options.fullWidth) {
|
||||
let clickedIndex = $(e.target)
|
||||
.closest('.carousel-item')
|
||||
.index();
|
||||
let diff = this._wrap(this.center) - clickedIndex;
|
||||
|
||||
// Disable clicks if carousel was shifted by click
|
||||
if (diff !== 0) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
this._cycleTo(clickedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Indicator CLick
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleIndicatorClick(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
let indicator = $(e.target).closest('.indicator-item');
|
||||
if (indicator.length) {
|
||||
this._cycleTo(indicator.index());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Throttle Resize
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleResize(e) {
|
||||
if (this.options.fullWidth) {
|
||||
this.itemWidth = this.$el
|
||||
.find('.carousel-item')
|
||||
.first()
|
||||
.innerWidth();
|
||||
this.imageHeight = this.$el.find('.carousel-item.active').height();
|
||||
this.dim = this.itemWidth * 2 + this.options.padding;
|
||||
this.offset = this.center * 2 * this.itemWidth;
|
||||
this.target = this.offset;
|
||||
this._setCarouselHeight(true);
|
||||
} else {
|
||||
this._scroll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set carousel height based on first slide
|
||||
* @param {Booleam} imageOnly - true for image slides
|
||||
*/
|
||||
_setCarouselHeight(imageOnly) {
|
||||
let firstSlide = this.$el.find('.carousel-item.active').length
|
||||
? this.$el.find('.carousel-item.active').first()
|
||||
: this.$el.find('.carousel-item').first();
|
||||
let firstImage = firstSlide.find('img').first();
|
||||
if (firstImage.length) {
|
||||
if (firstImage[0].complete) {
|
||||
// If image won't trigger the load event
|
||||
let imageHeight = firstImage.height();
|
||||
if (imageHeight > 0) {
|
||||
this.$el.css('height', imageHeight + 'px');
|
||||
} else {
|
||||
// If image still has no height, use the natural dimensions to calculate
|
||||
let naturalWidth = firstImage[0].naturalWidth;
|
||||
let naturalHeight = firstImage[0].naturalHeight;
|
||||
let adjustedHeight = this.$el.width() / naturalWidth * naturalHeight;
|
||||
this.$el.css('height', adjustedHeight + 'px');
|
||||
}
|
||||
} else {
|
||||
// Get height when image is loaded normally
|
||||
firstImage.one('load', (el, i) => {
|
||||
this.$el.css('height', el.offsetHeight + 'px');
|
||||
});
|
||||
}
|
||||
} else if (!imageOnly) {
|
||||
let slideHeight = firstSlide.height();
|
||||
this.$el.css('height', slideHeight + 'px');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get x position from event
|
||||
* @param {Event} e
|
||||
*/
|
||||
_xpos(e) {
|
||||
// touch event
|
||||
if (e.targetTouches && e.targetTouches.length >= 1) {
|
||||
return e.targetTouches[0].clientX;
|
||||
}
|
||||
|
||||
// mouse event
|
||||
return e.clientX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get y position from event
|
||||
* @param {Event} e
|
||||
*/
|
||||
_ypos(e) {
|
||||
// touch event
|
||||
if (e.targetTouches && e.targetTouches.length >= 1) {
|
||||
return e.targetTouches[0].clientY;
|
||||
}
|
||||
|
||||
// mouse event
|
||||
return e.clientY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap index
|
||||
* @param {Number} x
|
||||
*/
|
||||
_wrap(x) {
|
||||
return x >= this.count ? x % this.count : x < 0 ? this._wrap(this.count + x % this.count) : x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks scrolling information
|
||||
*/
|
||||
_track() {
|
||||
let now, elapsed, delta, v;
|
||||
|
||||
now = Date.now();
|
||||
elapsed = now - this.timestamp;
|
||||
this.timestamp = now;
|
||||
delta = this.offset - this.frame;
|
||||
this.frame = this.offset;
|
||||
|
||||
v = 1000 * delta / (1 + elapsed);
|
||||
this.velocity = 0.8 * v + 0.2 * this.velocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto scrolls to nearest carousel item.
|
||||
*/
|
||||
_autoScroll() {
|
||||
let elapsed, delta;
|
||||
|
||||
if (this.amplitude) {
|
||||
elapsed = Date.now() - this.timestamp;
|
||||
delta = this.amplitude * Math.exp(-elapsed / this.options.duration);
|
||||
if (delta > 2 || delta < -2) {
|
||||
this._scroll(this.target - delta);
|
||||
requestAnimationFrame(this._autoScrollBound);
|
||||
} else {
|
||||
this._scroll(this.target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to target
|
||||
* @param {Number} x
|
||||
*/
|
||||
_scroll(x) {
|
||||
// Track scrolling state
|
||||
if (!this.$el.hasClass('scrolling')) {
|
||||
this.el.classList.add('scrolling');
|
||||
}
|
||||
if (this.scrollingTimeout != null) {
|
||||
window.clearTimeout(this.scrollingTimeout);
|
||||
}
|
||||
this.scrollingTimeout = window.setTimeout(() => {
|
||||
this.$el.removeClass('scrolling');
|
||||
}, this.options.duration);
|
||||
|
||||
// Start actual scroll
|
||||
let i,
|
||||
half,
|
||||
delta,
|
||||
dir,
|
||||
tween,
|
||||
el,
|
||||
alignment,
|
||||
zTranslation,
|
||||
tweenedOpacity,
|
||||
centerTweenedOpacity;
|
||||
let lastCenter = this.center;
|
||||
let numVisibleOffset = 1 / this.options.numVisible;
|
||||
|
||||
this.offset = typeof x === 'number' ? x : this.offset;
|
||||
this.center = Math.floor((this.offset + this.dim / 2) / this.dim);
|
||||
delta = this.offset - this.center * this.dim;
|
||||
dir = delta < 0 ? 1 : -1;
|
||||
tween = -dir * delta * 2 / this.dim;
|
||||
half = this.count >> 1;
|
||||
|
||||
if (this.options.fullWidth) {
|
||||
alignment = 'translateX(0)';
|
||||
centerTweenedOpacity = 1;
|
||||
} else {
|
||||
alignment = 'translateX(' + (this.el.clientWidth - this.itemWidth) / 2 + 'px) ';
|
||||
alignment += 'translateY(' + (this.el.clientHeight - this.itemHeight) / 2 + 'px)';
|
||||
centerTweenedOpacity = 1 - numVisibleOffset * tween;
|
||||
}
|
||||
|
||||
// Set indicator active
|
||||
if (this.showIndicators) {
|
||||
let diff = this.center % this.count;
|
||||
let activeIndicator = this.$indicators.find('.indicator-item.active');
|
||||
if (activeIndicator.index() !== diff) {
|
||||
activeIndicator.removeClass('active');
|
||||
this.$indicators
|
||||
.find('.indicator-item')
|
||||
.eq(diff)[0]
|
||||
.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
// center
|
||||
// Don't show wrapped items.
|
||||
if (!this.noWrap || (this.center >= 0 && this.center < this.count)) {
|
||||
el = this.images[this._wrap(this.center)];
|
||||
|
||||
// Add active class to center item.
|
||||
if (!$(el).hasClass('active')) {
|
||||
this.$el.find('.carousel-item').removeClass('active');
|
||||
el.classList.add('active');
|
||||
}
|
||||
let transformString = `${alignment} translateX(${-delta / 2}px) translateX(${dir *
|
||||
this.options.shift *
|
||||
tween *
|
||||
i}px) translateZ(${this.options.dist * tween}px)`;
|
||||
this._updateItemStyle(el, centerTweenedOpacity, 0, transformString);
|
||||
}
|
||||
|
||||
for (i = 1; i <= half; ++i) {
|
||||
// right side
|
||||
if (this.options.fullWidth) {
|
||||
zTranslation = this.options.dist;
|
||||
tweenedOpacity = i === half && delta < 0 ? 1 - tween : 1;
|
||||
} else {
|
||||
zTranslation = this.options.dist * (i * 2 + tween * dir);
|
||||
tweenedOpacity = 1 - numVisibleOffset * (i * 2 + tween * dir);
|
||||
}
|
||||
// Don't show wrapped items.
|
||||
if (!this.noWrap || this.center + i < this.count) {
|
||||
el = this.images[this._wrap(this.center + i)];
|
||||
let transformString = `${alignment} translateX(${this.options.shift +
|
||||
(this.dim * i - delta) / 2}px) translateZ(${zTranslation}px)`;
|
||||
this._updateItemStyle(el, tweenedOpacity, -i, transformString);
|
||||
}
|
||||
|
||||
// left side
|
||||
if (this.options.fullWidth) {
|
||||
zTranslation = this.options.dist;
|
||||
tweenedOpacity = i === half && delta > 0 ? 1 - tween : 1;
|
||||
} else {
|
||||
zTranslation = this.options.dist * (i * 2 - tween * dir);
|
||||
tweenedOpacity = 1 - numVisibleOffset * (i * 2 - tween * dir);
|
||||
}
|
||||
// Don't show wrapped items.
|
||||
if (!this.noWrap || this.center - i >= 0) {
|
||||
el = this.images[this._wrap(this.center - i)];
|
||||
let transformString = `${alignment} translateX(${-this.options.shift +
|
||||
(-this.dim * i - delta) / 2}px) translateZ(${zTranslation}px)`;
|
||||
this._updateItemStyle(el, tweenedOpacity, -i, transformString);
|
||||
}
|
||||
}
|
||||
|
||||
// center
|
||||
// Don't show wrapped items.
|
||||
if (!this.noWrap || (this.center >= 0 && this.center < this.count)) {
|
||||
el = this.images[this._wrap(this.center)];
|
||||
let transformString = `${alignment} translateX(${-delta / 2}px) translateX(${dir *
|
||||
this.options.shift *
|
||||
tween}px) translateZ(${this.options.dist * tween}px)`;
|
||||
this._updateItemStyle(el, centerTweenedOpacity, 0, transformString);
|
||||
}
|
||||
|
||||
// onCycleTo callback
|
||||
let $currItem = this.$el.find('.carousel-item').eq(this._wrap(this.center));
|
||||
if (lastCenter !== this.center && typeof this.options.onCycleTo === 'function') {
|
||||
this.options.onCycleTo.call(this, $currItem[0], this.dragged);
|
||||
}
|
||||
|
||||
// One time callback
|
||||
if (typeof this.oneTimeCallback === 'function') {
|
||||
this.oneTimeCallback.call(this, $currItem[0], this.dragged);
|
||||
this.oneTimeCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycle to target
|
||||
* @param {Element} el
|
||||
* @param {Number} opacity
|
||||
* @param {Number} zIndex
|
||||
* @param {String} transform
|
||||
*/
|
||||
_updateItemStyle(el, opacity, zIndex, transform) {
|
||||
el.style[this.xform] = transform;
|
||||
el.style.zIndex = zIndex;
|
||||
el.style.opacity = opacity;
|
||||
el.style.visibility = 'visible';
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycle to target
|
||||
* @param {Number} n
|
||||
* @param {Function} callback
|
||||
*/
|
||||
_cycleTo(n, callback) {
|
||||
let diff = this.center % this.count - n;
|
||||
|
||||
// Account for wraparound.
|
||||
if (!this.noWrap) {
|
||||
if (diff < 0) {
|
||||
if (Math.abs(diff + this.count) < Math.abs(diff)) {
|
||||
diff += this.count;
|
||||
}
|
||||
} else if (diff > 0) {
|
||||
if (Math.abs(diff - this.count) < diff) {
|
||||
diff -= this.count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.target = this.dim * Math.round(this.offset / this.dim);
|
||||
// Next
|
||||
if (diff < 0) {
|
||||
this.target += this.dim * Math.abs(diff);
|
||||
|
||||
// Prev
|
||||
} else if (diff > 0) {
|
||||
this.target -= this.dim * diff;
|
||||
}
|
||||
|
||||
// Set one time callback
|
||||
if (typeof callback === 'function') {
|
||||
this.oneTimeCallback = callback;
|
||||
}
|
||||
|
||||
// Scroll
|
||||
if (this.offset !== this.target) {
|
||||
this.amplitude = this.target - this.offset;
|
||||
this.timestamp = Date.now();
|
||||
requestAnimationFrame(this._autoScrollBound);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycle to next item
|
||||
* @param {Number} [n]
|
||||
*/
|
||||
next(n) {
|
||||
if (n === undefined || isNaN(n)) {
|
||||
n = 1;
|
||||
}
|
||||
|
||||
let index = this.center + n;
|
||||
if (index >= this.count || index < 0) {
|
||||
if (this.noWrap) {
|
||||
return;
|
||||
}
|
||||
|
||||
index = this._wrap(index);
|
||||
}
|
||||
this._cycleTo(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycle to previous item
|
||||
* @param {Number} [n]
|
||||
*/
|
||||
prev(n) {
|
||||
if (n === undefined || isNaN(n)) {
|
||||
n = 1;
|
||||
}
|
||||
|
||||
let index = this.center - n;
|
||||
if (index >= this.count || index < 0) {
|
||||
if (this.noWrap) {
|
||||
return;
|
||||
}
|
||||
|
||||
index = this._wrap(index);
|
||||
}
|
||||
|
||||
this._cycleTo(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycle to nth item
|
||||
* @param {Number} [n]
|
||||
* @param {Function} callback
|
||||
*/
|
||||
set(n, callback) {
|
||||
if (n === undefined || isNaN(n)) {
|
||||
n = 0;
|
||||
}
|
||||
|
||||
if (n > this.count || n < 0) {
|
||||
if (this.noWrap) {
|
||||
return;
|
||||
}
|
||||
|
||||
n = this._wrap(n);
|
||||
}
|
||||
|
||||
this._cycleTo(n, callback);
|
||||
}
|
||||
}
|
||||
|
||||
M.Carousel = Carousel;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Carousel, 'carousel', 'M_Carousel');
|
||||
}
|
||||
})(cash);
|
||||
|
|
@ -0,0 +1,960 @@
|
|||
/*! cash-dom 1.3.5, https://github.com/kenwheeler/cash @license MIT */
|
||||
(function (factory) {
|
||||
window.cash = factory();
|
||||
})(function () {
|
||||
var doc = document, win = window, ArrayProto = Array.prototype, slice = ArrayProto.slice, filter = ArrayProto.filter, push = ArrayProto.push;
|
||||
|
||||
var noop = function () {}, isFunction = function (item) {
|
||||
// @see https://crbug.com/568448
|
||||
return typeof item === typeof noop && item.call;
|
||||
}, isString = function (item) {
|
||||
return typeof item === typeof "";
|
||||
};
|
||||
|
||||
var idMatch = /^#[\w-]*$/, classMatch = /^\.[\w-]*$/, htmlMatch = /<.+>/, singlet = /^\w+$/;
|
||||
|
||||
function find(selector, context) {
|
||||
context = context || doc;
|
||||
var elems = (classMatch.test(selector) ? context.getElementsByClassName(selector.slice(1)) : singlet.test(selector) ? context.getElementsByTagName(selector) : context.querySelectorAll(selector));
|
||||
return elems;
|
||||
}
|
||||
|
||||
var frag;
|
||||
function parseHTML(str) {
|
||||
if (!frag) {
|
||||
frag = doc.implementation.createHTMLDocument(null);
|
||||
var base = frag.createElement("base");
|
||||
base.href = doc.location.href;
|
||||
frag.head.appendChild(base);
|
||||
}
|
||||
|
||||
frag.body.innerHTML = str;
|
||||
|
||||
return frag.body.childNodes;
|
||||
}
|
||||
|
||||
function onReady(fn) {
|
||||
if (doc.readyState !== "loading") {
|
||||
fn();
|
||||
} else {
|
||||
doc.addEventListener("DOMContentLoaded", fn);
|
||||
}
|
||||
}
|
||||
|
||||
function Init(selector, context) {
|
||||
if (!selector) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// If already a cash collection, don't do any further processing
|
||||
if (selector.cash && selector !== win) {
|
||||
return selector;
|
||||
}
|
||||
|
||||
var elems = selector, i = 0, length;
|
||||
|
||||
if (isString(selector)) {
|
||||
elems = (idMatch.test(selector) ?
|
||||
// If an ID use the faster getElementById check
|
||||
doc.getElementById(selector.slice(1)) : htmlMatch.test(selector) ?
|
||||
// If HTML, parse it into real elements
|
||||
parseHTML(selector) :
|
||||
// else use `find`
|
||||
find(selector, context));
|
||||
|
||||
// If function, use as shortcut for DOM ready
|
||||
} else if (isFunction(selector)) {
|
||||
onReady(selector);return this;
|
||||
}
|
||||
|
||||
if (!elems) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// If a single DOM element is passed in or received via ID, return the single element
|
||||
if (elems.nodeType || elems === win) {
|
||||
this[0] = elems;
|
||||
this.length = 1;
|
||||
} else {
|
||||
// Treat like an array and loop through each item.
|
||||
length = this.length = elems.length;
|
||||
for (; i < length; i++) {
|
||||
this[i] = elems[i];
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
function cash(selector, context) {
|
||||
return new Init(selector, context);
|
||||
}
|
||||
|
||||
var fn = cash.fn = cash.prototype = Init.prototype = { // jshint ignore:line
|
||||
cash: true,
|
||||
length: 0,
|
||||
push: push,
|
||||
splice: ArrayProto.splice,
|
||||
map: ArrayProto.map,
|
||||
init: Init
|
||||
};
|
||||
|
||||
Object.defineProperty(fn, "constructor", { value: cash });
|
||||
|
||||
cash.parseHTML = parseHTML;
|
||||
cash.noop = noop;
|
||||
cash.isFunction = isFunction;
|
||||
cash.isString = isString;
|
||||
|
||||
cash.extend = fn.extend = function (target) {
|
||||
target = target || {};
|
||||
|
||||
var args = slice.call(arguments), length = args.length, i = 1;
|
||||
|
||||
if (args.length === 1) {
|
||||
target = this;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
for (; i < length; i++) {
|
||||
if (!args[i]) {
|
||||
continue;
|
||||
}
|
||||
for (var key in args[i]) {
|
||||
if (args[i].hasOwnProperty(key)) {
|
||||
target[key] = args[i][key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
function each(collection, callback) {
|
||||
var l = collection.length, i = 0;
|
||||
|
||||
for (; i < l; i++) {
|
||||
if (callback.call(collection[i], collection[i], i, collection) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function matches(el, selector) {
|
||||
var m = el && (el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector || el.oMatchesSelector);
|
||||
return !!m && m.call(el, selector);
|
||||
}
|
||||
|
||||
function getCompareFunction(selector) {
|
||||
return (
|
||||
/* Use browser's `matches` function if string */
|
||||
isString(selector) ? matches :
|
||||
/* Match a cash element */
|
||||
selector.cash ? function (el) {
|
||||
return selector.is(el);
|
||||
} :
|
||||
/* Direct comparison */
|
||||
function (el, selector) {
|
||||
return el === selector;
|
||||
});
|
||||
}
|
||||
|
||||
function unique(collection) {
|
||||
return cash(slice.call(collection).filter(function (item, index, self) {
|
||||
return self.indexOf(item) === index;
|
||||
}));
|
||||
}
|
||||
|
||||
cash.extend({
|
||||
merge: function (first, second) {
|
||||
var len = +second.length, i = first.length, j = 0;
|
||||
|
||||
for (; j < len; i++, j++) {
|
||||
first[i] = second[j];
|
||||
}
|
||||
|
||||
first.length = i;
|
||||
return first;
|
||||
},
|
||||
|
||||
each: each,
|
||||
matches: matches,
|
||||
unique: unique,
|
||||
isArray: Array.isArray,
|
||||
isNumeric: function (n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var uid = cash.uid = "_cash" + Date.now();
|
||||
|
||||
function getDataCache(node) {
|
||||
return (node[uid] = node[uid] || {});
|
||||
}
|
||||
|
||||
function setData(node, key, value) {
|
||||
return (getDataCache(node)[key] = value);
|
||||
}
|
||||
|
||||
function getData(node, key) {
|
||||
var c = getDataCache(node);
|
||||
if (c[key] === undefined) {
|
||||
c[key] = node.dataset ? node.dataset[key] : cash(node).attr("data-" + key);
|
||||
}
|
||||
return c[key];
|
||||
}
|
||||
|
||||
function removeData(node, key) {
|
||||
var c = getDataCache(node);
|
||||
if (c) {
|
||||
delete c[key];
|
||||
} else if (node.dataset) {
|
||||
delete node.dataset[key];
|
||||
} else {
|
||||
cash(node).removeAttr("data-" + name);
|
||||
}
|
||||
}
|
||||
|
||||
fn.extend({
|
||||
data: function (name, value) {
|
||||
if (isString(name)) {
|
||||
return (value === undefined ? getData(this[0], name) : this.each(function (v) {
|
||||
return setData(v, name, value);
|
||||
}));
|
||||
}
|
||||
|
||||
for (var key in name) {
|
||||
this.data(key, name[key]);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
removeData: function (key) {
|
||||
return this.each(function (v) {
|
||||
return removeData(v, key);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var notWhiteMatch = /\S+/g;
|
||||
|
||||
function getClasses(c) {
|
||||
return isString(c) && c.match(notWhiteMatch);
|
||||
}
|
||||
|
||||
function hasClass(v, c) {
|
||||
return (v.classList ? v.classList.contains(c) : new RegExp("(^| )" + c + "( |$)", "gi").test(v.className));
|
||||
}
|
||||
|
||||
function addClass(v, c, spacedName) {
|
||||
if (v.classList) {
|
||||
v.classList.add(c);
|
||||
} else if (spacedName.indexOf(" " + c + " ")) {
|
||||
v.className += " " + c;
|
||||
}
|
||||
}
|
||||
|
||||
function removeClass(v, c) {
|
||||
if (v.classList) {
|
||||
v.classList.remove(c);
|
||||
} else {
|
||||
v.className = v.className.replace(c, "");
|
||||
}
|
||||
}
|
||||
|
||||
fn.extend({
|
||||
addClass: function (c) {
|
||||
var classes = getClasses(c);
|
||||
|
||||
return (classes ? this.each(function (v) {
|
||||
var spacedName = " " + v.className + " ";
|
||||
each(classes, function (c) {
|
||||
addClass(v, c, spacedName);
|
||||
});
|
||||
}) : this);
|
||||
},
|
||||
|
||||
attr: function (name, value) {
|
||||
if (!name) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (isString(name)) {
|
||||
if (value === undefined) {
|
||||
return this[0] ? this[0].getAttribute ? this[0].getAttribute(name) : this[0][name] : undefined;
|
||||
}
|
||||
|
||||
return this.each(function (v) {
|
||||
if (v.setAttribute) {
|
||||
v.setAttribute(name, value);
|
||||
} else {
|
||||
v[name] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (var key in name) {
|
||||
this.attr(key, name[key]);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
hasClass: function (c) {
|
||||
var check = false, classes = getClasses(c);
|
||||
if (classes && classes.length) {
|
||||
this.each(function (v) {
|
||||
check = hasClass(v, classes[0]);
|
||||
return !check;
|
||||
});
|
||||
}
|
||||
return check;
|
||||
},
|
||||
|
||||
prop: function (name, value) {
|
||||
if (isString(name)) {
|
||||
return (value === undefined ? this[0][name] : this.each(function (v) {
|
||||
v[name] = value;
|
||||
}));
|
||||
}
|
||||
|
||||
for (var key in name) {
|
||||
this.prop(key, name[key]);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
removeAttr: function (name) {
|
||||
return this.each(function (v) {
|
||||
if (v.removeAttribute) {
|
||||
v.removeAttribute(name);
|
||||
} else {
|
||||
delete v[name];
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
removeClass: function (c) {
|
||||
if (!arguments.length) {
|
||||
return this.attr("class", "");
|
||||
}
|
||||
var classes = getClasses(c);
|
||||
return (classes ? this.each(function (v) {
|
||||
each(classes, function (c) {
|
||||
removeClass(v, c);
|
||||
});
|
||||
}) : this);
|
||||
},
|
||||
|
||||
removeProp: function (name) {
|
||||
return this.each(function (v) {
|
||||
delete v[name];
|
||||
});
|
||||
},
|
||||
|
||||
toggleClass: function (c, state) {
|
||||
if (state !== undefined) {
|
||||
return this[state ? "addClass" : "removeClass"](c);
|
||||
}
|
||||
var classes = getClasses(c);
|
||||
return (classes ? this.each(function (v) {
|
||||
var spacedName = " " + v.className + " ";
|
||||
each(classes, function (c) {
|
||||
if (hasClass(v, c)) {
|
||||
removeClass(v, c);
|
||||
} else {
|
||||
addClass(v, c, spacedName);
|
||||
}
|
||||
});
|
||||
}) : this);
|
||||
} });
|
||||
|
||||
fn.extend({
|
||||
add: function (selector, context) {
|
||||
return unique(cash.merge(this, cash(selector, context)));
|
||||
},
|
||||
|
||||
each: function (callback) {
|
||||
each(this, callback);
|
||||
return this;
|
||||
},
|
||||
|
||||
eq: function (index) {
|
||||
return cash(this.get(index));
|
||||
},
|
||||
|
||||
filter: function (selector) {
|
||||
if (!selector) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var comparator = (isFunction(selector) ? selector : getCompareFunction(selector));
|
||||
|
||||
return cash(filter.call(this, function (e) {
|
||||
return comparator(e, selector);
|
||||
}));
|
||||
},
|
||||
|
||||
first: function () {
|
||||
return this.eq(0);
|
||||
},
|
||||
|
||||
get: function (index) {
|
||||
if (index === undefined) {
|
||||
return slice.call(this);
|
||||
}
|
||||
return (index < 0 ? this[index + this.length] : this[index]);
|
||||
},
|
||||
|
||||
index: function (elem) {
|
||||
var child = elem ? cash(elem)[0] : this[0], collection = elem ? this : cash(child).parent().children();
|
||||
return slice.call(collection).indexOf(child);
|
||||
},
|
||||
|
||||
last: function () {
|
||||
return this.eq(-1);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var camelCase = (function () {
|
||||
var camelRegex = /(?:^\w|[A-Z]|\b\w)/g, whiteSpace = /[\s-_]+/g;
|
||||
return function (str) {
|
||||
return str.replace(camelRegex, function (letter, index) {
|
||||
return letter[index === 0 ? "toLowerCase" : "toUpperCase"]();
|
||||
}).replace(whiteSpace, "");
|
||||
};
|
||||
}());
|
||||
|
||||
var getPrefixedProp = (function () {
|
||||
var cache = {}, doc = document, div = doc.createElement("div"), style = div.style;
|
||||
|
||||
return function (prop) {
|
||||
prop = camelCase(prop);
|
||||
if (cache[prop]) {
|
||||
return cache[prop];
|
||||
}
|
||||
|
||||
var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), prefixes = ["webkit", "moz", "ms", "o"], props = (prop + " " + (prefixes).join(ucProp + " ") + ucProp).split(" ");
|
||||
|
||||
each(props, function (p) {
|
||||
if (p in style) {
|
||||
cache[p] = prop = cache[prop] = p;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return cache[prop];
|
||||
};
|
||||
}());
|
||||
|
||||
cash.prefixedProp = getPrefixedProp;
|
||||
cash.camelCase = camelCase;
|
||||
|
||||
fn.extend({
|
||||
css: function (prop, value) {
|
||||
if (isString(prop)) {
|
||||
prop = getPrefixedProp(prop);
|
||||
return (arguments.length > 1 ? this.each(function (v) {
|
||||
return v.style[prop] = value;
|
||||
}) : win.getComputedStyle(this[0])[prop]);
|
||||
}
|
||||
|
||||
for (var key in prop) {
|
||||
this.css(key, prop[key]);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function compute(el, prop) {
|
||||
return parseInt(win.getComputedStyle(el[0], null)[prop], 10) || 0;
|
||||
}
|
||||
|
||||
each(["Width", "Height"], function (v) {
|
||||
var lower = v.toLowerCase();
|
||||
|
||||
fn[lower] = function () {
|
||||
return this[0].getBoundingClientRect()[lower];
|
||||
};
|
||||
|
||||
fn["inner" + v] = function () {
|
||||
return this[0]["client" + v];
|
||||
};
|
||||
|
||||
fn["outer" + v] = function (margins) {
|
||||
return this[0]["offset" + v] + (margins ? compute(this, "margin" + (v === "Width" ? "Left" : "Top")) + compute(this, "margin" + (v === "Width" ? "Right" : "Bottom")) : 0);
|
||||
};
|
||||
});
|
||||
|
||||
function registerEvent(node, eventName, callback) {
|
||||
var eventCache = getData(node, "_cashEvents") || setData(node, "_cashEvents", {});
|
||||
eventCache[eventName] = eventCache[eventName] || [];
|
||||
eventCache[eventName].push(callback);
|
||||
node.addEventListener(eventName, callback);
|
||||
}
|
||||
|
||||
function removeEvent(node, eventName, callback) {
|
||||
var events = getData(node, "_cashEvents"), eventCache = (events && events[eventName]), index;
|
||||
|
||||
if (!eventCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
node.removeEventListener(eventName, callback);
|
||||
index = eventCache.indexOf(callback);
|
||||
if (index >= 0) {
|
||||
eventCache.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
each(eventCache, function (event) {
|
||||
node.removeEventListener(eventName, event);
|
||||
});
|
||||
eventCache = [];
|
||||
}
|
||||
}
|
||||
|
||||
fn.extend({
|
||||
off: function (eventName, callback) {
|
||||
return this.each(function (v) {
|
||||
return removeEvent(v, eventName, callback);
|
||||
});
|
||||
},
|
||||
|
||||
on: function (eventName, delegate, callback, runOnce) {
|
||||
// jshint ignore:line
|
||||
var originalCallback;
|
||||
if (!isString(eventName)) {
|
||||
for (var key in eventName) {
|
||||
this.on(key, delegate, eventName[key]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
if (isFunction(delegate)) {
|
||||
callback = delegate;
|
||||
delegate = null;
|
||||
}
|
||||
|
||||
if (eventName === "ready") {
|
||||
onReady(callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
if (delegate) {
|
||||
originalCallback = callback;
|
||||
callback = function (e) {
|
||||
var t = e.target;
|
||||
while (!matches(t, delegate)) {
|
||||
if (t === this || t === null) {
|
||||
return (t = false);
|
||||
}
|
||||
|
||||
t = t.parentNode;
|
||||
}
|
||||
|
||||
if (t) {
|
||||
originalCallback.call(t, e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return this.each(function (v) {
|
||||
var finalCallback = callback;
|
||||
if (runOnce) {
|
||||
finalCallback = function () {
|
||||
callback.apply(this, arguments);
|
||||
removeEvent(v, eventName, finalCallback);
|
||||
};
|
||||
}
|
||||
registerEvent(v, eventName, finalCallback);
|
||||
});
|
||||
},
|
||||
|
||||
one: function (eventName, delegate, callback) {
|
||||
return this.on(eventName, delegate, callback, true);
|
||||
},
|
||||
|
||||
ready: onReady,
|
||||
|
||||
/**
|
||||
* Modified
|
||||
* Triggers browser event
|
||||
* @param String eventName
|
||||
* @param Object data - Add properties to event object
|
||||
*/
|
||||
trigger: function (eventName, data) {
|
||||
if (document.createEvent) {
|
||||
let evt = document.createEvent('HTMLEvents');
|
||||
evt.initEvent(eventName, true, false);
|
||||
evt = this.extend(evt, data);
|
||||
return this.each(function (v) {
|
||||
return v.dispatchEvent(evt);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function encode(name, value) {
|
||||
return "&" + encodeURIComponent(name) + "=" + encodeURIComponent(value).replace(/%20/g, "+");
|
||||
}
|
||||
|
||||
function getSelectMultiple_(el) {
|
||||
var values = [];
|
||||
each(el.options, function (o) {
|
||||
if (o.selected) {
|
||||
values.push(o.value);
|
||||
}
|
||||
});
|
||||
return values.length ? values : null;
|
||||
}
|
||||
|
||||
function getSelectSingle_(el) {
|
||||
var selectedIndex = el.selectedIndex;
|
||||
return selectedIndex >= 0 ? el.options[selectedIndex].value : null;
|
||||
}
|
||||
|
||||
function getValue(el) {
|
||||
var type = el.type;
|
||||
if (!type) {
|
||||
return null;
|
||||
}
|
||||
switch (type.toLowerCase()) {
|
||||
case "select-one":
|
||||
return getSelectSingle_(el);
|
||||
case "select-multiple":
|
||||
return getSelectMultiple_(el);
|
||||
case "radio":
|
||||
return (el.checked) ? el.value : null;
|
||||
case "checkbox":
|
||||
return (el.checked) ? el.value : null;
|
||||
default:
|
||||
return el.value ? el.value : null;
|
||||
}
|
||||
}
|
||||
|
||||
fn.extend({
|
||||
serialize: function () {
|
||||
var query = "";
|
||||
|
||||
each(this[0].elements || this, function (el) {
|
||||
if (el.disabled || el.tagName === "FIELDSET") {
|
||||
return;
|
||||
}
|
||||
var name = el.name;
|
||||
switch (el.type.toLowerCase()) {
|
||||
case "file":
|
||||
case "reset":
|
||||
case "submit":
|
||||
case "button":
|
||||
break;
|
||||
case "select-multiple":
|
||||
var values = getValue(el);
|
||||
if (values !== null) {
|
||||
each(values, function (value) {
|
||||
query += encode(name, value);
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
var value = getValue(el);
|
||||
if (value !== null) {
|
||||
query += encode(name, value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return query.substr(1);
|
||||
},
|
||||
|
||||
val: function (value) {
|
||||
if (value === undefined) {
|
||||
return getValue(this[0]);
|
||||
}
|
||||
|
||||
return this.each(function (v) {
|
||||
return v.value = value;
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function insertElement(el, child, prepend) {
|
||||
if (prepend) {
|
||||
var first = el.childNodes[0];
|
||||
el.insertBefore(child, first);
|
||||
} else {
|
||||
el.appendChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
function insertContent(parent, child, prepend) {
|
||||
var str = isString(child);
|
||||
|
||||
if (!str && child.length) {
|
||||
each(child, function (v) {
|
||||
return insertContent(parent, v, prepend);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
each(parent, str ? function (v) {
|
||||
return v.insertAdjacentHTML(prepend ? "afterbegin" : "beforeend", child);
|
||||
} : function (v, i) {
|
||||
return insertElement(v, (i === 0 ? child : child.cloneNode(true)), prepend);
|
||||
});
|
||||
}
|
||||
|
||||
fn.extend({
|
||||
after: function (selector) {
|
||||
cash(selector).insertAfter(this);
|
||||
return this;
|
||||
},
|
||||
|
||||
append: function (content) {
|
||||
insertContent(this, content);
|
||||
return this;
|
||||
},
|
||||
|
||||
appendTo: function (parent) {
|
||||
insertContent(cash(parent), this);
|
||||
return this;
|
||||
},
|
||||
|
||||
before: function (selector) {
|
||||
cash(selector).insertBefore(this);
|
||||
return this;
|
||||
},
|
||||
|
||||
clone: function () {
|
||||
return cash(this.map(function (v) {
|
||||
return v.cloneNode(true);
|
||||
}));
|
||||
},
|
||||
|
||||
empty: function () {
|
||||
this.html("");
|
||||
return this;
|
||||
},
|
||||
|
||||
html: function (content) {
|
||||
if (content === undefined) {
|
||||
return this[0].innerHTML;
|
||||
}
|
||||
var source = (content.nodeType ? content[0].outerHTML : content);
|
||||
return this.each(function (v) {
|
||||
return v.innerHTML = source;
|
||||
});
|
||||
},
|
||||
|
||||
insertAfter: function (selector) {
|
||||
var _this = this;
|
||||
|
||||
|
||||
cash(selector).each(function (el, i) {
|
||||
var parent = el.parentNode, sibling = el.nextSibling;
|
||||
_this.each(function (v) {
|
||||
parent.insertBefore((i === 0 ? v : v.cloneNode(true)), sibling);
|
||||
});
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
insertBefore: function (selector) {
|
||||
var _this2 = this;
|
||||
cash(selector).each(function (el, i) {
|
||||
var parent = el.parentNode;
|
||||
_this2.each(function (v) {
|
||||
parent.insertBefore((i === 0 ? v : v.cloneNode(true)), el);
|
||||
});
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
||||
prepend: function (content) {
|
||||
insertContent(this, content, true);
|
||||
return this;
|
||||
},
|
||||
|
||||
prependTo: function (parent) {
|
||||
insertContent(cash(parent), this, true);
|
||||
return this;
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
return this.each(function (v) {
|
||||
if (!!v.parentNode) {
|
||||
return v.parentNode.removeChild(v);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
text: function (content) {
|
||||
if (content === undefined) {
|
||||
return this[0].textContent;
|
||||
}
|
||||
return this.each(function (v) {
|
||||
return v.textContent = content;
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var docEl = doc.documentElement;
|
||||
|
||||
fn.extend({
|
||||
position: function () {
|
||||
var el = this[0];
|
||||
return {
|
||||
left: el.offsetLeft,
|
||||
top: el.offsetTop
|
||||
};
|
||||
},
|
||||
|
||||
offset: function () {
|
||||
var rect = this[0].getBoundingClientRect();
|
||||
return {
|
||||
top: rect.top + win.pageYOffset - docEl.clientTop,
|
||||
left: rect.left + win.pageXOffset - docEl.clientLeft
|
||||
};
|
||||
},
|
||||
|
||||
offsetParent: function () {
|
||||
return cash(this[0].offsetParent);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
fn.extend({
|
||||
children: function (selector) {
|
||||
var elems = [];
|
||||
this.each(function (el) {
|
||||
push.apply(elems, el.children);
|
||||
});
|
||||
elems = unique(elems);
|
||||
|
||||
return (!selector ? elems : elems.filter(function (v) {
|
||||
return matches(v, selector);
|
||||
}));
|
||||
},
|
||||
|
||||
closest: function (selector) {
|
||||
if (!selector || this.length < 1) {
|
||||
return cash();
|
||||
}
|
||||
if (this.is(selector)) {
|
||||
return this.filter(selector);
|
||||
}
|
||||
return this.parent().closest(selector);
|
||||
},
|
||||
|
||||
is: function (selector) {
|
||||
if (!selector) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var match = false, comparator = getCompareFunction(selector);
|
||||
|
||||
this.each(function (el) {
|
||||
match = comparator(el, selector);
|
||||
return !match;
|
||||
});
|
||||
|
||||
return match;
|
||||
},
|
||||
|
||||
find: function (selector) {
|
||||
if (!selector || selector.nodeType) {
|
||||
return cash(selector && this.has(selector).length ? selector : null);
|
||||
}
|
||||
|
||||
var elems = [];
|
||||
this.each(function (el) {
|
||||
push.apply(elems, find(selector, el));
|
||||
});
|
||||
|
||||
return unique(elems);
|
||||
},
|
||||
|
||||
has: function (selector) {
|
||||
var comparator = (isString(selector) ? function (el) {
|
||||
return find(selector, el).length !== 0;
|
||||
} : function (el) {
|
||||
return el.contains(selector);
|
||||
});
|
||||
|
||||
return this.filter(comparator);
|
||||
},
|
||||
|
||||
next: function () {
|
||||
return cash(this[0].nextElementSibling);
|
||||
},
|
||||
|
||||
not: function (selector) {
|
||||
if (!selector) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var comparator = getCompareFunction(selector);
|
||||
|
||||
return this.filter(function (el) {
|
||||
return !comparator(el, selector);
|
||||
});
|
||||
},
|
||||
|
||||
parent: function () {
|
||||
var result = [];
|
||||
|
||||
this.each(function (item) {
|
||||
if (item && item.parentNode) {
|
||||
result.push(item.parentNode);
|
||||
}
|
||||
});
|
||||
|
||||
return unique(result);
|
||||
},
|
||||
|
||||
parents: function (selector) {
|
||||
var last, result = [];
|
||||
|
||||
this.each(function (item) {
|
||||
last = item;
|
||||
|
||||
while (last && last.parentNode && last !== doc.body.parentNode) {
|
||||
last = last.parentNode;
|
||||
|
||||
if (!selector || (selector && matches(last, selector))) {
|
||||
result.push(last);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return unique(result);
|
||||
},
|
||||
|
||||
prev: function () {
|
||||
return cash(this[0].previousElementSibling);
|
||||
},
|
||||
|
||||
siblings: function (selector) {
|
||||
var collection = this.parent().children(selector), el = this[0];
|
||||
|
||||
return collection.filter(function (i) {
|
||||
return i !== el;
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
return cash;
|
||||
});
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
(function($) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class CharacterCounter extends Component {
|
||||
/**
|
||||
* Construct CharacterCounter instance
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(CharacterCounter, el, options);
|
||||
|
||||
this.el.M_CharacterCounter = this;
|
||||
|
||||
/**
|
||||
* Options for the character counter
|
||||
*/
|
||||
this.options = $.extend({}, CharacterCounter.defaults, options);
|
||||
|
||||
this.isInvalid = false;
|
||||
this.isValidLength = false;
|
||||
this._setupCounter();
|
||||
this._setupEventHandlers();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_CharacterCounter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this.el.CharacterCounter = undefined;
|
||||
this._removeCounter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleUpdateCounterBound = this.updateCounter.bind(this);
|
||||
|
||||
this.el.addEventListener('focus', this._handleUpdateCounterBound, true);
|
||||
this.el.addEventListener('input', this._handleUpdateCounterBound, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
this.el.removeEventListener('focus', this._handleUpdateCounterBound, true);
|
||||
this.el.removeEventListener('input', this._handleUpdateCounterBound, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup counter element
|
||||
*/
|
||||
_setupCounter() {
|
||||
this.counterEl = document.createElement('span');
|
||||
$(this.counterEl)
|
||||
.addClass('character-counter')
|
||||
.css({
|
||||
float: 'right',
|
||||
'font-size': '12px',
|
||||
height: 1
|
||||
});
|
||||
|
||||
this.$el.parent().append(this.counterEl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove counter element
|
||||
*/
|
||||
_removeCounter() {
|
||||
$(this.counterEl).remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update counter
|
||||
*/
|
||||
updateCounter() {
|
||||
let maxLength = +this.$el.attr('data-length'),
|
||||
actualLength = this.el.value.length;
|
||||
this.isValidLength = actualLength <= maxLength;
|
||||
let counterString = actualLength;
|
||||
|
||||
if (maxLength) {
|
||||
counterString += '/' + maxLength;
|
||||
this._validateInput();
|
||||
}
|
||||
|
||||
$(this.counterEl).html(counterString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add validation classes
|
||||
*/
|
||||
_validateInput() {
|
||||
if (this.isValidLength && this.isInvalid) {
|
||||
this.isInvalid = false;
|
||||
this.$el.removeClass('invalid');
|
||||
} else if (!this.isValidLength && !this.isInvalid) {
|
||||
this.isInvalid = true;
|
||||
this.$el.removeClass('valid');
|
||||
this.$el.addClass('invalid');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
M.CharacterCounter = CharacterCounter;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(CharacterCounter, 'characterCounter', 'M_CharacterCounter');
|
||||
}
|
||||
})(cash);
|
||||
|
|
@ -0,0 +1,481 @@
|
|||
(function($) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
data: [],
|
||||
placeholder: '',
|
||||
secondaryPlaceholder: '',
|
||||
autocompleteOptions: {},
|
||||
limit: Infinity,
|
||||
onChipAdd: null,
|
||||
onChipSelect: null,
|
||||
onChipDelete: null
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Object} chip
|
||||
* @property {String} tag chip tag string
|
||||
* @property {String} [image] chip avatar image string
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class Chips extends Component {
|
||||
/**
|
||||
* Construct Chips instance and set up overlay
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Chips, el, options);
|
||||
|
||||
this.el.M_Chips = this;
|
||||
|
||||
/**
|
||||
* Options for the modal
|
||||
* @member Chips#options
|
||||
* @prop {Array} data
|
||||
* @prop {String} placeholder
|
||||
* @prop {String} secondaryPlaceholder
|
||||
* @prop {Object} autocompleteOptions
|
||||
*/
|
||||
this.options = $.extend({}, Chips.defaults, options);
|
||||
|
||||
this.$el.addClass('chips input-field');
|
||||
this.chipsData = [];
|
||||
this.$chips = $();
|
||||
this._setupInput();
|
||||
this.hasAutocomplete = Object.keys(this.options.autocompleteOptions).length > 0;
|
||||
|
||||
// Set input id
|
||||
if (!this.$input.attr('id')) {
|
||||
this.$input.attr('id', M.guid());
|
||||
}
|
||||
|
||||
// Render initial chips
|
||||
if (this.options.data.length) {
|
||||
this.chipsData = this.options.data;
|
||||
this._renderChips(this.chipsData);
|
||||
}
|
||||
|
||||
// Setup autocomplete if needed
|
||||
if (this.hasAutocomplete) {
|
||||
this._setupAutocomplete();
|
||||
}
|
||||
|
||||
this._setPlaceholder();
|
||||
this._setupLabel();
|
||||
this._setupEventHandlers();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Chips;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Chips Data
|
||||
*/
|
||||
getData() {
|
||||
return this.chipsData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this.$chips.remove();
|
||||
this.el.M_Chips = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleChipClickBound = this._handleChipClick.bind(this);
|
||||
this._handleInputKeydownBound = this._handleInputKeydown.bind(this);
|
||||
this._handleInputFocusBound = this._handleInputFocus.bind(this);
|
||||
this._handleInputBlurBound = this._handleInputBlur.bind(this);
|
||||
|
||||
this.el.addEventListener('click', this._handleChipClickBound);
|
||||
document.addEventListener('keydown', Chips._handleChipsKeydown);
|
||||
document.addEventListener('keyup', Chips._handleChipsKeyup);
|
||||
this.el.addEventListener('blur', Chips._handleChipsBlur, true);
|
||||
this.$input[0].addEventListener('focus', this._handleInputFocusBound);
|
||||
this.$input[0].addEventListener('blur', this._handleInputBlurBound);
|
||||
this.$input[0].addEventListener('keydown', this._handleInputKeydownBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
this.el.removeEventListener('click', this._handleChipClickBound);
|
||||
document.removeEventListener('keydown', Chips._handleChipsKeydown);
|
||||
document.removeEventListener('keyup', Chips._handleChipsKeyup);
|
||||
this.el.removeEventListener('blur', Chips._handleChipsBlur, true);
|
||||
this.$input[0].removeEventListener('focus', this._handleInputFocusBound);
|
||||
this.$input[0].removeEventListener('blur', this._handleInputBlurBound);
|
||||
this.$input[0].removeEventListener('keydown', this._handleInputKeydownBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Chip Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleChipClick(e) {
|
||||
let $chip = $(e.target).closest('.chip');
|
||||
let clickedClose = $(e.target).is('.close');
|
||||
if ($chip.length) {
|
||||
let index = $chip.index();
|
||||
if (clickedClose) {
|
||||
// delete chip
|
||||
this.deleteChip(index);
|
||||
this.$input[0].focus();
|
||||
} else {
|
||||
// select chip
|
||||
this.selectChip(index);
|
||||
}
|
||||
|
||||
// Default handle click to focus on input
|
||||
} else {
|
||||
this.$input[0].focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Chips Keydown
|
||||
* @param {Event} e
|
||||
*/
|
||||
static _handleChipsKeydown(e) {
|
||||
Chips._keydown = true;
|
||||
|
||||
let $chips = $(e.target).closest('.chips');
|
||||
let chipsKeydown = e.target && $chips.length;
|
||||
|
||||
// Don't handle keydown inputs on input and textarea
|
||||
if ($(e.target).is('input, textarea') || !chipsKeydown) {
|
||||
return;
|
||||
}
|
||||
|
||||
let currChips = $chips[0].M_Chips;
|
||||
|
||||
// backspace and delete
|
||||
if (e.keyCode === 8 || e.keyCode === 46) {
|
||||
e.preventDefault();
|
||||
|
||||
let selectIndex = currChips.chipsData.length;
|
||||
if (currChips._selectedChip) {
|
||||
let index = currChips._selectedChip.index();
|
||||
currChips.deleteChip(index);
|
||||
currChips._selectedChip = null;
|
||||
|
||||
// Make sure selectIndex doesn't go negative
|
||||
selectIndex = Math.max(index - 1, 0);
|
||||
}
|
||||
|
||||
if (currChips.chipsData.length) {
|
||||
currChips.selectChip(selectIndex);
|
||||
}
|
||||
|
||||
// left arrow key
|
||||
} else if (e.keyCode === 37) {
|
||||
if (currChips._selectedChip) {
|
||||
let selectIndex = currChips._selectedChip.index() - 1;
|
||||
if (selectIndex < 0) {
|
||||
return;
|
||||
}
|
||||
currChips.selectChip(selectIndex);
|
||||
}
|
||||
|
||||
// right arrow key
|
||||
} else if (e.keyCode === 39) {
|
||||
if (currChips._selectedChip) {
|
||||
let selectIndex = currChips._selectedChip.index() + 1;
|
||||
|
||||
if (selectIndex >= currChips.chipsData.length) {
|
||||
currChips.$input[0].focus();
|
||||
} else {
|
||||
currChips.selectChip(selectIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Chips Keyup
|
||||
* @param {Event} e
|
||||
*/
|
||||
static _handleChipsKeyup(e) {
|
||||
Chips._keydown = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Chips Blur
|
||||
* @param {Event} e
|
||||
*/
|
||||
static _handleChipsBlur(e) {
|
||||
if (!Chips._keydown) {
|
||||
let $chips = $(e.target).closest('.chips');
|
||||
let currChips = $chips[0].M_Chips;
|
||||
|
||||
currChips._selectedChip = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Input Focus
|
||||
*/
|
||||
_handleInputFocus() {
|
||||
this.$el.addClass('focus');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Input Blur
|
||||
*/
|
||||
_handleInputBlur() {
|
||||
this.$el.removeClass('focus');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Input Keydown
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleInputKeydown(e) {
|
||||
Chips._keydown = true;
|
||||
|
||||
// enter
|
||||
if (e.keyCode === 13) {
|
||||
// Override enter if autocompleting.
|
||||
if (this.hasAutocomplete && this.autocomplete && this.autocomplete.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
this.addChip({
|
||||
tag: this.$input[0].value
|
||||
});
|
||||
this.$input[0].value = '';
|
||||
|
||||
// delete or left
|
||||
} else if (
|
||||
(e.keyCode === 8 || e.keyCode === 37) &&
|
||||
this.$input[0].value === '' &&
|
||||
this.chipsData.length
|
||||
) {
|
||||
e.preventDefault();
|
||||
this.selectChip(this.chipsData.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render Chip
|
||||
* @param {chip} chip
|
||||
* @return {Element}
|
||||
*/
|
||||
_renderChip(chip) {
|
||||
if (!chip.tag) {
|
||||
return;
|
||||
}
|
||||
|
||||
let renderedChip = document.createElement('div');
|
||||
let closeIcon = document.createElement('i');
|
||||
renderedChip.classList.add('chip');
|
||||
renderedChip.textContent = chip.tag;
|
||||
renderedChip.setAttribute('tabindex', 0);
|
||||
$(closeIcon).addClass('material-icons close');
|
||||
closeIcon.textContent = 'close';
|
||||
|
||||
// attach image if needed
|
||||
if (chip.image) {
|
||||
let img = document.createElement('img');
|
||||
img.setAttribute('src', chip.image);
|
||||
renderedChip.insertBefore(img, renderedChip.firstChild);
|
||||
}
|
||||
|
||||
renderedChip.appendChild(closeIcon);
|
||||
return renderedChip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render Chips
|
||||
*/
|
||||
_renderChips() {
|
||||
this.$chips.remove();
|
||||
for (let i = 0; i < this.chipsData.length; i++) {
|
||||
let chipEl = this._renderChip(this.chipsData[i]);
|
||||
this.$el.append(chipEl);
|
||||
this.$chips.add(chipEl);
|
||||
}
|
||||
|
||||
// move input to end
|
||||
this.$el.append(this.$input[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Autocomplete
|
||||
*/
|
||||
_setupAutocomplete() {
|
||||
this.options.autocompleteOptions.onAutocomplete = (val) => {
|
||||
this.addChip({
|
||||
tag: val
|
||||
});
|
||||
this.$input[0].value = '';
|
||||
this.$input[0].focus();
|
||||
};
|
||||
|
||||
this.autocomplete = M.Autocomplete.init(this.$input[0], this.options.autocompleteOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Input
|
||||
*/
|
||||
_setupInput() {
|
||||
this.$input = this.$el.find('input');
|
||||
if (!this.$input.length) {
|
||||
this.$input = $('<input></input>');
|
||||
this.$el.append(this.$input);
|
||||
}
|
||||
|
||||
this.$input.addClass('input');
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Label
|
||||
*/
|
||||
_setupLabel() {
|
||||
this.$label = this.$el.find('label');
|
||||
if (this.$label.length) {
|
||||
this.$label.setAttribute('for', this.$input.attr('id'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set placeholder
|
||||
*/
|
||||
_setPlaceholder() {
|
||||
if (this.chipsData !== undefined && !this.chipsData.length && this.options.placeholder) {
|
||||
$(this.$input).prop('placeholder', this.options.placeholder);
|
||||
} else if (
|
||||
(this.chipsData === undefined || !!this.chipsData.length) &&
|
||||
this.options.secondaryPlaceholder
|
||||
) {
|
||||
$(this.$input).prop('placeholder', this.options.secondaryPlaceholder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if chip is valid
|
||||
* @param {chip} chip
|
||||
*/
|
||||
_isValid(chip) {
|
||||
if (chip.hasOwnProperty('tag') && chip.tag !== '') {
|
||||
let exists = false;
|
||||
for (let i = 0; i < this.chipsData.length; i++) {
|
||||
if (this.chipsData[i].tag === chip.tag) {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return !exists;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add chip
|
||||
* @param {chip} chip
|
||||
*/
|
||||
addChip(chip) {
|
||||
if (!this._isValid(chip) || this.chipsData.length >= this.options.limit) {
|
||||
return;
|
||||
}
|
||||
|
||||
let renderedChip = this._renderChip(chip);
|
||||
this.$chips.add(renderedChip);
|
||||
this.chipsData.push(chip);
|
||||
$(this.$input).before(renderedChip);
|
||||
this._setPlaceholder();
|
||||
|
||||
// fire chipAdd callback
|
||||
if (typeof this.options.onChipAdd === 'function') {
|
||||
this.options.onChipAdd.call(this, this.$el, renderedChip);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete chip
|
||||
* @param {Number} chip
|
||||
*/
|
||||
deleteChip(chipIndex) {
|
||||
let $chip = this.$chips.eq(chipIndex);
|
||||
this.$chips.eq(chipIndex).remove();
|
||||
this.$chips = this.$chips.filter(function(el) {
|
||||
return $(el).index() >= 0;
|
||||
});
|
||||
this.chipsData.splice(chipIndex, 1);
|
||||
this._setPlaceholder();
|
||||
|
||||
// fire chipDelete callback
|
||||
if (typeof this.options.onChipDelete === 'function') {
|
||||
this.options.onChipDelete.call(this, this.$el, $chip[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select chip
|
||||
* @param {Number} chip
|
||||
*/
|
||||
selectChip(chipIndex) {
|
||||
let $chip = this.$chips.eq(chipIndex);
|
||||
this._selectedChip = $chip;
|
||||
$chip[0].focus();
|
||||
|
||||
// fire chipSelect callback
|
||||
if (typeof this.options.onChipSelect === 'function') {
|
||||
this.options.onChipSelect.call(this, this.$el, $chip[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof Chips
|
||||
*/
|
||||
Chips._keydown = false;
|
||||
|
||||
M.Chips = Chips;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Chips, 'chips', 'M_Chips');
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// Handle removal of static chips.
|
||||
$(document.body).on('click', '.chip .close', function() {
|
||||
let $chips = $(this).closest('.chips');
|
||||
if ($chips.length && $chips[0].M_Chips) {
|
||||
return;
|
||||
}
|
||||
$(this)
|
||||
.closest('.chip')
|
||||
.remove();
|
||||
});
|
||||
});
|
||||
})(cash);
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
(function($, anim) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
accordion: true,
|
||||
onOpenStart: undefined,
|
||||
onOpenEnd: undefined,
|
||||
onCloseStart: undefined,
|
||||
onCloseEnd: undefined,
|
||||
inDuration: 300,
|
||||
outDuration: 300
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class Collapsible extends Component {
|
||||
/**
|
||||
* Construct Collapsible instance
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Collapsible, el, options);
|
||||
|
||||
this.el.M_Collapsible = this;
|
||||
|
||||
/**
|
||||
* Options for the collapsible
|
||||
* @member Collapsible#options
|
||||
* @prop {Boolean} [accordion=false] - Type of the collapsible
|
||||
* @prop {Function} onOpenStart - Callback function called before collapsible is opened
|
||||
* @prop {Function} onOpenEnd - Callback function called after collapsible is opened
|
||||
* @prop {Function} onCloseStart - Callback function called before collapsible is closed
|
||||
* @prop {Function} onCloseEnd - Callback function called after collapsible is closed
|
||||
* @prop {Number} inDuration - Transition in duration in milliseconds.
|
||||
* @prop {Number} outDuration - Transition duration in milliseconds.
|
||||
*/
|
||||
this.options = $.extend({}, Collapsible.defaults, options);
|
||||
|
||||
// Setup tab indices
|
||||
this.$headers = this.$el.children('li').children('.collapsible-header');
|
||||
this.$headers.attr('tabindex', 0);
|
||||
|
||||
this._setupEventHandlers();
|
||||
|
||||
// Open first active
|
||||
let $activeBodies = this.$el.children('li.active').children('.collapsible-body');
|
||||
if (this.options.accordion) {
|
||||
// Handle Accordion
|
||||
$activeBodies.first().css('display', 'block');
|
||||
} else {
|
||||
// Handle Expandables
|
||||
$activeBodies.css('display', 'block');
|
||||
}
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Collapsible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this.el.M_Collapsible = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleCollapsibleClickBound = this._handleCollapsibleClick.bind(this);
|
||||
this._handleCollapsibleKeydownBound = this._handleCollapsibleKeydown.bind(this);
|
||||
this.el.addEventListener('click', this._handleCollapsibleClickBound);
|
||||
this.$headers.each((header) => {
|
||||
header.addEventListener('keydown', this._handleCollapsibleKeydownBound);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
this.el.removeEventListener('click', this._handleCollapsibleClickBound);
|
||||
this.$headers.each((header) => {
|
||||
header.removeEventListener('keydown', this._handleCollapsibleKeydownBound);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Collapsible Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleCollapsibleClick(e) {
|
||||
let $header = $(e.target).closest('.collapsible-header');
|
||||
if (e.target && $header.length) {
|
||||
let $collapsible = $header.closest('.collapsible');
|
||||
if ($collapsible[0] === this.el) {
|
||||
let $collapsibleLi = $header.closest('li');
|
||||
let $collapsibleLis = $collapsible.children('li');
|
||||
let isActive = $collapsibleLi[0].classList.contains('active');
|
||||
let index = $collapsibleLis.index($collapsibleLi);
|
||||
|
||||
if (isActive) {
|
||||
this.close(index);
|
||||
} else {
|
||||
this.open(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Collapsible Keydown
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleCollapsibleKeydown(e) {
|
||||
if (e.keyCode === 13) {
|
||||
this._handleCollapsibleClickBound(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate in collapsible slide
|
||||
* @param {Number} index - 0th index of slide
|
||||
*/
|
||||
_animateIn(index) {
|
||||
let $collapsibleLi = this.$el.children('li').eq(index);
|
||||
if ($collapsibleLi.length) {
|
||||
let $body = $collapsibleLi.children('.collapsible-body');
|
||||
|
||||
anim.remove($body[0]);
|
||||
$body.css({
|
||||
display: 'block',
|
||||
overflow: 'hidden',
|
||||
height: 0,
|
||||
paddingTop: '',
|
||||
paddingBottom: ''
|
||||
});
|
||||
|
||||
let pTop = $body.css('padding-top');
|
||||
let pBottom = $body.css('padding-bottom');
|
||||
let finalHeight = $body[0].scrollHeight;
|
||||
$body.css({
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0
|
||||
});
|
||||
|
||||
anim({
|
||||
targets: $body[0],
|
||||
height: finalHeight,
|
||||
paddingTop: pTop,
|
||||
paddingBottom: pBottom,
|
||||
duration: this.options.inDuration,
|
||||
easing: 'easeInOutCubic',
|
||||
complete: (anim) => {
|
||||
$body.css({
|
||||
overflow: '',
|
||||
paddingTop: '',
|
||||
paddingBottom: '',
|
||||
height: ''
|
||||
});
|
||||
|
||||
// onOpenEnd callback
|
||||
if (typeof this.options.onOpenEnd === 'function') {
|
||||
this.options.onOpenEnd.call(this, $collapsibleLi[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate out collapsible slide
|
||||
* @param {Number} index - 0th index of slide to open
|
||||
*/
|
||||
_animateOut(index) {
|
||||
let $collapsibleLi = this.$el.children('li').eq(index);
|
||||
if ($collapsibleLi.length) {
|
||||
let $body = $collapsibleLi.children('.collapsible-body');
|
||||
anim.remove($body[0]);
|
||||
$body.css('overflow', 'hidden');
|
||||
anim({
|
||||
targets: $body[0],
|
||||
height: 0,
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
duration: this.options.outDuration,
|
||||
easing: 'easeInOutCubic',
|
||||
complete: () => {
|
||||
$body.css({
|
||||
height: '',
|
||||
overflow: '',
|
||||
padding: '',
|
||||
display: ''
|
||||
});
|
||||
|
||||
// onCloseEnd callback
|
||||
if (typeof this.options.onCloseEnd === 'function') {
|
||||
this.options.onCloseEnd.call(this, $collapsibleLi[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open Collapsible
|
||||
* @param {Number} index - 0th index of slide
|
||||
*/
|
||||
open(index) {
|
||||
let $collapsibleLi = this.$el.children('li').eq(index);
|
||||
if ($collapsibleLi.length && !$collapsibleLi[0].classList.contains('active')) {
|
||||
// onOpenStart callback
|
||||
if (typeof this.options.onOpenStart === 'function') {
|
||||
this.options.onOpenStart.call(this, $collapsibleLi[0]);
|
||||
}
|
||||
|
||||
// Handle accordion behavior
|
||||
if (this.options.accordion) {
|
||||
let $collapsibleLis = this.$el.children('li');
|
||||
let $activeLis = this.$el.children('li.active');
|
||||
$activeLis.each((el) => {
|
||||
let index = $collapsibleLis.index($(el));
|
||||
this.close(index);
|
||||
});
|
||||
}
|
||||
|
||||
// Animate in
|
||||
$collapsibleLi[0].classList.add('active');
|
||||
this._animateIn(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close Collapsible
|
||||
* @param {Number} index - 0th index of slide
|
||||
*/
|
||||
close(index) {
|
||||
let $collapsibleLi = this.$el.children('li').eq(index);
|
||||
if ($collapsibleLi.length && $collapsibleLi[0].classList.contains('active')) {
|
||||
// onCloseStart callback
|
||||
if (typeof this.options.onCloseStart === 'function') {
|
||||
this.options.onCloseStart.call(this, $collapsibleLi[0]);
|
||||
}
|
||||
|
||||
// Animate out
|
||||
$collapsibleLi[0].classList.remove('active');
|
||||
this._animateOut(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
M.Collapsible = Collapsible;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Collapsible, 'collapsible', 'M_Collapsible');
|
||||
}
|
||||
})(cash, M.anime);
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
class Component {
|
||||
/**
|
||||
* Generic constructor for all components
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(classDef, el, options) {
|
||||
// Display error if el is valid HTML Element
|
||||
if (!(el instanceof Element)) {
|
||||
console.error(Error(el + ' is not an HTML Element'));
|
||||
}
|
||||
|
||||
// If exists, destroy and reinitialize in child
|
||||
let ins = classDef.getInstance(el);
|
||||
if (!!ins) {
|
||||
ins.destroy();
|
||||
}
|
||||
|
||||
this.el = el;
|
||||
this.$el = cash(el);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes components
|
||||
* @param {class} classDef
|
||||
* @param {Element | NodeList | jQuery} els
|
||||
* @param {Object} options
|
||||
*/
|
||||
static init(classDef, els, options) {
|
||||
let instances = null;
|
||||
if (els instanceof Element) {
|
||||
instances = new classDef(els, options);
|
||||
} else if (!!els && (els.jquery || els.cash || els instanceof NodeList)) {
|
||||
let instancesArr = [];
|
||||
for (let i = 0; i < els.length; i++) {
|
||||
instancesArr.push(new classDef(els[i], options));
|
||||
}
|
||||
instances = instancesArr;
|
||||
}
|
||||
|
||||
return instances;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,975 @@
|
|||
(function($) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
// Close when date is selected
|
||||
autoClose: false,
|
||||
|
||||
// the default output format for the input field value
|
||||
format: 'mmm dd, yyyy',
|
||||
|
||||
// Used to create date object from current input string
|
||||
parse: null,
|
||||
|
||||
// The initial date to view when first opened
|
||||
defaultDate: null,
|
||||
|
||||
// Make the `defaultDate` the initial selected value
|
||||
setDefaultDate: false,
|
||||
|
||||
disableWeekends: false,
|
||||
|
||||
disableDayFn: null,
|
||||
|
||||
// First day of week (0: Sunday, 1: Monday etc)
|
||||
firstDay: 0,
|
||||
|
||||
// The earliest date that can be selected
|
||||
minDate: null,
|
||||
// Thelatest date that can be selected
|
||||
maxDate: null,
|
||||
|
||||
// Number of years either side, or array of upper/lower range
|
||||
yearRange: 10,
|
||||
|
||||
// used internally (don't config outside)
|
||||
minYear: 0,
|
||||
maxYear: 9999,
|
||||
minMonth: undefined,
|
||||
maxMonth: undefined,
|
||||
|
||||
startRange: null,
|
||||
endRange: null,
|
||||
|
||||
isRTL: false,
|
||||
|
||||
// Render the month after year in the calendar title
|
||||
showMonthAfterYear: false,
|
||||
|
||||
// Render days of the calendar grid that fall in the next or previous month
|
||||
showDaysInNextAndPreviousMonths: false,
|
||||
|
||||
// Specify a DOM element to render the calendar in
|
||||
container: null,
|
||||
|
||||
// Show clear button
|
||||
showClearBtn: false,
|
||||
|
||||
// internationalization
|
||||
i18n: {
|
||||
cancel: 'Cancel',
|
||||
clear: 'Clear',
|
||||
done: 'Ok',
|
||||
previousMonth: '‹',
|
||||
nextMonth: '›',
|
||||
months: [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December'
|
||||
],
|
||||
monthsShort: [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec'
|
||||
],
|
||||
weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
||||
weekdaysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
||||
weekdaysAbbrev: ['S', 'M', 'T', 'W', 'T', 'F', 'S']
|
||||
},
|
||||
|
||||
// events array
|
||||
events: [],
|
||||
|
||||
// callback function
|
||||
onSelect: null,
|
||||
onOpen: null,
|
||||
onClose: null,
|
||||
onDraw: null
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class Datepicker extends Component {
|
||||
/**
|
||||
* Construct Datepicker instance and set up overlay
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Datepicker, el, options);
|
||||
|
||||
this.el.M_Datepicker = this;
|
||||
|
||||
this.options = $.extend({}, Datepicker.defaults, options);
|
||||
|
||||
// make sure i18n defaults are not lost when only few i18n option properties are passed
|
||||
if (!!options && options.hasOwnProperty('i18n') && typeof options.i18n === 'object') {
|
||||
this.options.i18n = $.extend({}, Datepicker.defaults.i18n, options.i18n);
|
||||
}
|
||||
|
||||
// Remove time component from minDate and maxDate options
|
||||
if (this.options.minDate) this.options.minDate.setHours(0, 0, 0, 0);
|
||||
if (this.options.maxDate) this.options.maxDate.setHours(0, 0, 0, 0);
|
||||
|
||||
this.id = M.guid();
|
||||
|
||||
this._setupVariables();
|
||||
this._insertHTMLIntoDOM();
|
||||
this._setupModal();
|
||||
|
||||
this._setupEventHandlers();
|
||||
|
||||
if (!this.options.defaultDate) {
|
||||
this.options.defaultDate = new Date(Date.parse(this.el.value));
|
||||
}
|
||||
|
||||
let defDate = this.options.defaultDate;
|
||||
if (Datepicker._isDate(defDate)) {
|
||||
if (this.options.setDefaultDate) {
|
||||
this.setDate(defDate, true);
|
||||
this.setInputValue();
|
||||
} else {
|
||||
this.gotoDate(defDate);
|
||||
}
|
||||
} else {
|
||||
this.gotoDate(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes open/close state of datepicker
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
static _isDate(obj) {
|
||||
return /Date/.test(Object.prototype.toString.call(obj)) && !isNaN(obj.getTime());
|
||||
}
|
||||
|
||||
static _isWeekend(date) {
|
||||
let day = date.getDay();
|
||||
return day === 0 || day === 6;
|
||||
}
|
||||
|
||||
static _setToStartOfDay(date) {
|
||||
if (Datepicker._isDate(date)) date.setHours(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
static _getDaysInMonth(year, month) {
|
||||
return [31, Datepicker._isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][
|
||||
month
|
||||
];
|
||||
}
|
||||
|
||||
static _isLeapYear(year) {
|
||||
// solution by Matti Virkkunen: http://stackoverflow.com/a/4881951
|
||||
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
||||
}
|
||||
|
||||
static _compareDates(a, b) {
|
||||
// weak date comparison (use setToStartOfDay(date) to ensure correct result)
|
||||
return a.getTime() === b.getTime();
|
||||
}
|
||||
|
||||
static _setToStartOfDay(date) {
|
||||
if (Datepicker._isDate(date)) date.setHours(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Datepicker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this.modal.destroy();
|
||||
$(this.modalEl).remove();
|
||||
this.destroySelects();
|
||||
this.el.M_Datepicker = undefined;
|
||||
}
|
||||
|
||||
destroySelects() {
|
||||
let oldYearSelect = this.calendarEl.querySelector('.orig-select-year');
|
||||
if (oldYearSelect) {
|
||||
M.FormSelect.getInstance(oldYearSelect).destroy();
|
||||
}
|
||||
let oldMonthSelect = this.calendarEl.querySelector('.orig-select-month');
|
||||
if (oldMonthSelect) {
|
||||
M.FormSelect.getInstance(oldMonthSelect).destroy();
|
||||
}
|
||||
}
|
||||
|
||||
_insertHTMLIntoDOM() {
|
||||
if (this.options.showClearBtn) {
|
||||
$(this.clearBtn).css({ visibility: '' });
|
||||
this.clearBtn.innerHTML = this.options.i18n.clear;
|
||||
}
|
||||
|
||||
this.doneBtn.innerHTML = this.options.i18n.done;
|
||||
this.cancelBtn.innerHTML = this.options.i18n.cancel;
|
||||
|
||||
if (this.options.container) {
|
||||
this.$modalEl.appendTo(this.options.container);
|
||||
} else {
|
||||
this.$modalEl.insertBefore(this.el);
|
||||
}
|
||||
}
|
||||
|
||||
_setupModal() {
|
||||
this.modalEl.id = 'modal-' + this.id;
|
||||
this.modal = M.Modal.init(this.modalEl, {
|
||||
onCloseEnd: () => {
|
||||
this.isOpen = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toString(format) {
|
||||
format = format || this.options.format;
|
||||
if (!Datepicker._isDate(this.date)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let formatArray = format.split(/(d{1,4}|m{1,4}|y{4}|yy|!.)/g);
|
||||
let formattedDate = formatArray
|
||||
.map((label) => {
|
||||
if (this.formats[label]) {
|
||||
return this.formats[label]();
|
||||
}
|
||||
|
||||
return label;
|
||||
})
|
||||
.join('');
|
||||
return formattedDate;
|
||||
}
|
||||
|
||||
setDate(date, preventOnSelect) {
|
||||
if (!date) {
|
||||
this.date = null;
|
||||
this._renderDateDisplay();
|
||||
return this.draw();
|
||||
}
|
||||
if (typeof date === 'string') {
|
||||
date = new Date(Date.parse(date));
|
||||
}
|
||||
if (!Datepicker._isDate(date)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let min = this.options.minDate,
|
||||
max = this.options.maxDate;
|
||||
|
||||
if (Datepicker._isDate(min) && date < min) {
|
||||
date = min;
|
||||
} else if (Datepicker._isDate(max) && date > max) {
|
||||
date = max;
|
||||
}
|
||||
|
||||
this.date = new Date(date.getTime());
|
||||
|
||||
this._renderDateDisplay();
|
||||
|
||||
Datepicker._setToStartOfDay(this.date);
|
||||
this.gotoDate(this.date);
|
||||
|
||||
if (!preventOnSelect && typeof this.options.onSelect === 'function') {
|
||||
this.options.onSelect.call(this, this.date);
|
||||
}
|
||||
}
|
||||
|
||||
setInputValue() {
|
||||
this.el.value = this.toString();
|
||||
this.$el.trigger('change', { firedBy: this });
|
||||
}
|
||||
|
||||
_renderDateDisplay() {
|
||||
let displayDate = Datepicker._isDate(this.date) ? this.date : new Date();
|
||||
let i18n = this.options.i18n;
|
||||
let day = i18n.weekdaysShort[displayDate.getDay()];
|
||||
let month = i18n.monthsShort[displayDate.getMonth()];
|
||||
let date = displayDate.getDate();
|
||||
this.yearTextEl.innerHTML = displayDate.getFullYear();
|
||||
this.dateTextEl.innerHTML = `${day}, ${month} ${date}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* change view to a specific date
|
||||
*/
|
||||
gotoDate(date) {
|
||||
let newCalendar = true;
|
||||
|
||||
if (!Datepicker._isDate(date)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.calendars) {
|
||||
let firstVisibleDate = new Date(this.calendars[0].year, this.calendars[0].month, 1),
|
||||
lastVisibleDate = new Date(
|
||||
this.calendars[this.calendars.length - 1].year,
|
||||
this.calendars[this.calendars.length - 1].month,
|
||||
1
|
||||
),
|
||||
visibleDate = date.getTime();
|
||||
// get the end of the month
|
||||
lastVisibleDate.setMonth(lastVisibleDate.getMonth() + 1);
|
||||
lastVisibleDate.setDate(lastVisibleDate.getDate() - 1);
|
||||
newCalendar =
|
||||
visibleDate < firstVisibleDate.getTime() || lastVisibleDate.getTime() < visibleDate;
|
||||
}
|
||||
|
||||
if (newCalendar) {
|
||||
this.calendars = [
|
||||
{
|
||||
month: date.getMonth(),
|
||||
year: date.getFullYear()
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
this.adjustCalendars();
|
||||
}
|
||||
|
||||
adjustCalendars() {
|
||||
this.calendars[0] = this.adjustCalendar(this.calendars[0]);
|
||||
this.draw();
|
||||
}
|
||||
|
||||
adjustCalendar(calendar) {
|
||||
if (calendar.month < 0) {
|
||||
calendar.year -= Math.ceil(Math.abs(calendar.month) / 12);
|
||||
calendar.month += 12;
|
||||
}
|
||||
if (calendar.month > 11) {
|
||||
calendar.year += Math.floor(Math.abs(calendar.month) / 12);
|
||||
calendar.month -= 12;
|
||||
}
|
||||
return calendar;
|
||||
}
|
||||
|
||||
nextMonth() {
|
||||
this.calendars[0].month++;
|
||||
this.adjustCalendars();
|
||||
}
|
||||
|
||||
prevMonth() {
|
||||
this.calendars[0].month--;
|
||||
this.adjustCalendars();
|
||||
}
|
||||
|
||||
render(year, month, randId) {
|
||||
let opts = this.options,
|
||||
now = new Date(),
|
||||
days = Datepicker._getDaysInMonth(year, month),
|
||||
before = new Date(year, month, 1).getDay(),
|
||||
data = [],
|
||||
row = [];
|
||||
Datepicker._setToStartOfDay(now);
|
||||
if (opts.firstDay > 0) {
|
||||
before -= opts.firstDay;
|
||||
if (before < 0) {
|
||||
before += 7;
|
||||
}
|
||||
}
|
||||
let previousMonth = month === 0 ? 11 : month - 1,
|
||||
nextMonth = month === 11 ? 0 : month + 1,
|
||||
yearOfPreviousMonth = month === 0 ? year - 1 : year,
|
||||
yearOfNextMonth = month === 11 ? year + 1 : year,
|
||||
daysInPreviousMonth = Datepicker._getDaysInMonth(yearOfPreviousMonth, previousMonth);
|
||||
let cells = days + before,
|
||||
after = cells;
|
||||
while (after > 7) {
|
||||
after -= 7;
|
||||
}
|
||||
cells += 7 - after;
|
||||
let isWeekSelected = false;
|
||||
for (let i = 0, r = 0; i < cells; i++) {
|
||||
let day = new Date(year, month, 1 + (i - before)),
|
||||
isSelected = Datepicker._isDate(this.date)
|
||||
? Datepicker._compareDates(day, this.date)
|
||||
: false,
|
||||
isToday = Datepicker._compareDates(day, now),
|
||||
hasEvent = opts.events.indexOf(day.toDateString()) !== -1 ? true : false,
|
||||
isEmpty = i < before || i >= days + before,
|
||||
dayNumber = 1 + (i - before),
|
||||
monthNumber = month,
|
||||
yearNumber = year,
|
||||
isStartRange = opts.startRange && Datepicker._compareDates(opts.startRange, day),
|
||||
isEndRange = opts.endRange && Datepicker._compareDates(opts.endRange, day),
|
||||
isInRange =
|
||||
opts.startRange && opts.endRange && opts.startRange < day && day < opts.endRange,
|
||||
isDisabled =
|
||||
(opts.minDate && day < opts.minDate) ||
|
||||
(opts.maxDate && day > opts.maxDate) ||
|
||||
(opts.disableWeekends && Datepicker._isWeekend(day)) ||
|
||||
(opts.disableDayFn && opts.disableDayFn(day));
|
||||
|
||||
if (isEmpty) {
|
||||
if (i < before) {
|
||||
dayNumber = daysInPreviousMonth + dayNumber;
|
||||
monthNumber = previousMonth;
|
||||
yearNumber = yearOfPreviousMonth;
|
||||
} else {
|
||||
dayNumber = dayNumber - days;
|
||||
monthNumber = nextMonth;
|
||||
yearNumber = yearOfNextMonth;
|
||||
}
|
||||
}
|
||||
|
||||
let dayConfig = {
|
||||
day: dayNumber,
|
||||
month: monthNumber,
|
||||
year: yearNumber,
|
||||
hasEvent: hasEvent,
|
||||
isSelected: isSelected,
|
||||
isToday: isToday,
|
||||
isDisabled: isDisabled,
|
||||
isEmpty: isEmpty,
|
||||
isStartRange: isStartRange,
|
||||
isEndRange: isEndRange,
|
||||
isInRange: isInRange,
|
||||
showDaysInNextAndPreviousMonths: opts.showDaysInNextAndPreviousMonths
|
||||
};
|
||||
|
||||
row.push(this.renderDay(dayConfig));
|
||||
|
||||
if (++r === 7) {
|
||||
data.push(this.renderRow(row, opts.isRTL, isWeekSelected));
|
||||
row = [];
|
||||
r = 0;
|
||||
isWeekSelected = false;
|
||||
}
|
||||
}
|
||||
return this.renderTable(opts, data, randId);
|
||||
}
|
||||
|
||||
renderDay(opts) {
|
||||
let arr = [];
|
||||
let ariaSelected = 'false';
|
||||
if (opts.isEmpty) {
|
||||
if (opts.showDaysInNextAndPreviousMonths) {
|
||||
arr.push('is-outside-current-month');
|
||||
arr.push('is-selection-disabled');
|
||||
} else {
|
||||
return '<td class="is-empty"></td>';
|
||||
}
|
||||
}
|
||||
if (opts.isDisabled) {
|
||||
arr.push('is-disabled');
|
||||
}
|
||||
|
||||
if (opts.isToday) {
|
||||
arr.push('is-today');
|
||||
}
|
||||
if (opts.isSelected) {
|
||||
arr.push('is-selected');
|
||||
ariaSelected = 'true';
|
||||
}
|
||||
if (opts.hasEvent) {
|
||||
arr.push('has-event');
|
||||
}
|
||||
if (opts.isInRange) {
|
||||
arr.push('is-inrange');
|
||||
}
|
||||
if (opts.isStartRange) {
|
||||
arr.push('is-startrange');
|
||||
}
|
||||
if (opts.isEndRange) {
|
||||
arr.push('is-endrange');
|
||||
}
|
||||
return (
|
||||
`<td data-day="${opts.day}" class="${arr.join(' ')}" aria-selected="${ariaSelected}">` +
|
||||
`<button class="datepicker-day-button" type="button" data-year="${opts.year}" data-month="${
|
||||
opts.month
|
||||
}" data-day="${opts.day}">${opts.day}</button>` +
|
||||
'</td>'
|
||||
);
|
||||
}
|
||||
|
||||
renderRow(days, isRTL, isRowSelected) {
|
||||
return (
|
||||
'<tr class="datepicker-row' +
|
||||
(isRowSelected ? ' is-selected' : '') +
|
||||
'">' +
|
||||
(isRTL ? days.reverse() : days).join('') +
|
||||
'</tr>'
|
||||
);
|
||||
}
|
||||
|
||||
renderTable(opts, data, randId) {
|
||||
return (
|
||||
'<div class="datepicker-table-wrapper"><table cellpadding="0" cellspacing="0" class="datepicker-table" role="grid" aria-labelledby="' +
|
||||
randId +
|
||||
'">' +
|
||||
this.renderHead(opts) +
|
||||
this.renderBody(data) +
|
||||
'</table></div>'
|
||||
);
|
||||
}
|
||||
|
||||
renderHead(opts) {
|
||||
let i,
|
||||
arr = [];
|
||||
for (i = 0; i < 7; i++) {
|
||||
arr.push(
|
||||
`<th scope="col"><abbr title="${this.renderDayName(opts, i)}">${this.renderDayName(
|
||||
opts,
|
||||
i,
|
||||
true
|
||||
)}</abbr></th>`
|
||||
);
|
||||
}
|
||||
return '<thead><tr>' + (opts.isRTL ? arr.reverse() : arr).join('') + '</tr></thead>';
|
||||
}
|
||||
|
||||
renderBody(rows) {
|
||||
return '<tbody>' + rows.join('') + '</tbody>';
|
||||
}
|
||||
|
||||
renderTitle(instance, c, year, month, refYear, randId) {
|
||||
let i,
|
||||
j,
|
||||
arr,
|
||||
opts = this.options,
|
||||
isMinYear = year === opts.minYear,
|
||||
isMaxYear = year === opts.maxYear,
|
||||
html =
|
||||
'<div id="' +
|
||||
randId +
|
||||
'" class="datepicker-controls" role="heading" aria-live="assertive">',
|
||||
monthHtml,
|
||||
yearHtml,
|
||||
prev = true,
|
||||
next = true;
|
||||
|
||||
for (arr = [], i = 0; i < 12; i++) {
|
||||
arr.push(
|
||||
'<option value="' +
|
||||
(year === refYear ? i - c : 12 + i - c) +
|
||||
'"' +
|
||||
(i === month ? ' selected="selected"' : '') +
|
||||
((isMinYear && i < opts.minMonth) || (isMaxYear && i > opts.maxMonth)
|
||||
? 'disabled="disabled"'
|
||||
: '') +
|
||||
'>' +
|
||||
opts.i18n.months[i] +
|
||||
'</option>'
|
||||
);
|
||||
}
|
||||
|
||||
monthHtml =
|
||||
'<select class="datepicker-select orig-select-month" tabindex="-1">' +
|
||||
arr.join('') +
|
||||
'</select>';
|
||||
|
||||
if ($.isArray(opts.yearRange)) {
|
||||
i = opts.yearRange[0];
|
||||
j = opts.yearRange[1] + 1;
|
||||
} else {
|
||||
i = year - opts.yearRange;
|
||||
j = 1 + year + opts.yearRange;
|
||||
}
|
||||
|
||||
for (arr = []; i < j && i <= opts.maxYear; i++) {
|
||||
if (i >= opts.minYear) {
|
||||
arr.push(`<option value="${i}" ${i === year ? 'selected="selected"' : ''}>${i}</option>`);
|
||||
}
|
||||
}
|
||||
|
||||
yearHtml = `<select class="datepicker-select orig-select-year" tabindex="-1">${arr.join(
|
||||
''
|
||||
)}</select>`;
|
||||
|
||||
let leftArrow =
|
||||
'<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"/><path d="M0-.5h24v24H0z" fill="none"/></svg>';
|
||||
html += `<button class="month-prev${
|
||||
prev ? '' : ' is-disabled'
|
||||
}" type="button">${leftArrow}</button>`;
|
||||
|
||||
html += '<div class="selects-container">';
|
||||
if (opts.showMonthAfterYear) {
|
||||
html += yearHtml + monthHtml;
|
||||
} else {
|
||||
html += monthHtml + yearHtml;
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
if (isMinYear && (month === 0 || opts.minMonth >= month)) {
|
||||
prev = false;
|
||||
}
|
||||
|
||||
if (isMaxYear && (month === 11 || opts.maxMonth <= month)) {
|
||||
next = false;
|
||||
}
|
||||
|
||||
let rightArrow =
|
||||
'<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"/><path d="M0-.25h24v24H0z" fill="none"/></svg>';
|
||||
html += `<button class="month-next${
|
||||
next ? '' : ' is-disabled'
|
||||
}" type="button">${rightArrow}</button>`;
|
||||
|
||||
return (html += '</div>');
|
||||
}
|
||||
|
||||
/**
|
||||
* refresh the HTML
|
||||
*/
|
||||
draw(force) {
|
||||
if (!this.isOpen && !force) {
|
||||
return;
|
||||
}
|
||||
let opts = this.options,
|
||||
minYear = opts.minYear,
|
||||
maxYear = opts.maxYear,
|
||||
minMonth = opts.minMonth,
|
||||
maxMonth = opts.maxMonth,
|
||||
html = '',
|
||||
randId;
|
||||
|
||||
if (this._y <= minYear) {
|
||||
this._y = minYear;
|
||||
if (!isNaN(minMonth) && this._m < minMonth) {
|
||||
this._m = minMonth;
|
||||
}
|
||||
}
|
||||
if (this._y >= maxYear) {
|
||||
this._y = maxYear;
|
||||
if (!isNaN(maxMonth) && this._m > maxMonth) {
|
||||
this._m = maxMonth;
|
||||
}
|
||||
}
|
||||
|
||||
randId =
|
||||
'datepicker-title-' +
|
||||
Math.random()
|
||||
.toString(36)
|
||||
.replace(/[^a-z]+/g, '')
|
||||
.substr(0, 2);
|
||||
|
||||
for (let c = 0; c < 1; c++) {
|
||||
this._renderDateDisplay();
|
||||
html +=
|
||||
this.renderTitle(
|
||||
this,
|
||||
c,
|
||||
this.calendars[c].year,
|
||||
this.calendars[c].month,
|
||||
this.calendars[0].year,
|
||||
randId
|
||||
) + this.render(this.calendars[c].year, this.calendars[c].month, randId);
|
||||
}
|
||||
|
||||
this.destroySelects();
|
||||
|
||||
this.calendarEl.innerHTML = html;
|
||||
|
||||
// Init Materialize Select
|
||||
let yearSelect = this.calendarEl.querySelector('.orig-select-year');
|
||||
let monthSelect = this.calendarEl.querySelector('.orig-select-month');
|
||||
M.FormSelect.init(yearSelect, {
|
||||
classes: 'select-year',
|
||||
dropdownOptions: { container: document.body, constrainWidth: false }
|
||||
});
|
||||
M.FormSelect.init(monthSelect, {
|
||||
classes: 'select-month',
|
||||
dropdownOptions: { container: document.body, constrainWidth: false }
|
||||
});
|
||||
|
||||
// Add change handlers for select
|
||||
yearSelect.addEventListener('change', this._handleYearChange.bind(this));
|
||||
monthSelect.addEventListener('change', this._handleMonthChange.bind(this));
|
||||
|
||||
if (typeof this.options.onDraw === 'function') {
|
||||
this.options.onDraw(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleInputKeydownBound = this._handleInputKeydown.bind(this);
|
||||
this._handleInputClickBound = this._handleInputClick.bind(this);
|
||||
this._handleInputChangeBound = this._handleInputChange.bind(this);
|
||||
this._handleCalendarClickBound = this._handleCalendarClick.bind(this);
|
||||
this._finishSelectionBound = this._finishSelection.bind(this);
|
||||
this._handleMonthChange = this._handleMonthChange.bind(this);
|
||||
this._closeBound = this.close.bind(this);
|
||||
|
||||
this.el.addEventListener('click', this._handleInputClickBound);
|
||||
this.el.addEventListener('keydown', this._handleInputKeydownBound);
|
||||
this.el.addEventListener('change', this._handleInputChangeBound);
|
||||
this.calendarEl.addEventListener('click', this._handleCalendarClickBound);
|
||||
this.doneBtn.addEventListener('click', this._finishSelectionBound);
|
||||
this.cancelBtn.addEventListener('click', this._closeBound);
|
||||
|
||||
if (this.options.showClearBtn) {
|
||||
this._handleClearClickBound = this._handleClearClick.bind(this);
|
||||
this.clearBtn.addEventListener('click', this._handleClearClickBound);
|
||||
}
|
||||
}
|
||||
|
||||
_setupVariables() {
|
||||
this.$modalEl = $(Datepicker._template);
|
||||
this.modalEl = this.$modalEl[0];
|
||||
|
||||
this.calendarEl = this.modalEl.querySelector('.datepicker-calendar');
|
||||
|
||||
this.yearTextEl = this.modalEl.querySelector('.year-text');
|
||||
this.dateTextEl = this.modalEl.querySelector('.date-text');
|
||||
if (this.options.showClearBtn) {
|
||||
this.clearBtn = this.modalEl.querySelector('.datepicker-clear');
|
||||
}
|
||||
this.doneBtn = this.modalEl.querySelector('.datepicker-done');
|
||||
this.cancelBtn = this.modalEl.querySelector('.datepicker-cancel');
|
||||
|
||||
this.formats = {
|
||||
d: () => {
|
||||
return this.date.getDate();
|
||||
},
|
||||
dd: () => {
|
||||
let d = this.date.getDate();
|
||||
return (d < 10 ? '0' : '') + d;
|
||||
},
|
||||
ddd: () => {
|
||||
return this.options.i18n.weekdaysShort[this.date.getDay()];
|
||||
},
|
||||
dddd: () => {
|
||||
return this.options.i18n.weekdays[this.date.getDay()];
|
||||
},
|
||||
m: () => {
|
||||
return this.date.getMonth() + 1;
|
||||
},
|
||||
mm: () => {
|
||||
let m = this.date.getMonth() + 1;
|
||||
return (m < 10 ? '0' : '') + m;
|
||||
},
|
||||
mmm: () => {
|
||||
return this.options.i18n.monthsShort[this.date.getMonth()];
|
||||
},
|
||||
mmmm: () => {
|
||||
return this.options.i18n.months[this.date.getMonth()];
|
||||
},
|
||||
yy: () => {
|
||||
return ('' + this.date.getFullYear()).slice(2);
|
||||
},
|
||||
yyyy: () => {
|
||||
return this.date.getFullYear();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
this.el.removeEventListener('click', this._handleInputClickBound);
|
||||
this.el.removeEventListener('keydown', this._handleInputKeydownBound);
|
||||
this.el.removeEventListener('change', this._handleInputChangeBound);
|
||||
this.calendarEl.removeEventListener('click', this._handleCalendarClickBound);
|
||||
}
|
||||
|
||||
_handleInputClick() {
|
||||
this.open();
|
||||
}
|
||||
|
||||
_handleInputKeydown(e) {
|
||||
if (e.which === M.keys.ENTER) {
|
||||
e.preventDefault();
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
|
||||
_handleCalendarClick(e) {
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
let $target = $(e.target);
|
||||
if (!$target.hasClass('is-disabled')) {
|
||||
if (
|
||||
$target.hasClass('datepicker-day-button') &&
|
||||
!$target.hasClass('is-empty') &&
|
||||
!$target.parent().hasClass('is-disabled')
|
||||
) {
|
||||
this.setDate(
|
||||
new Date(
|
||||
e.target.getAttribute('data-year'),
|
||||
e.target.getAttribute('data-month'),
|
||||
e.target.getAttribute('data-day')
|
||||
)
|
||||
);
|
||||
if (this.options.autoClose) {
|
||||
this._finishSelection();
|
||||
}
|
||||
} else if ($target.closest('.month-prev').length) {
|
||||
this.prevMonth();
|
||||
} else if ($target.closest('.month-next').length) {
|
||||
this.nextMonth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_handleClearClick() {
|
||||
this.date = null;
|
||||
this.setInputValue();
|
||||
this.close();
|
||||
}
|
||||
|
||||
_handleMonthChange(e) {
|
||||
this.gotoMonth(e.target.value);
|
||||
}
|
||||
|
||||
_handleYearChange(e) {
|
||||
this.gotoYear(e.target.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* change view to a specific month (zero-index, e.g. 0: January)
|
||||
*/
|
||||
gotoMonth(month) {
|
||||
if (!isNaN(month)) {
|
||||
this.calendars[0].month = parseInt(month, 10);
|
||||
this.adjustCalendars();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* change view to a specific full year (e.g. "2012")
|
||||
*/
|
||||
gotoYear(year) {
|
||||
if (!isNaN(year)) {
|
||||
this.calendars[0].year = parseInt(year, 10);
|
||||
this.adjustCalendars();
|
||||
}
|
||||
}
|
||||
|
||||
_handleInputChange(e) {
|
||||
let date;
|
||||
|
||||
// Prevent change event from being fired when triggered by the plugin
|
||||
if (e.firedBy === this) {
|
||||
return;
|
||||
}
|
||||
if (this.options.parse) {
|
||||
date = this.options.parse(this.el.value, this.options.format);
|
||||
} else {
|
||||
date = new Date(Date.parse(this.el.value));
|
||||
}
|
||||
|
||||
if (Datepicker._isDate(date)) {
|
||||
this.setDate(date);
|
||||
}
|
||||
}
|
||||
|
||||
renderDayName(opts, day, abbr) {
|
||||
day += opts.firstDay;
|
||||
while (day >= 7) {
|
||||
day -= 7;
|
||||
}
|
||||
return abbr ? opts.i18n.weekdaysAbbrev[day] : opts.i18n.weekdays[day];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set input value to the selected date and close Datepicker
|
||||
*/
|
||||
_finishSelection() {
|
||||
this.setInputValue();
|
||||
this.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open Datepicker
|
||||
*/
|
||||
open() {
|
||||
if (this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isOpen = true;
|
||||
if (typeof this.options.onOpen === 'function') {
|
||||
this.options.onOpen.call(this);
|
||||
}
|
||||
this.draw();
|
||||
this.modal.open();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close Datepicker
|
||||
*/
|
||||
close() {
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isOpen = false;
|
||||
if (typeof this.options.onClose === 'function') {
|
||||
this.options.onClose.call(this);
|
||||
}
|
||||
this.modal.close();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
Datepicker._template = [
|
||||
'<div class= "modal datepicker-modal">',
|
||||
'<div class="modal-content datepicker-container">',
|
||||
'<div class="datepicker-date-display">',
|
||||
'<span class="year-text"></span>',
|
||||
'<span class="date-text"></span>',
|
||||
'</div>',
|
||||
'<div class="datepicker-calendar-container">',
|
||||
'<div class="datepicker-calendar"></div>',
|
||||
'<div class="datepicker-footer">',
|
||||
'<button class="btn-flat datepicker-clear waves-effect" style="visibility: hidden;" type="button"></button>',
|
||||
'<div class="confirmation-btns">',
|
||||
'<button class="btn-flat datepicker-cancel waves-effect" type="button"></button>',
|
||||
'<button class="btn-flat datepicker-done waves-effect" type="button"></button>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join('');
|
||||
|
||||
M.Datepicker = Datepicker;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Datepicker, 'datepicker', 'M_Datepicker');
|
||||
}
|
||||
})(cash);
|
||||
|
|
@ -0,0 +1,617 @@
|
|||
(function($, anim) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
alignment: 'left',
|
||||
autoFocus: true,
|
||||
constrainWidth: true,
|
||||
container: null,
|
||||
coverTrigger: true,
|
||||
closeOnClick: true,
|
||||
hover: false,
|
||||
inDuration: 150,
|
||||
outDuration: 250,
|
||||
onOpenStart: null,
|
||||
onOpenEnd: null,
|
||||
onCloseStart: null,
|
||||
onCloseEnd: null,
|
||||
onItemClick: null
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
class Dropdown extends Component {
|
||||
constructor(el, options) {
|
||||
super(Dropdown, el, options);
|
||||
|
||||
this.el.M_Dropdown = this;
|
||||
Dropdown._dropdowns.push(this);
|
||||
|
||||
this.id = M.getIdFromTrigger(el);
|
||||
this.dropdownEl = document.getElementById(this.id);
|
||||
this.$dropdownEl = $(this.dropdownEl);
|
||||
|
||||
/**
|
||||
* Options for the dropdown
|
||||
* @member Dropdown#options
|
||||
* @prop {String} [alignment='left'] - Edge which the dropdown is aligned to
|
||||
* @prop {Boolean} [autoFocus=true] - Automatically focus dropdown el for keyboard
|
||||
* @prop {Boolean} [constrainWidth=true] - Constrain width to width of the button
|
||||
* @prop {Element} container - Container element to attach dropdown to (optional)
|
||||
* @prop {Boolean} [coverTrigger=true] - Place dropdown over trigger
|
||||
* @prop {Boolean} [closeOnClick=true] - Close on click of dropdown item
|
||||
* @prop {Boolean} [hover=false] - Open dropdown on hover
|
||||
* @prop {Number} [inDuration=150] - Duration of open animation in ms
|
||||
* @prop {Number} [outDuration=250] - Duration of close animation in ms
|
||||
* @prop {Function} onOpenStart - Function called when dropdown starts opening
|
||||
* @prop {Function} onOpenEnd - Function called when dropdown finishes opening
|
||||
* @prop {Function} onCloseStart - Function called when dropdown starts closing
|
||||
* @prop {Function} onCloseEnd - Function called when dropdown finishes closing
|
||||
*/
|
||||
this.options = $.extend({}, Dropdown.defaults, options);
|
||||
|
||||
/**
|
||||
* Describes open/close state of dropdown
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.isOpen = false;
|
||||
|
||||
/**
|
||||
* Describes if dropdown content is scrollable
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.isScrollable = false;
|
||||
|
||||
/**
|
||||
* Describes if touch moving on dropdown content
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.isTouchMoving = false;
|
||||
|
||||
this.focusedIndex = -1;
|
||||
this.filterQuery = [];
|
||||
|
||||
// Move dropdown-content after dropdown-trigger
|
||||
if (!!this.options.container) {
|
||||
$(this.options.container).append(this.dropdownEl);
|
||||
} else {
|
||||
this.$el.after(this.dropdownEl);
|
||||
}
|
||||
|
||||
this._makeDropdownFocusable();
|
||||
this._resetFilterQueryBound = this._resetFilterQuery.bind(this);
|
||||
this._handleDocumentClickBound = this._handleDocumentClick.bind(this);
|
||||
this._handleDocumentTouchmoveBound = this._handleDocumentTouchmove.bind(this);
|
||||
this._handleDropdownClickBound = this._handleDropdownClick.bind(this);
|
||||
this._handleDropdownKeydownBound = this._handleDropdownKeydown.bind(this);
|
||||
this._handleTriggerKeydownBound = this._handleTriggerKeydown.bind(this);
|
||||
this._setupEventHandlers();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Dropdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._resetDropdownStyles();
|
||||
this._removeEventHandlers();
|
||||
Dropdown._dropdowns.splice(Dropdown._dropdowns.indexOf(this), 1);
|
||||
this.el.M_Dropdown = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
// Trigger keydown handler
|
||||
this.el.addEventListener('keydown', this._handleTriggerKeydownBound);
|
||||
|
||||
// Item click handler
|
||||
this.dropdownEl.addEventListener('click', this._handleDropdownClickBound);
|
||||
|
||||
// Hover event handlers
|
||||
if (this.options.hover) {
|
||||
this._handleMouseEnterBound = this._handleMouseEnter.bind(this);
|
||||
this.el.addEventListener('mouseenter', this._handleMouseEnterBound);
|
||||
this._handleMouseLeaveBound = this._handleMouseLeave.bind(this);
|
||||
this.el.addEventListener('mouseleave', this._handleMouseLeaveBound);
|
||||
this.dropdownEl.addEventListener('mouseleave', this._handleMouseLeaveBound);
|
||||
|
||||
// Click event handlers
|
||||
} else {
|
||||
this._handleClickBound = this._handleClick.bind(this);
|
||||
this.el.addEventListener('click', this._handleClickBound);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
this.el.removeEventListener('keydown', this._handleTriggerKeydownBound);
|
||||
this.dropdownEl.removeEventListener('click', this._handleDropdownClickBound);
|
||||
|
||||
if (this.options.hover) {
|
||||
this.el.removeEventListener('mouseenter', this._handleMouseEnterBound);
|
||||
this.el.removeEventListener('mouseleave', this._handleMouseLeaveBound);
|
||||
this.dropdownEl.removeEventListener('mouseleave', this._handleMouseLeaveBound);
|
||||
} else {
|
||||
this.el.removeEventListener('click', this._handleClickBound);
|
||||
}
|
||||
}
|
||||
|
||||
_setupTemporaryEventHandlers() {
|
||||
// Use capture phase event handler to prevent click
|
||||
document.body.addEventListener('click', this._handleDocumentClickBound, true);
|
||||
document.body.addEventListener('touchend', this._handleDocumentClickBound);
|
||||
document.body.addEventListener('touchmove', this._handleDocumentTouchmoveBound);
|
||||
this.dropdownEl.addEventListener('keydown', this._handleDropdownKeydownBound);
|
||||
}
|
||||
|
||||
_removeTemporaryEventHandlers() {
|
||||
// Use capture phase event handler to prevent click
|
||||
document.body.removeEventListener('click', this._handleDocumentClickBound, true);
|
||||
document.body.removeEventListener('touchend', this._handleDocumentClickBound);
|
||||
document.body.removeEventListener('touchmove', this._handleDocumentTouchmoveBound);
|
||||
this.dropdownEl.removeEventListener('keydown', this._handleDropdownKeydownBound);
|
||||
}
|
||||
|
||||
_handleClick(e) {
|
||||
e.preventDefault();
|
||||
this.open();
|
||||
}
|
||||
|
||||
_handleMouseEnter() {
|
||||
this.open();
|
||||
}
|
||||
|
||||
_handleMouseLeave(e) {
|
||||
let toEl = e.toElement || e.relatedTarget;
|
||||
let leaveToDropdownContent = !!$(toEl).closest('.dropdown-content').length;
|
||||
let leaveToActiveDropdownTrigger = false;
|
||||
|
||||
let $closestTrigger = $(toEl).closest('.dropdown-trigger');
|
||||
if (
|
||||
$closestTrigger.length &&
|
||||
!!$closestTrigger[0].M_Dropdown &&
|
||||
$closestTrigger[0].M_Dropdown.isOpen
|
||||
) {
|
||||
leaveToActiveDropdownTrigger = true;
|
||||
}
|
||||
|
||||
// Close hover dropdown if mouse did not leave to either active dropdown-trigger or dropdown-content
|
||||
if (!leaveToActiveDropdownTrigger && !leaveToDropdownContent) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
_handleDocumentClick(e) {
|
||||
let $target = $(e.target);
|
||||
if (
|
||||
this.options.closeOnClick &&
|
||||
$target.closest('.dropdown-content').length &&
|
||||
!this.isTouchMoving
|
||||
) {
|
||||
// isTouchMoving to check if scrolling on mobile.
|
||||
setTimeout(() => {
|
||||
this.close();
|
||||
}, 0);
|
||||
} else if (
|
||||
$target.closest('.dropdown-trigger').length ||
|
||||
!$target.closest('.dropdown-content').length
|
||||
) {
|
||||
setTimeout(() => {
|
||||
this.close();
|
||||
}, 0);
|
||||
}
|
||||
this.isTouchMoving = false;
|
||||
}
|
||||
|
||||
_handleTriggerKeydown(e) {
|
||||
// ARROW DOWN OR ENTER WHEN SELECT IS CLOSED - open Dropdown
|
||||
if ((e.which === M.keys.ARROW_DOWN || e.which === M.keys.ENTER) && !this.isOpen) {
|
||||
e.preventDefault();
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Document Touchmove
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleDocumentTouchmove(e) {
|
||||
let $target = $(e.target);
|
||||
if ($target.closest('.dropdown-content').length) {
|
||||
this.isTouchMoving = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Dropdown Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleDropdownClick(e) {
|
||||
// onItemClick callback
|
||||
if (typeof this.options.onItemClick === 'function') {
|
||||
let itemEl = $(e.target).closest('li')[0];
|
||||
this.options.onItemClick.call(this, itemEl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Dropdown Keydown
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleDropdownKeydown(e) {
|
||||
if (e.which === M.keys.TAB) {
|
||||
e.preventDefault();
|
||||
this.close();
|
||||
|
||||
// Navigate down dropdown list
|
||||
} else if ((e.which === M.keys.ARROW_DOWN || e.which === M.keys.ARROW_UP) && this.isOpen) {
|
||||
e.preventDefault();
|
||||
let direction = e.which === M.keys.ARROW_DOWN ? 1 : -1;
|
||||
let newFocusedIndex = this.focusedIndex;
|
||||
let foundNewIndex = false;
|
||||
do {
|
||||
newFocusedIndex = newFocusedIndex + direction;
|
||||
|
||||
if (
|
||||
!!this.dropdownEl.children[newFocusedIndex] &&
|
||||
this.dropdownEl.children[newFocusedIndex].tabIndex !== -1
|
||||
) {
|
||||
foundNewIndex = true;
|
||||
break;
|
||||
}
|
||||
} while (newFocusedIndex < this.dropdownEl.children.length && newFocusedIndex >= 0);
|
||||
|
||||
if (foundNewIndex) {
|
||||
this.focusedIndex = newFocusedIndex;
|
||||
this._focusFocusedItem();
|
||||
}
|
||||
|
||||
// ENTER selects choice on focused item
|
||||
} else if (e.which === M.keys.ENTER && this.isOpen) {
|
||||
// Search for <a> and <button>
|
||||
let focusedElement = this.dropdownEl.children[this.focusedIndex];
|
||||
let $activatableElement = $(focusedElement)
|
||||
.find('a, button')
|
||||
.first();
|
||||
|
||||
// Click a or button tag if exists, otherwise click li tag
|
||||
if (!!$activatableElement.length) {
|
||||
$activatableElement[0].click();
|
||||
} else if (!!focusedElement) {
|
||||
focusedElement.click();
|
||||
}
|
||||
|
||||
// Close dropdown on ESC
|
||||
} else if (e.which === M.keys.ESC && this.isOpen) {
|
||||
e.preventDefault();
|
||||
this.close();
|
||||
}
|
||||
|
||||
// CASE WHEN USER TYPE LETTERS
|
||||
let letter = String.fromCharCode(e.which).toLowerCase(),
|
||||
nonLetters = [9, 13, 27, 38, 40];
|
||||
if (letter && nonLetters.indexOf(e.which) === -1) {
|
||||
this.filterQuery.push(letter);
|
||||
|
||||
let string = this.filterQuery.join(''),
|
||||
newOptionEl = $(this.dropdownEl)
|
||||
.find('li')
|
||||
.filter((el) => {
|
||||
return (
|
||||
$(el)
|
||||
.text()
|
||||
.toLowerCase()
|
||||
.indexOf(string) === 0
|
||||
);
|
||||
})[0];
|
||||
|
||||
if (newOptionEl) {
|
||||
this.focusedIndex = $(newOptionEl).index();
|
||||
this._focusFocusedItem();
|
||||
}
|
||||
}
|
||||
|
||||
this.filterTimeout = setTimeout(this._resetFilterQueryBound, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup dropdown
|
||||
*/
|
||||
_resetFilterQuery() {
|
||||
this.filterQuery = [];
|
||||
}
|
||||
|
||||
_resetDropdownStyles() {
|
||||
this.$dropdownEl.css({
|
||||
display: '',
|
||||
width: '',
|
||||
height: '',
|
||||
left: '',
|
||||
top: '',
|
||||
'transform-origin': '',
|
||||
transform: '',
|
||||
opacity: ''
|
||||
});
|
||||
}
|
||||
|
||||
_makeDropdownFocusable() {
|
||||
// Needed for arrow key navigation
|
||||
this.dropdownEl.tabIndex = 0;
|
||||
|
||||
// Only set tabindex if it hasn't been set by user
|
||||
$(this.dropdownEl)
|
||||
.children()
|
||||
.each(function(el) {
|
||||
if (!el.getAttribute('tabindex')) {
|
||||
el.setAttribute('tabindex', 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_focusFocusedItem() {
|
||||
if (
|
||||
this.focusedIndex >= 0 &&
|
||||
this.focusedIndex < this.dropdownEl.children.length &&
|
||||
this.options.autoFocus
|
||||
) {
|
||||
this.dropdownEl.children[this.focusedIndex].focus();
|
||||
}
|
||||
}
|
||||
|
||||
_getDropdownPosition() {
|
||||
let offsetParentBRect = this.el.offsetParent.getBoundingClientRect();
|
||||
let triggerBRect = this.el.getBoundingClientRect();
|
||||
let dropdownBRect = this.dropdownEl.getBoundingClientRect();
|
||||
|
||||
let idealHeight = dropdownBRect.height;
|
||||
let idealWidth = dropdownBRect.width;
|
||||
let idealXPos = triggerBRect.left - dropdownBRect.left;
|
||||
let idealYPos = triggerBRect.top - dropdownBRect.top;
|
||||
|
||||
let dropdownBounds = {
|
||||
left: idealXPos,
|
||||
top: idealYPos,
|
||||
height: idealHeight,
|
||||
width: idealWidth
|
||||
};
|
||||
|
||||
// Countainer here will be closest ancestor with overflow: hidden
|
||||
let closestOverflowParent = !!this.dropdownEl.offsetParent
|
||||
? this.dropdownEl.offsetParent
|
||||
: this.dropdownEl.parentNode;
|
||||
|
||||
let alignments = M.checkPossibleAlignments(
|
||||
this.el,
|
||||
closestOverflowParent,
|
||||
dropdownBounds,
|
||||
this.options.coverTrigger ? 0 : triggerBRect.height
|
||||
);
|
||||
|
||||
let verticalAlignment = 'top';
|
||||
let horizontalAlignment = this.options.alignment;
|
||||
idealYPos += this.options.coverTrigger ? 0 : triggerBRect.height;
|
||||
|
||||
// Reset isScrollable
|
||||
this.isScrollable = false;
|
||||
|
||||
if (!alignments.top) {
|
||||
if (alignments.bottom) {
|
||||
verticalAlignment = 'bottom';
|
||||
} else {
|
||||
this.isScrollable = true;
|
||||
|
||||
// Determine which side has most space and cutoff at correct height
|
||||
if (alignments.spaceOnTop > alignments.spaceOnBottom) {
|
||||
verticalAlignment = 'bottom';
|
||||
idealHeight += alignments.spaceOnTop;
|
||||
idealYPos -= alignments.spaceOnTop;
|
||||
} else {
|
||||
idealHeight += alignments.spaceOnBottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If preferred horizontal alignment is possible
|
||||
if (!alignments[horizontalAlignment]) {
|
||||
let oppositeAlignment = horizontalAlignment === 'left' ? 'right' : 'left';
|
||||
if (alignments[oppositeAlignment]) {
|
||||
horizontalAlignment = oppositeAlignment;
|
||||
} else {
|
||||
// Determine which side has most space and cutoff at correct height
|
||||
if (alignments.spaceOnLeft > alignments.spaceOnRight) {
|
||||
horizontalAlignment = 'right';
|
||||
idealWidth += alignments.spaceOnLeft;
|
||||
idealXPos -= alignments.spaceOnLeft;
|
||||
} else {
|
||||
horizontalAlignment = 'left';
|
||||
idealWidth += alignments.spaceOnRight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (verticalAlignment === 'bottom') {
|
||||
idealYPos =
|
||||
idealYPos - dropdownBRect.height + (this.options.coverTrigger ? triggerBRect.height : 0);
|
||||
}
|
||||
if (horizontalAlignment === 'right') {
|
||||
idealXPos = idealXPos - dropdownBRect.width + triggerBRect.width;
|
||||
}
|
||||
return {
|
||||
x: idealXPos,
|
||||
y: idealYPos,
|
||||
verticalAlignment: verticalAlignment,
|
||||
horizontalAlignment: horizontalAlignment,
|
||||
height: idealHeight,
|
||||
width: idealWidth
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate in dropdown
|
||||
*/
|
||||
_animateIn() {
|
||||
anim.remove(this.dropdownEl);
|
||||
anim({
|
||||
targets: this.dropdownEl,
|
||||
opacity: {
|
||||
value: [0, 1],
|
||||
easing: 'easeOutQuad'
|
||||
},
|
||||
scaleX: [0.3, 1],
|
||||
scaleY: [0.3, 1],
|
||||
duration: this.options.inDuration,
|
||||
easing: 'easeOutQuint',
|
||||
complete: (anim) => {
|
||||
if (this.options.autoFocus) {
|
||||
this.dropdownEl.focus();
|
||||
}
|
||||
|
||||
// onOpenEnd callback
|
||||
if (typeof this.options.onOpenEnd === 'function') {
|
||||
this.options.onOpenEnd.call(this, this.el);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate out dropdown
|
||||
*/
|
||||
_animateOut() {
|
||||
anim.remove(this.dropdownEl);
|
||||
anim({
|
||||
targets: this.dropdownEl,
|
||||
opacity: {
|
||||
value: 0,
|
||||
easing: 'easeOutQuint'
|
||||
},
|
||||
scaleX: 0.3,
|
||||
scaleY: 0.3,
|
||||
duration: this.options.outDuration,
|
||||
easing: 'easeOutQuint',
|
||||
complete: (anim) => {
|
||||
this._resetDropdownStyles();
|
||||
|
||||
// onCloseEnd callback
|
||||
if (typeof this.options.onCloseEnd === 'function') {
|
||||
this.options.onCloseEnd.call(this, this.el);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Place dropdown
|
||||
*/
|
||||
_placeDropdown() {
|
||||
// Set width before calculating positionInfo
|
||||
let idealWidth = this.options.constrainWidth
|
||||
? this.el.getBoundingClientRect().width
|
||||
: this.dropdownEl.getBoundingClientRect().width;
|
||||
this.dropdownEl.style.width = idealWidth + 'px';
|
||||
|
||||
let positionInfo = this._getDropdownPosition();
|
||||
this.dropdownEl.style.left = positionInfo.x + 'px';
|
||||
this.dropdownEl.style.top = positionInfo.y + 'px';
|
||||
this.dropdownEl.style.height = positionInfo.height + 'px';
|
||||
this.dropdownEl.style.width = positionInfo.width + 'px';
|
||||
this.dropdownEl.style.transformOrigin = `${
|
||||
positionInfo.horizontalAlignment === 'left' ? '0' : '100%'
|
||||
} ${positionInfo.verticalAlignment === 'top' ? '0' : '100%'}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open Dropdown
|
||||
*/
|
||||
open() {
|
||||
if (this.isOpen) {
|
||||
return;
|
||||
}
|
||||
this.isOpen = true;
|
||||
|
||||
// onOpenStart callback
|
||||
if (typeof this.options.onOpenStart === 'function') {
|
||||
this.options.onOpenStart.call(this, this.el);
|
||||
}
|
||||
|
||||
// Reset styles
|
||||
this._resetDropdownStyles();
|
||||
this.dropdownEl.style.display = 'block';
|
||||
|
||||
this._placeDropdown();
|
||||
this._animateIn();
|
||||
this._setupTemporaryEventHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close Dropdown
|
||||
*/
|
||||
close() {
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
this.isOpen = false;
|
||||
this.focusedIndex = -1;
|
||||
|
||||
// onCloseStart callback
|
||||
if (typeof this.options.onCloseStart === 'function') {
|
||||
this.options.onCloseStart.call(this, this.el);
|
||||
}
|
||||
|
||||
this._animateOut();
|
||||
this._removeTemporaryEventHandlers();
|
||||
|
||||
if (this.options.autoFocus) {
|
||||
this.el.focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculate dimensions
|
||||
*/
|
||||
recalculateDimensions() {
|
||||
if (this.isOpen) {
|
||||
this.$dropdownEl.css({
|
||||
width: '',
|
||||
height: '',
|
||||
left: '',
|
||||
top: '',
|
||||
'transform-origin': ''
|
||||
});
|
||||
this._placeDropdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof Dropdown
|
||||
*/
|
||||
Dropdown._dropdowns = [];
|
||||
|
||||
M.Dropdown = Dropdown;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Dropdown, 'dropdown', 'M_Dropdown');
|
||||
}
|
||||
})(cash, M.anime);
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
(function($) {
|
||||
// Function to update labels of text fields
|
||||
M.updateTextFields = function() {
|
||||
let input_selector =
|
||||
'input[type=text], input[type=password], input[type=email], input[type=url], input[type=tel], input[type=number], input[type=search], input[type=date], input[type=time], textarea';
|
||||
$(input_selector).each(function(element, index) {
|
||||
let $this = $(this);
|
||||
if (
|
||||
element.value.length > 0 ||
|
||||
$(element).is(':focus') ||
|
||||
element.autofocus ||
|
||||
$this.attr('placeholder') !== null
|
||||
) {
|
||||
$this.siblings('label').addClass('active');
|
||||
} else if (element.validity) {
|
||||
$this.siblings('label').toggleClass('active', element.validity.badInput === true);
|
||||
} else {
|
||||
$this.siblings('label').removeClass('active');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
M.validate_field = function(object) {
|
||||
let hasLength = object.attr('data-length') !== null;
|
||||
let lenAttr = parseInt(object.attr('data-length'));
|
||||
let len = object[0].value.length;
|
||||
|
||||
if (len === 0 && object[0].validity.badInput === false && !object.is(':required')) {
|
||||
if (object.hasClass('validate')) {
|
||||
object.removeClass('valid');
|
||||
object.removeClass('invalid');
|
||||
}
|
||||
} else {
|
||||
if (object.hasClass('validate')) {
|
||||
// Check for character counter attributes
|
||||
if (
|
||||
(object.is(':valid') && hasLength && len <= lenAttr) ||
|
||||
(object.is(':valid') && !hasLength)
|
||||
) {
|
||||
object.removeClass('invalid');
|
||||
object.addClass('valid');
|
||||
} else {
|
||||
object.removeClass('valid');
|
||||
object.addClass('invalid');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
M.textareaAutoResize = function($textarea) {
|
||||
// Wrap if native element
|
||||
if ($textarea instanceof Element) {
|
||||
$textarea = $($textarea);
|
||||
}
|
||||
|
||||
if (!$textarea.length) {
|
||||
console.error('No textarea element found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Textarea Auto Resize
|
||||
let hiddenDiv = $('.hiddendiv').first();
|
||||
if (!hiddenDiv.length) {
|
||||
hiddenDiv = $('<div class="hiddendiv common"></div>');
|
||||
$('body').append(hiddenDiv);
|
||||
}
|
||||
|
||||
// Set font properties of hiddenDiv
|
||||
let fontFamily = $textarea.css('font-family');
|
||||
let fontSize = $textarea.css('font-size');
|
||||
let lineHeight = $textarea.css('line-height');
|
||||
|
||||
// Firefox can't handle padding shorthand.
|
||||
let paddingTop = $textarea.css('padding-top');
|
||||
let paddingRight = $textarea.css('padding-right');
|
||||
let paddingBottom = $textarea.css('padding-bottom');
|
||||
let paddingLeft = $textarea.css('padding-left');
|
||||
|
||||
if (fontSize) {
|
||||
hiddenDiv.css('font-size', fontSize);
|
||||
}
|
||||
if (fontFamily) {
|
||||
hiddenDiv.css('font-family', fontFamily);
|
||||
}
|
||||
if (lineHeight) {
|
||||
hiddenDiv.css('line-height', lineHeight);
|
||||
}
|
||||
if (paddingTop) {
|
||||
hiddenDiv.css('padding-top', paddingTop);
|
||||
}
|
||||
if (paddingRight) {
|
||||
hiddenDiv.css('padding-right', paddingRight);
|
||||
}
|
||||
if (paddingBottom) {
|
||||
hiddenDiv.css('padding-bottom', paddingBottom);
|
||||
}
|
||||
if (paddingLeft) {
|
||||
hiddenDiv.css('padding-left', paddingLeft);
|
||||
}
|
||||
|
||||
// Set original-height, if none
|
||||
if (!$textarea.data('original-height')) {
|
||||
$textarea.data('original-height', $textarea.height());
|
||||
}
|
||||
|
||||
if ($textarea.attr('wrap') === 'off') {
|
||||
hiddenDiv.css('overflow-wrap', 'normal').css('white-space', 'pre');
|
||||
}
|
||||
|
||||
hiddenDiv.text($textarea[0].value + '\n');
|
||||
let content = hiddenDiv.html().replace(/\n/g, '<br>');
|
||||
hiddenDiv.html(content);
|
||||
|
||||
// When textarea is hidden, width goes crazy.
|
||||
// Approximate with half of window size
|
||||
|
||||
if ($textarea[0].offsetWidth > 0 && $textarea[0].offsetHeight > 0) {
|
||||
hiddenDiv.css('width', $textarea.width() + 'px');
|
||||
} else {
|
||||
hiddenDiv.css('width', window.innerWidth / 2 + 'px');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize if the new height is greater than the
|
||||
* original height of the textarea
|
||||
*/
|
||||
if ($textarea.data('original-height') <= hiddenDiv.innerHeight()) {
|
||||
$textarea.css('height', hiddenDiv.innerHeight() + 'px');
|
||||
} else if ($textarea[0].value.length < $textarea.data('previous-length')) {
|
||||
/**
|
||||
* In case the new height is less than original height, it
|
||||
* means the textarea has less text than before
|
||||
* So we set the height to the original one
|
||||
*/
|
||||
$textarea.css('height', $textarea.data('original-height') + 'px');
|
||||
}
|
||||
$textarea.data('previous-length', $textarea[0].value.length);
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
// Text based inputs
|
||||
let input_selector =
|
||||
'input[type=text], input[type=password], input[type=email], input[type=url], input[type=tel], input[type=number], input[type=search], input[type=date], input[type=time], textarea';
|
||||
|
||||
// Add active if form auto complete
|
||||
$(document).on('change', input_selector, function() {
|
||||
if (this.value.length !== 0 || $(this).attr('placeholder') !== null) {
|
||||
$(this)
|
||||
.siblings('label')
|
||||
.addClass('active');
|
||||
}
|
||||
M.validate_field($(this));
|
||||
});
|
||||
|
||||
// Add active if input element has been pre-populated on document ready
|
||||
$(document).ready(function() {
|
||||
M.updateTextFields();
|
||||
});
|
||||
|
||||
// HTML DOM FORM RESET handling
|
||||
$(document).on('reset', function(e) {
|
||||
let formReset = $(e.target);
|
||||
if (formReset.is('form')) {
|
||||
formReset
|
||||
.find(input_selector)
|
||||
.removeClass('valid')
|
||||
.removeClass('invalid');
|
||||
formReset.find(input_selector).each(function(e) {
|
||||
if (this.value.length) {
|
||||
$(this)
|
||||
.siblings('label')
|
||||
.removeClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Reset select (after native reset)
|
||||
setTimeout(function() {
|
||||
formReset.find('select').each(function() {
|
||||
// check if initialized
|
||||
if (this.M_FormSelect) {
|
||||
$(this).trigger('change');
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Add active when element has focus
|
||||
* @param {Event} e
|
||||
*/
|
||||
document.addEventListener(
|
||||
'focus',
|
||||
function(e) {
|
||||
if ($(e.target).is(input_selector)) {
|
||||
$(e.target)
|
||||
.siblings('label, .prefix')
|
||||
.addClass('active');
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
/**
|
||||
* Remove active when element is blurred
|
||||
* @param {Event} e
|
||||
*/
|
||||
document.addEventListener(
|
||||
'blur',
|
||||
function(e) {
|
||||
let $inputElement = $(e.target);
|
||||
if ($inputElement.is(input_selector)) {
|
||||
let selector = '.prefix';
|
||||
|
||||
if (
|
||||
$inputElement[0].value.length === 0 &&
|
||||
$inputElement[0].validity.badInput !== true &&
|
||||
$inputElement.attr('placeholder') === null
|
||||
) {
|
||||
selector += ', label';
|
||||
}
|
||||
$inputElement.siblings(selector).removeClass('active');
|
||||
M.validate_field($inputElement);
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
// Radio and Checkbox focus class
|
||||
let radio_checkbox = 'input[type=radio], input[type=checkbox]';
|
||||
$(document).on('keyup', radio_checkbox, function(e) {
|
||||
// TAB, check if tabbing to radio or checkbox.
|
||||
if (e.which === M.keys.TAB) {
|
||||
$(this).addClass('tabbed');
|
||||
let $this = $(this);
|
||||
$this.one('blur', function(e) {
|
||||
$(this).removeClass('tabbed');
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
let text_area_selector = '.materialize-textarea';
|
||||
$(text_area_selector).each(function() {
|
||||
let $textarea = $(this);
|
||||
/**
|
||||
* Resize textarea on document load after storing
|
||||
* the original height and the original length
|
||||
*/
|
||||
$textarea.data('original-height', $textarea.height());
|
||||
$textarea.data('previous-length', this.value.length);
|
||||
M.textareaAutoResize($textarea);
|
||||
});
|
||||
|
||||
$(document).on('keyup', text_area_selector, function() {
|
||||
M.textareaAutoResize($(this));
|
||||
});
|
||||
$(document).on('keydown', text_area_selector, function() {
|
||||
M.textareaAutoResize($(this));
|
||||
});
|
||||
|
||||
// File Input Path
|
||||
$(document).on('change', '.file-field input[type="file"]', function() {
|
||||
let file_field = $(this).closest('.file-field');
|
||||
let path_input = file_field.find('input.file-path');
|
||||
let files = $(this)[0].files;
|
||||
let file_names = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
file_names.push(files[i].name);
|
||||
}
|
||||
path_input[0].value = file_names.join(', ');
|
||||
path_input.trigger('change');
|
||||
});
|
||||
}); // End of $(document).ready
|
||||
})(cash);
|
||||
|
|
@ -0,0 +1,427 @@
|
|||
// Required for Meteor package, the use of window prevents export by Meteor
|
||||
(function(window) {
|
||||
if (window.Package) {
|
||||
M = {};
|
||||
} else {
|
||||
window.M = {};
|
||||
}
|
||||
|
||||
// Check for jQuery
|
||||
M.jQueryLoaded = !!window.jQuery;
|
||||
})(window);
|
||||
|
||||
// AMD
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define('M', [], function() {
|
||||
return M;
|
||||
});
|
||||
|
||||
// Common JS
|
||||
} else if (typeof exports !== 'undefined' && !exports.nodeType) {
|
||||
if (typeof module !== 'undefined' && !module.nodeType && module.exports) {
|
||||
exports = module.exports = M;
|
||||
}
|
||||
exports.default = M;
|
||||
}
|
||||
|
||||
M.version = '1.0.0';
|
||||
|
||||
M.keys = {
|
||||
TAB: 9,
|
||||
ENTER: 13,
|
||||
ESC: 27,
|
||||
ARROW_UP: 38,
|
||||
ARROW_DOWN: 40
|
||||
};
|
||||
|
||||
/**
|
||||
* TabPress Keydown handler
|
||||
*/
|
||||
M.tabPressed = false;
|
||||
M.keyDown = false;
|
||||
let docHandleKeydown = function(e) {
|
||||
M.keyDown = true;
|
||||
if (e.which === M.keys.TAB || e.which === M.keys.ARROW_DOWN || e.which === M.keys.ARROW_UP) {
|
||||
M.tabPressed = true;
|
||||
}
|
||||
};
|
||||
let docHandleKeyup = function(e) {
|
||||
M.keyDown = false;
|
||||
if (e.which === M.keys.TAB || e.which === M.keys.ARROW_DOWN || e.which === M.keys.ARROW_UP) {
|
||||
M.tabPressed = false;
|
||||
}
|
||||
};
|
||||
let docHandleFocus = function(e) {
|
||||
if (M.keyDown) {
|
||||
document.body.classList.add('keyboard-focused');
|
||||
}
|
||||
};
|
||||
let docHandleBlur = function(e) {
|
||||
document.body.classList.remove('keyboard-focused');
|
||||
};
|
||||
document.addEventListener('keydown', docHandleKeydown, true);
|
||||
document.addEventListener('keyup', docHandleKeyup, true);
|
||||
document.addEventListener('focus', docHandleFocus, true);
|
||||
document.addEventListener('blur', docHandleBlur, true);
|
||||
|
||||
/**
|
||||
* Initialize jQuery wrapper for plugin
|
||||
* @param {Class} plugin javascript class
|
||||
* @param {string} pluginName jQuery plugin name
|
||||
* @param {string} classRef Class reference name
|
||||
*/
|
||||
M.initializeJqueryWrapper = function(plugin, pluginName, classRef) {
|
||||
jQuery.fn[pluginName] = function(methodOrOptions) {
|
||||
// Call plugin method if valid method name is passed in
|
||||
if (plugin.prototype[methodOrOptions]) {
|
||||
let params = Array.prototype.slice.call(arguments, 1);
|
||||
|
||||
// Getter methods
|
||||
if (methodOrOptions.slice(0, 3) === 'get') {
|
||||
let instance = this.first()[0][classRef];
|
||||
return instance[methodOrOptions].apply(instance, params);
|
||||
}
|
||||
|
||||
// Void methods
|
||||
return this.each(function() {
|
||||
let instance = this[classRef];
|
||||
instance[methodOrOptions].apply(instance, params);
|
||||
});
|
||||
|
||||
// Initialize plugin if options or no argument is passed in
|
||||
} else if (typeof methodOrOptions === 'object' || !methodOrOptions) {
|
||||
plugin.init(this, arguments[0]);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Return error if an unrecognized method name is passed in
|
||||
jQuery.error(`Method ${methodOrOptions} does not exist on jQuery.${pluginName}`);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Automatically initialize components
|
||||
* @param {Element} context DOM Element to search within for components
|
||||
*/
|
||||
M.AutoInit = function(context) {
|
||||
// Use document.body if no context is given
|
||||
let root = !!context ? context : document.body;
|
||||
|
||||
let registry = {
|
||||
Autocomplete: root.querySelectorAll('.autocomplete:not(.no-autoinit)'),
|
||||
Carousel: root.querySelectorAll('.carousel:not(.no-autoinit)'),
|
||||
Chips: root.querySelectorAll('.chips:not(.no-autoinit)'),
|
||||
Collapsible: root.querySelectorAll('.collapsible:not(.no-autoinit)'),
|
||||
Datepicker: root.querySelectorAll('.datepicker:not(.no-autoinit)'),
|
||||
Dropdown: root.querySelectorAll('.dropdown-trigger:not(.no-autoinit)'),
|
||||
Materialbox: root.querySelectorAll('.materialboxed:not(.no-autoinit)'),
|
||||
Modal: root.querySelectorAll('.modal:not(.no-autoinit)'),
|
||||
Parallax: root.querySelectorAll('.parallax:not(.no-autoinit)'),
|
||||
Pushpin: root.querySelectorAll('.pushpin:not(.no-autoinit)'),
|
||||
ScrollSpy: root.querySelectorAll('.scrollspy:not(.no-autoinit)'),
|
||||
FormSelect: root.querySelectorAll('select:not(.no-autoinit)'),
|
||||
Sidenav: root.querySelectorAll('.sidenav:not(.no-autoinit)'),
|
||||
Tabs: root.querySelectorAll('.tabs:not(.no-autoinit)'),
|
||||
TapTarget: root.querySelectorAll('.tap-target:not(.no-autoinit)'),
|
||||
Timepicker: root.querySelectorAll('.timepicker:not(.no-autoinit)'),
|
||||
Tooltip: root.querySelectorAll('.tooltipped:not(.no-autoinit)'),
|
||||
FloatingActionButton: root.querySelectorAll('.fixed-action-btn:not(.no-autoinit)')
|
||||
};
|
||||
|
||||
for (let pluginName in registry) {
|
||||
let plugin = M[pluginName];
|
||||
plugin.init(registry[pluginName]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate approximated selector string for a jQuery object
|
||||
* @param {jQuery} obj jQuery object to be parsed
|
||||
* @returns {string}
|
||||
*/
|
||||
M.objectSelectorString = function(obj) {
|
||||
let tagStr = obj.prop('tagName') || '';
|
||||
let idStr = obj.attr('id') || '';
|
||||
let classStr = obj.attr('class') || '';
|
||||
return (tagStr + idStr + classStr).replace(/\s/g, '');
|
||||
};
|
||||
|
||||
// Unique Random ID
|
||||
M.guid = (function() {
|
||||
function s4() {
|
||||
return Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
}
|
||||
return function() {
|
||||
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
* Escapes hash from special characters
|
||||
* @param {string} hash String returned from this.hash
|
||||
* @returns {string}
|
||||
*/
|
||||
M.escapeHash = function(hash) {
|
||||
return hash.replace(/(:|\.|\[|\]|,|=|\/)/g, '\\$1');
|
||||
};
|
||||
|
||||
M.elementOrParentIsFixed = function(element) {
|
||||
let $element = $(element);
|
||||
let $checkElements = $element.add($element.parents());
|
||||
let isFixed = false;
|
||||
$checkElements.each(function() {
|
||||
if ($(this).css('position') === 'fixed') {
|
||||
isFixed = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return isFixed;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Object} Edges
|
||||
* @property {Boolean} top If the top edge was exceeded
|
||||
* @property {Boolean} right If the right edge was exceeded
|
||||
* @property {Boolean} bottom If the bottom edge was exceeded
|
||||
* @property {Boolean} left If the left edge was exceeded
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Bounding
|
||||
* @property {Number} left left offset coordinate
|
||||
* @property {Number} top top offset coordinate
|
||||
* @property {Number} width
|
||||
* @property {Number} height
|
||||
*/
|
||||
|
||||
/**
|
||||
* Escapes hash from special characters
|
||||
* @param {Element} container Container element that acts as the boundary
|
||||
* @param {Bounding} bounding element bounding that is being checked
|
||||
* @param {Number} offset offset from edge that counts as exceeding
|
||||
* @returns {Edges}
|
||||
*/
|
||||
M.checkWithinContainer = function(container, bounding, offset) {
|
||||
let edges = {
|
||||
top: false,
|
||||
right: false,
|
||||
bottom: false,
|
||||
left: false
|
||||
};
|
||||
|
||||
let containerRect = container.getBoundingClientRect();
|
||||
// If body element is smaller than viewport, use viewport height instead.
|
||||
let containerBottom =
|
||||
container === document.body
|
||||
? Math.max(containerRect.bottom, window.innerHeight)
|
||||
: containerRect.bottom;
|
||||
|
||||
let scrollLeft = container.scrollLeft;
|
||||
let scrollTop = container.scrollTop;
|
||||
|
||||
let scrolledX = bounding.left - scrollLeft;
|
||||
let scrolledY = bounding.top - scrollTop;
|
||||
|
||||
// Check for container and viewport for each edge
|
||||
if (scrolledX < containerRect.left + offset || scrolledX < offset) {
|
||||
edges.left = true;
|
||||
}
|
||||
|
||||
if (
|
||||
scrolledX + bounding.width > containerRect.right - offset ||
|
||||
scrolledX + bounding.width > window.innerWidth - offset
|
||||
) {
|
||||
edges.right = true;
|
||||
}
|
||||
|
||||
if (scrolledY < containerRect.top + offset || scrolledY < offset) {
|
||||
edges.top = true;
|
||||
}
|
||||
|
||||
if (
|
||||
scrolledY + bounding.height > containerBottom - offset ||
|
||||
scrolledY + bounding.height > window.innerHeight - offset
|
||||
) {
|
||||
edges.bottom = true;
|
||||
}
|
||||
|
||||
return edges;
|
||||
};
|
||||
|
||||
M.checkPossibleAlignments = function(el, container, bounding, offset) {
|
||||
let canAlign = {
|
||||
top: true,
|
||||
right: true,
|
||||
bottom: true,
|
||||
left: true,
|
||||
spaceOnTop: null,
|
||||
spaceOnRight: null,
|
||||
spaceOnBottom: null,
|
||||
spaceOnLeft: null
|
||||
};
|
||||
|
||||
let containerAllowsOverflow = getComputedStyle(container).overflow === 'visible';
|
||||
let containerRect = container.getBoundingClientRect();
|
||||
let containerHeight = Math.min(containerRect.height, window.innerHeight);
|
||||
let containerWidth = Math.min(containerRect.width, window.innerWidth);
|
||||
let elOffsetRect = el.getBoundingClientRect();
|
||||
|
||||
let scrollLeft = container.scrollLeft;
|
||||
let scrollTop = container.scrollTop;
|
||||
|
||||
let scrolledX = bounding.left - scrollLeft;
|
||||
let scrolledYTopEdge = bounding.top - scrollTop;
|
||||
let scrolledYBottomEdge = bounding.top + elOffsetRect.height - scrollTop;
|
||||
|
||||
// Check for container and viewport for left
|
||||
canAlign.spaceOnRight = !containerAllowsOverflow
|
||||
? containerWidth - (scrolledX + bounding.width)
|
||||
: window.innerWidth - (elOffsetRect.left + bounding.width);
|
||||
if (canAlign.spaceOnRight < 0) {
|
||||
canAlign.left = false;
|
||||
}
|
||||
|
||||
// Check for container and viewport for Right
|
||||
canAlign.spaceOnLeft = !containerAllowsOverflow
|
||||
? scrolledX - bounding.width + elOffsetRect.width
|
||||
: elOffsetRect.right - bounding.width;
|
||||
if (canAlign.spaceOnLeft < 0) {
|
||||
canAlign.right = false;
|
||||
}
|
||||
|
||||
// Check for container and viewport for Top
|
||||
canAlign.spaceOnBottom = !containerAllowsOverflow
|
||||
? containerHeight - (scrolledYTopEdge + bounding.height + offset)
|
||||
: window.innerHeight - (elOffsetRect.top + bounding.height + offset);
|
||||
if (canAlign.spaceOnBottom < 0) {
|
||||
canAlign.top = false;
|
||||
}
|
||||
|
||||
// Check for container and viewport for Bottom
|
||||
canAlign.spaceOnTop = !containerAllowsOverflow
|
||||
? scrolledYBottomEdge - (bounding.height - offset)
|
||||
: elOffsetRect.bottom - (bounding.height + offset);
|
||||
if (canAlign.spaceOnTop < 0) {
|
||||
canAlign.bottom = false;
|
||||
}
|
||||
|
||||
return canAlign;
|
||||
};
|
||||
|
||||
M.getOverflowParent = function(element) {
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (element === document.body || getComputedStyle(element).overflow !== 'visible') {
|
||||
return element;
|
||||
}
|
||||
|
||||
return M.getOverflowParent(element.parentElement);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets id of component from a trigger
|
||||
* @param {Element} trigger trigger
|
||||
* @returns {string}
|
||||
*/
|
||||
M.getIdFromTrigger = function(trigger) {
|
||||
let id = trigger.getAttribute('data-target');
|
||||
if (!id) {
|
||||
id = trigger.getAttribute('href');
|
||||
if (id) {
|
||||
id = id.slice(1);
|
||||
} else {
|
||||
id = '';
|
||||
}
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Multi browser support for document scroll top
|
||||
* @returns {Number}
|
||||
*/
|
||||
M.getDocumentScrollTop = function() {
|
||||
return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Multi browser support for document scroll left
|
||||
* @returns {Number}
|
||||
*/
|
||||
M.getDocumentScrollLeft = function() {
|
||||
return window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Object} Edges
|
||||
* @property {Boolean} top If the top edge was exceeded
|
||||
* @property {Boolean} right If the right edge was exceeded
|
||||
* @property {Boolean} bottom If the bottom edge was exceeded
|
||||
* @property {Boolean} left If the left edge was exceeded
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Bounding
|
||||
* @property {Number} left left offset coordinate
|
||||
* @property {Number} top top offset coordinate
|
||||
* @property {Number} width
|
||||
* @property {Number} height
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get time in ms
|
||||
* @license https://raw.github.com/jashkenas/underscore/master/LICENSE
|
||||
* @type {function}
|
||||
* @return {number}
|
||||
*/
|
||||
let getTime =
|
||||
Date.now ||
|
||||
function() {
|
||||
return new Date().getTime();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a function, that, when invoked, will only be triggered at most once
|
||||
* during a given window of time. Normally, the throttled function will run
|
||||
* as much as it can, without ever going more than once per `wait` duration;
|
||||
* but if you'd like to disable the execution on the leading edge, pass
|
||||
* `{leading: false}`. To disable execution on the trailing edge, ditto.
|
||||
* @license https://raw.github.com/jashkenas/underscore/master/LICENSE
|
||||
* @param {function} func
|
||||
* @param {number} wait
|
||||
* @param {Object=} options
|
||||
* @returns {Function}
|
||||
*/
|
||||
M.throttle = function(func, wait, options) {
|
||||
let context, args, result;
|
||||
let timeout = null;
|
||||
let previous = 0;
|
||||
options || (options = {});
|
||||
let later = function() {
|
||||
previous = options.leading === false ? 0 : getTime();
|
||||
timeout = null;
|
||||
result = func.apply(context, args);
|
||||
context = args = null;
|
||||
};
|
||||
return function() {
|
||||
let now = getTime();
|
||||
if (!previous && options.leading === false) previous = now;
|
||||
let remaining = wait - (now - previous);
|
||||
context = this;
|
||||
args = arguments;
|
||||
if (remaining <= 0) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
previous = now;
|
||||
result = func.apply(context, args);
|
||||
context = args = null;
|
||||
} else if (!timeout && options.trailing !== false) {
|
||||
timeout = setTimeout(later, remaining);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,453 @@
|
|||
(function($, anim) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
inDuration: 275,
|
||||
outDuration: 200,
|
||||
onOpenStart: null,
|
||||
onOpenEnd: null,
|
||||
onCloseStart: null,
|
||||
onCloseEnd: null
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class Materialbox extends Component {
|
||||
/**
|
||||
* Construct Materialbox instance
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Materialbox, el, options);
|
||||
|
||||
this.el.M_Materialbox = this;
|
||||
|
||||
/**
|
||||
* Options for the modal
|
||||
* @member Materialbox#options
|
||||
* @prop {Number} [inDuration=275] - Length in ms of enter transition
|
||||
* @prop {Number} [outDuration=200] - Length in ms of exit transition
|
||||
* @prop {Function} onOpenStart - Callback function called before materialbox is opened
|
||||
* @prop {Function} onOpenEnd - Callback function called after materialbox is opened
|
||||
* @prop {Function} onCloseStart - Callback function called before materialbox is closed
|
||||
* @prop {Function} onCloseEnd - Callback function called after materialbox is closed
|
||||
*/
|
||||
this.options = $.extend({}, Materialbox.defaults, options);
|
||||
|
||||
this.overlayActive = false;
|
||||
this.doneAnimating = true;
|
||||
this.placeholder = $('<div></div>').addClass('material-placeholder');
|
||||
this.originalWidth = 0;
|
||||
this.originalHeight = 0;
|
||||
this.originInlineStyles = this.$el.attr('style');
|
||||
this.caption = this.el.getAttribute('data-caption') || '';
|
||||
|
||||
// Wrap
|
||||
this.$el.before(this.placeholder);
|
||||
this.placeholder.append(this.$el);
|
||||
|
||||
this._setupEventHandlers();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Materialbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this.el.M_Materialbox = undefined;
|
||||
|
||||
// Unwrap image
|
||||
$(this.placeholder)
|
||||
.after(this.el)
|
||||
.remove();
|
||||
|
||||
this.$el.removeAttr('style');
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleMaterialboxClickBound = this._handleMaterialboxClick.bind(this);
|
||||
this.el.addEventListener('click', this._handleMaterialboxClickBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
this.el.removeEventListener('click', this._handleMaterialboxClickBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Materialbox Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleMaterialboxClick(e) {
|
||||
// If already modal, return to original
|
||||
if (this.doneAnimating === false || (this.overlayActive && this.doneAnimating)) {
|
||||
this.close();
|
||||
} else {
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Window Scroll
|
||||
*/
|
||||
_handleWindowScroll() {
|
||||
if (this.overlayActive) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Window Resize
|
||||
*/
|
||||
_handleWindowResize() {
|
||||
if (this.overlayActive) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Window Resize
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleWindowEscape(e) {
|
||||
// ESC key
|
||||
if (e.keyCode === 27 && this.doneAnimating && this.overlayActive) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find ancestors with overflow: hidden; and make visible
|
||||
*/
|
||||
_makeAncestorsOverflowVisible() {
|
||||
this.ancestorsChanged = $();
|
||||
let ancestor = this.placeholder[0].parentNode;
|
||||
while (ancestor !== null && !$(ancestor).is(document)) {
|
||||
let curr = $(ancestor);
|
||||
if (curr.css('overflow') !== 'visible') {
|
||||
curr.css('overflow', 'visible');
|
||||
if (this.ancestorsChanged === undefined) {
|
||||
this.ancestorsChanged = curr;
|
||||
} else {
|
||||
this.ancestorsChanged = this.ancestorsChanged.add(curr);
|
||||
}
|
||||
}
|
||||
ancestor = ancestor.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate image in
|
||||
*/
|
||||
_animateImageIn() {
|
||||
let animOptions = {
|
||||
targets: this.el,
|
||||
height: [this.originalHeight, this.newHeight],
|
||||
width: [this.originalWidth, this.newWidth],
|
||||
left:
|
||||
M.getDocumentScrollLeft() +
|
||||
this.windowWidth / 2 -
|
||||
this.placeholder.offset().left -
|
||||
this.newWidth / 2,
|
||||
top:
|
||||
M.getDocumentScrollTop() +
|
||||
this.windowHeight / 2 -
|
||||
this.placeholder.offset().top -
|
||||
this.newHeight / 2,
|
||||
duration: this.options.inDuration,
|
||||
easing: 'easeOutQuad',
|
||||
complete: () => {
|
||||
this.doneAnimating = true;
|
||||
|
||||
// onOpenEnd callback
|
||||
if (typeof this.options.onOpenEnd === 'function') {
|
||||
this.options.onOpenEnd.call(this, this.el);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Override max-width or max-height if needed
|
||||
this.maxWidth = this.$el.css('max-width');
|
||||
this.maxHeight = this.$el.css('max-height');
|
||||
if (this.maxWidth !== 'none') {
|
||||
animOptions.maxWidth = this.newWidth;
|
||||
}
|
||||
if (this.maxHeight !== 'none') {
|
||||
animOptions.maxHeight = this.newHeight;
|
||||
}
|
||||
|
||||
anim(animOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate image out
|
||||
*/
|
||||
_animateImageOut() {
|
||||
let animOptions = {
|
||||
targets: this.el,
|
||||
width: this.originalWidth,
|
||||
height: this.originalHeight,
|
||||
left: 0,
|
||||
top: 0,
|
||||
duration: this.options.outDuration,
|
||||
easing: 'easeOutQuad',
|
||||
complete: () => {
|
||||
this.placeholder.css({
|
||||
height: '',
|
||||
width: '',
|
||||
position: '',
|
||||
top: '',
|
||||
left: ''
|
||||
});
|
||||
|
||||
// Revert to width or height attribute
|
||||
if (this.attrWidth) {
|
||||
this.$el.attr('width', this.attrWidth);
|
||||
}
|
||||
if (this.attrHeight) {
|
||||
this.$el.attr('height', this.attrHeight);
|
||||
}
|
||||
|
||||
this.$el.removeAttr('style');
|
||||
this.originInlineStyles && this.$el.attr('style', this.originInlineStyles);
|
||||
|
||||
// Remove class
|
||||
this.$el.removeClass('active');
|
||||
this.doneAnimating = true;
|
||||
|
||||
// Remove overflow overrides on ancestors
|
||||
if (this.ancestorsChanged.length) {
|
||||
this.ancestorsChanged.css('overflow', '');
|
||||
}
|
||||
|
||||
// onCloseEnd callback
|
||||
if (typeof this.options.onCloseEnd === 'function') {
|
||||
this.options.onCloseEnd.call(this, this.el);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
anim(animOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update open and close vars
|
||||
*/
|
||||
_updateVars() {
|
||||
this.windowWidth = window.innerWidth;
|
||||
this.windowHeight = window.innerHeight;
|
||||
this.caption = this.el.getAttribute('data-caption') || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Open Materialbox
|
||||
*/
|
||||
open() {
|
||||
this._updateVars();
|
||||
this.originalWidth = this.el.getBoundingClientRect().width;
|
||||
this.originalHeight = this.el.getBoundingClientRect().height;
|
||||
|
||||
// Set states
|
||||
this.doneAnimating = false;
|
||||
this.$el.addClass('active');
|
||||
this.overlayActive = true;
|
||||
|
||||
// onOpenStart callback
|
||||
if (typeof this.options.onOpenStart === 'function') {
|
||||
this.options.onOpenStart.call(this, this.el);
|
||||
}
|
||||
|
||||
// Set positioning for placeholder
|
||||
this.placeholder.css({
|
||||
width: this.placeholder[0].getBoundingClientRect().width + 'px',
|
||||
height: this.placeholder[0].getBoundingClientRect().height + 'px',
|
||||
position: 'relative',
|
||||
top: 0,
|
||||
left: 0
|
||||
});
|
||||
|
||||
this._makeAncestorsOverflowVisible();
|
||||
|
||||
// Set css on origin
|
||||
this.$el.css({
|
||||
position: 'absolute',
|
||||
'z-index': 1000,
|
||||
'will-change': 'left, top, width, height'
|
||||
});
|
||||
|
||||
// Change from width or height attribute to css
|
||||
this.attrWidth = this.$el.attr('width');
|
||||
this.attrHeight = this.$el.attr('height');
|
||||
if (this.attrWidth) {
|
||||
this.$el.css('width', this.attrWidth + 'px');
|
||||
this.$el.removeAttr('width');
|
||||
}
|
||||
if (this.attrHeight) {
|
||||
this.$el.css('width', this.attrHeight + 'px');
|
||||
this.$el.removeAttr('height');
|
||||
}
|
||||
|
||||
// Add overlay
|
||||
this.$overlay = $('<div id="materialbox-overlay"></div>')
|
||||
.css({
|
||||
opacity: 0
|
||||
})
|
||||
.one('click', () => {
|
||||
if (this.doneAnimating) {
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
||||
// Put before in origin image to preserve z-index layering.
|
||||
this.$el.before(this.$overlay);
|
||||
|
||||
// Set dimensions if needed
|
||||
let overlayOffset = this.$overlay[0].getBoundingClientRect();
|
||||
this.$overlay.css({
|
||||
width: this.windowWidth + 'px',
|
||||
height: this.windowHeight + 'px',
|
||||
left: -1 * overlayOffset.left + 'px',
|
||||
top: -1 * overlayOffset.top + 'px'
|
||||
});
|
||||
|
||||
anim.remove(this.el);
|
||||
anim.remove(this.$overlay[0]);
|
||||
|
||||
// Animate Overlay
|
||||
anim({
|
||||
targets: this.$overlay[0],
|
||||
opacity: 1,
|
||||
duration: this.options.inDuration,
|
||||
easing: 'easeOutQuad'
|
||||
});
|
||||
|
||||
// Add and animate caption if it exists
|
||||
if (this.caption !== '') {
|
||||
if (this.$photocaption) {
|
||||
anim.remove(this.$photoCaption[0]);
|
||||
}
|
||||
this.$photoCaption = $('<div class="materialbox-caption"></div>');
|
||||
this.$photoCaption.text(this.caption);
|
||||
$('body').append(this.$photoCaption);
|
||||
this.$photoCaption.css({ display: 'inline' });
|
||||
|
||||
anim({
|
||||
targets: this.$photoCaption[0],
|
||||
opacity: 1,
|
||||
duration: this.options.inDuration,
|
||||
easing: 'easeOutQuad'
|
||||
});
|
||||
}
|
||||
|
||||
// Resize Image
|
||||
let ratio = 0;
|
||||
let widthPercent = this.originalWidth / this.windowWidth;
|
||||
let heightPercent = this.originalHeight / this.windowHeight;
|
||||
this.newWidth = 0;
|
||||
this.newHeight = 0;
|
||||
|
||||
if (widthPercent > heightPercent) {
|
||||
ratio = this.originalHeight / this.originalWidth;
|
||||
this.newWidth = this.windowWidth * 0.9;
|
||||
this.newHeight = this.windowWidth * 0.9 * ratio;
|
||||
} else {
|
||||
ratio = this.originalWidth / this.originalHeight;
|
||||
this.newWidth = this.windowHeight * 0.9 * ratio;
|
||||
this.newHeight = this.windowHeight * 0.9;
|
||||
}
|
||||
|
||||
this._animateImageIn();
|
||||
|
||||
// Handle Exit triggers
|
||||
this._handleWindowScrollBound = this._handleWindowScroll.bind(this);
|
||||
this._handleWindowResizeBound = this._handleWindowResize.bind(this);
|
||||
this._handleWindowEscapeBound = this._handleWindowEscape.bind(this);
|
||||
|
||||
window.addEventListener('scroll', this._handleWindowScrollBound);
|
||||
window.addEventListener('resize', this._handleWindowResizeBound);
|
||||
window.addEventListener('keyup', this._handleWindowEscapeBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close Materialbox
|
||||
*/
|
||||
close() {
|
||||
this._updateVars();
|
||||
this.doneAnimating = false;
|
||||
|
||||
// onCloseStart callback
|
||||
if (typeof this.options.onCloseStart === 'function') {
|
||||
this.options.onCloseStart.call(this, this.el);
|
||||
}
|
||||
|
||||
anim.remove(this.el);
|
||||
anim.remove(this.$overlay[0]);
|
||||
|
||||
if (this.caption !== '') {
|
||||
anim.remove(this.$photoCaption[0]);
|
||||
}
|
||||
|
||||
// disable exit handlers
|
||||
window.removeEventListener('scroll', this._handleWindowScrollBound);
|
||||
window.removeEventListener('resize', this._handleWindowResizeBound);
|
||||
window.removeEventListener('keyup', this._handleWindowEscapeBound);
|
||||
|
||||
anim({
|
||||
targets: this.$overlay[0],
|
||||
opacity: 0,
|
||||
duration: this.options.outDuration,
|
||||
easing: 'easeOutQuad',
|
||||
complete: () => {
|
||||
this.overlayActive = false;
|
||||
this.$overlay.remove();
|
||||
}
|
||||
});
|
||||
|
||||
this._animateImageOut();
|
||||
|
||||
// Remove Caption + reset css settings on image
|
||||
if (this.caption !== '') {
|
||||
anim({
|
||||
targets: this.$photoCaption[0],
|
||||
opacity: 0,
|
||||
duration: this.options.outDuration,
|
||||
easing: 'easeOutQuad',
|
||||
complete: () => {
|
||||
this.$photoCaption.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
M.Materialbox = Materialbox;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Materialbox, 'materialbox', 'M_Materialbox');
|
||||
}
|
||||
})(cash, M.anime);
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,382 @@
|
|||
(function($, anim) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
opacity: 0.5,
|
||||
inDuration: 250,
|
||||
outDuration: 250,
|
||||
onOpenStart: null,
|
||||
onOpenEnd: null,
|
||||
onCloseStart: null,
|
||||
onCloseEnd: null,
|
||||
preventScrolling: true,
|
||||
dismissible: true,
|
||||
startingTop: '4%',
|
||||
endingTop: '10%'
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class Modal extends Component {
|
||||
/**
|
||||
* Construct Modal instance and set up overlay
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Modal, el, options);
|
||||
|
||||
this.el.M_Modal = this;
|
||||
|
||||
/**
|
||||
* Options for the modal
|
||||
* @member Modal#options
|
||||
* @prop {Number} [opacity=0.5] - Opacity of the modal overlay
|
||||
* @prop {Number} [inDuration=250] - Length in ms of enter transition
|
||||
* @prop {Number} [outDuration=250] - Length in ms of exit transition
|
||||
* @prop {Function} onOpenStart - Callback function called before modal is opened
|
||||
* @prop {Function} onOpenEnd - Callback function called after modal is opened
|
||||
* @prop {Function} onCloseStart - Callback function called before modal is closed
|
||||
* @prop {Function} onCloseEnd - Callback function called after modal is closed
|
||||
* @prop {Boolean} [dismissible=true] - Allow modal to be dismissed by keyboard or overlay click
|
||||
* @prop {String} [startingTop='4%'] - startingTop
|
||||
* @prop {String} [endingTop='10%'] - endingTop
|
||||
*/
|
||||
this.options = $.extend({}, Modal.defaults, options);
|
||||
|
||||
/**
|
||||
* Describes open/close state of modal
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.isOpen = false;
|
||||
|
||||
this.id = this.$el.attr('id');
|
||||
this._openingTrigger = undefined;
|
||||
this.$overlay = $('<div class="modal-overlay"></div>');
|
||||
this.el.tabIndex = 0;
|
||||
this._nthModalOpened = 0;
|
||||
|
||||
Modal._count++;
|
||||
this._setupEventHandlers();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Modal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
Modal._count--;
|
||||
this._removeEventHandlers();
|
||||
this.el.removeAttribute('style');
|
||||
this.$overlay.remove();
|
||||
this.el.M_Modal = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleOverlayClickBound = this._handleOverlayClick.bind(this);
|
||||
this._handleModalCloseClickBound = this._handleModalCloseClick.bind(this);
|
||||
|
||||
if (Modal._count === 1) {
|
||||
document.body.addEventListener('click', this._handleTriggerClick);
|
||||
}
|
||||
this.$overlay[0].addEventListener('click', this._handleOverlayClickBound);
|
||||
this.el.addEventListener('click', this._handleModalCloseClickBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
if (Modal._count === 0) {
|
||||
document.body.removeEventListener('click', this._handleTriggerClick);
|
||||
}
|
||||
this.$overlay[0].removeEventListener('click', this._handleOverlayClickBound);
|
||||
this.el.removeEventListener('click', this._handleModalCloseClickBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Trigger Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleTriggerClick(e) {
|
||||
let $trigger = $(e.target).closest('.modal-trigger');
|
||||
if ($trigger.length) {
|
||||
let modalId = M.getIdFromTrigger($trigger[0]);
|
||||
let modalInstance = document.getElementById(modalId).M_Modal;
|
||||
if (modalInstance) {
|
||||
modalInstance.open($trigger);
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Overlay Click
|
||||
*/
|
||||
_handleOverlayClick() {
|
||||
if (this.options.dismissible) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Modal Close Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleModalCloseClick(e) {
|
||||
let $closeTrigger = $(e.target).closest('.modal-close');
|
||||
if ($closeTrigger.length) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Keydown
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleKeydown(e) {
|
||||
// ESC key
|
||||
if (e.keyCode === 27 && this.options.dismissible) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Focus
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleFocus(e) {
|
||||
// Only trap focus if this modal is the last model opened (prevents loops in nested modals).
|
||||
if (!this.el.contains(e.target) && this._nthModalOpened === Modal._modalsOpen) {
|
||||
this.el.focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate in modal
|
||||
*/
|
||||
_animateIn() {
|
||||
// Set initial styles
|
||||
$.extend(this.el.style, {
|
||||
display: 'block',
|
||||
opacity: 0
|
||||
});
|
||||
$.extend(this.$overlay[0].style, {
|
||||
display: 'block',
|
||||
opacity: 0
|
||||
});
|
||||
|
||||
// Animate overlay
|
||||
anim({
|
||||
targets: this.$overlay[0],
|
||||
opacity: this.options.opacity,
|
||||
duration: this.options.inDuration,
|
||||
easing: 'easeOutQuad'
|
||||
});
|
||||
|
||||
// Define modal animation options
|
||||
let enterAnimOptions = {
|
||||
targets: this.el,
|
||||
duration: this.options.inDuration,
|
||||
easing: 'easeOutCubic',
|
||||
// Handle modal onOpenEnd callback
|
||||
complete: () => {
|
||||
if (typeof this.options.onOpenEnd === 'function') {
|
||||
this.options.onOpenEnd.call(this, this.el, this._openingTrigger);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Bottom sheet animation
|
||||
if (this.el.classList.contains('bottom-sheet')) {
|
||||
$.extend(enterAnimOptions, {
|
||||
bottom: 0,
|
||||
opacity: 1
|
||||
});
|
||||
anim(enterAnimOptions);
|
||||
|
||||
// Normal modal animation
|
||||
} else {
|
||||
$.extend(enterAnimOptions, {
|
||||
top: [this.options.startingTop, this.options.endingTop],
|
||||
opacity: 1,
|
||||
scaleX: [0.8, 1],
|
||||
scaleY: [0.8, 1]
|
||||
});
|
||||
anim(enterAnimOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate out modal
|
||||
*/
|
||||
_animateOut() {
|
||||
// Animate overlay
|
||||
anim({
|
||||
targets: this.$overlay[0],
|
||||
opacity: 0,
|
||||
duration: this.options.outDuration,
|
||||
easing: 'easeOutQuart'
|
||||
});
|
||||
|
||||
// Define modal animation options
|
||||
let exitAnimOptions = {
|
||||
targets: this.el,
|
||||
duration: this.options.outDuration,
|
||||
easing: 'easeOutCubic',
|
||||
// Handle modal ready callback
|
||||
complete: () => {
|
||||
this.el.style.display = 'none';
|
||||
this.$overlay.remove();
|
||||
|
||||
// Call onCloseEnd callback
|
||||
if (typeof this.options.onCloseEnd === 'function') {
|
||||
this.options.onCloseEnd.call(this, this.el);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Bottom sheet animation
|
||||
if (this.el.classList.contains('bottom-sheet')) {
|
||||
$.extend(exitAnimOptions, {
|
||||
bottom: '-100%',
|
||||
opacity: 0
|
||||
});
|
||||
anim(exitAnimOptions);
|
||||
|
||||
// Normal modal animation
|
||||
} else {
|
||||
$.extend(exitAnimOptions, {
|
||||
top: [this.options.endingTop, this.options.startingTop],
|
||||
opacity: 0,
|
||||
scaleX: 0.8,
|
||||
scaleY: 0.8
|
||||
});
|
||||
anim(exitAnimOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open Modal
|
||||
* @param {cash} [$trigger]
|
||||
*/
|
||||
open($trigger) {
|
||||
if (this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isOpen = true;
|
||||
Modal._modalsOpen++;
|
||||
this._nthModalOpened = Modal._modalsOpen;
|
||||
|
||||
// Set Z-Index based on number of currently open modals
|
||||
this.$overlay[0].style.zIndex = 1000 + Modal._modalsOpen * 2;
|
||||
this.el.style.zIndex = 1000 + Modal._modalsOpen * 2 + 1;
|
||||
|
||||
// Set opening trigger, undefined indicates modal was opened by javascript
|
||||
this._openingTrigger = !!$trigger ? $trigger[0] : undefined;
|
||||
|
||||
// onOpenStart callback
|
||||
if (typeof this.options.onOpenStart === 'function') {
|
||||
this.options.onOpenStart.call(this, this.el, this._openingTrigger);
|
||||
}
|
||||
|
||||
if (this.options.preventScrolling) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
this.el.classList.add('open');
|
||||
this.el.insertAdjacentElement('afterend', this.$overlay[0]);
|
||||
|
||||
if (this.options.dismissible) {
|
||||
this._handleKeydownBound = this._handleKeydown.bind(this);
|
||||
this._handleFocusBound = this._handleFocus.bind(this);
|
||||
document.addEventListener('keydown', this._handleKeydownBound);
|
||||
document.addEventListener('focus', this._handleFocusBound, true);
|
||||
}
|
||||
|
||||
anim.remove(this.el);
|
||||
anim.remove(this.$overlay[0]);
|
||||
this._animateIn();
|
||||
|
||||
// Focus modal
|
||||
this.el.focus();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close Modal
|
||||
*/
|
||||
close() {
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isOpen = false;
|
||||
Modal._modalsOpen--;
|
||||
this._nthModalOpened = 0;
|
||||
|
||||
// Call onCloseStart callback
|
||||
if (typeof this.options.onCloseStart === 'function') {
|
||||
this.options.onCloseStart.call(this, this.el);
|
||||
}
|
||||
|
||||
this.el.classList.remove('open');
|
||||
|
||||
// Enable body scrolling only if there are no more modals open.
|
||||
if (Modal._modalsOpen === 0) {
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
if (this.options.dismissible) {
|
||||
document.removeEventListener('keydown', this._handleKeydownBound);
|
||||
document.removeEventListener('focus', this._handleFocusBound, true);
|
||||
}
|
||||
|
||||
anim.remove(this.el);
|
||||
anim.remove(this.$overlay[0]);
|
||||
this._animateOut();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof Modal
|
||||
*/
|
||||
Modal._modalsOpen = 0;
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof Modal
|
||||
*/
|
||||
Modal._count = 0;
|
||||
|
||||
M.Modal = Modal;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Modal, 'modal', 'M_Modal');
|
||||
}
|
||||
})(cash, M.anime);
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
(function($) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
responsiveThreshold: 0 // breakpoint for swipeable
|
||||
};
|
||||
|
||||
class Parallax extends Component {
|
||||
constructor(el, options) {
|
||||
super(Parallax, el, options);
|
||||
|
||||
this.el.M_Parallax = this;
|
||||
|
||||
/**
|
||||
* Options for the Parallax
|
||||
* @member Parallax#options
|
||||
* @prop {Number} responsiveThreshold
|
||||
*/
|
||||
this.options = $.extend({}, Parallax.defaults, options);
|
||||
this._enabled = window.innerWidth > this.options.responsiveThreshold;
|
||||
|
||||
this.$img = this.$el.find('img').first();
|
||||
this.$img.each(function() {
|
||||
let el = this;
|
||||
if (el.complete) $(el).trigger('load');
|
||||
});
|
||||
|
||||
this._updateParallax();
|
||||
this._setupEventHandlers();
|
||||
this._setupStyles();
|
||||
|
||||
Parallax._parallaxes.push(this);
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Parallax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
Parallax._parallaxes.splice(Parallax._parallaxes.indexOf(this), 1);
|
||||
this.$img[0].style.transform = '';
|
||||
this._removeEventHandlers();
|
||||
|
||||
this.$el[0].M_Parallax = undefined;
|
||||
}
|
||||
|
||||
static _handleScroll() {
|
||||
for (let i = 0; i < Parallax._parallaxes.length; i++) {
|
||||
let parallaxInstance = Parallax._parallaxes[i];
|
||||
parallaxInstance._updateParallax.call(parallaxInstance);
|
||||
}
|
||||
}
|
||||
|
||||
static _handleWindowResize() {
|
||||
for (let i = 0; i < Parallax._parallaxes.length; i++) {
|
||||
let parallaxInstance = Parallax._parallaxes[i];
|
||||
parallaxInstance._enabled =
|
||||
window.innerWidth > parallaxInstance.options.responsiveThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
_setupEventHandlers() {
|
||||
this._handleImageLoadBound = this._handleImageLoad.bind(this);
|
||||
this.$img[0].addEventListener('load', this._handleImageLoadBound);
|
||||
|
||||
if (Parallax._parallaxes.length === 0) {
|
||||
Parallax._handleScrollThrottled = M.throttle(Parallax._handleScroll, 5);
|
||||
window.addEventListener('scroll', Parallax._handleScrollThrottled);
|
||||
|
||||
Parallax._handleWindowResizeThrottled = M.throttle(Parallax._handleWindowResize, 5);
|
||||
window.addEventListener('resize', Parallax._handleWindowResizeThrottled);
|
||||
}
|
||||
}
|
||||
|
||||
_removeEventHandlers() {
|
||||
this.$img[0].removeEventListener('load', this._handleImageLoadBound);
|
||||
|
||||
if (Parallax._parallaxes.length === 0) {
|
||||
window.removeEventListener('scroll', Parallax._handleScrollThrottled);
|
||||
window.removeEventListener('resize', Parallax._handleWindowResizeThrottled);
|
||||
}
|
||||
}
|
||||
|
||||
_setupStyles() {
|
||||
this.$img[0].style.opacity = 1;
|
||||
}
|
||||
|
||||
_handleImageLoad() {
|
||||
this._updateParallax();
|
||||
}
|
||||
|
||||
_updateParallax() {
|
||||
let containerHeight = this.$el.height() > 0 ? this.el.parentNode.offsetHeight : 500;
|
||||
let imgHeight = this.$img[0].offsetHeight;
|
||||
let parallaxDist = imgHeight - containerHeight;
|
||||
let bottom = this.$el.offset().top + containerHeight;
|
||||
let top = this.$el.offset().top;
|
||||
let scrollTop = M.getDocumentScrollTop();
|
||||
let windowHeight = window.innerHeight;
|
||||
let windowBottom = scrollTop + windowHeight;
|
||||
let percentScrolled = (windowBottom - top) / (containerHeight + windowHeight);
|
||||
let parallax = parallaxDist * percentScrolled;
|
||||
|
||||
if (!this._enabled) {
|
||||
this.$img[0].style.transform = '';
|
||||
} else if (bottom > scrollTop && top < scrollTop + windowHeight) {
|
||||
this.$img[0].style.transform = `translate3D(-50%, ${parallax}px, 0)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof Parallax
|
||||
*/
|
||||
Parallax._parallaxes = [];
|
||||
|
||||
M.Parallax = Parallax;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Parallax, 'parallax', 'M_Parallax');
|
||||
}
|
||||
})(cash);
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
(function($) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
top: 0,
|
||||
bottom: Infinity,
|
||||
offset: 0,
|
||||
onPositionChange: null
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class Pushpin extends Component {
|
||||
/**
|
||||
* Construct Pushpin instance
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Pushpin, el, options);
|
||||
|
||||
this.el.M_Pushpin = this;
|
||||
|
||||
/**
|
||||
* Options for the modal
|
||||
* @member Pushpin#options
|
||||
*/
|
||||
this.options = $.extend({}, Pushpin.defaults, options);
|
||||
|
||||
this.originalOffset = this.el.offsetTop;
|
||||
Pushpin._pushpins.push(this);
|
||||
this._setupEventHandlers();
|
||||
this._updatePosition();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Pushpin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this.el.style.top = null;
|
||||
this._removePinClasses();
|
||||
this._removeEventHandlers();
|
||||
|
||||
// Remove pushpin Inst
|
||||
let index = Pushpin._pushpins.indexOf(this);
|
||||
Pushpin._pushpins.splice(index, 1);
|
||||
}
|
||||
|
||||
static _updateElements() {
|
||||
for (let elIndex in Pushpin._pushpins) {
|
||||
let pInstance = Pushpin._pushpins[elIndex];
|
||||
pInstance._updatePosition();
|
||||
}
|
||||
}
|
||||
|
||||
_setupEventHandlers() {
|
||||
document.addEventListener('scroll', Pushpin._updateElements);
|
||||
}
|
||||
|
||||
_removeEventHandlers() {
|
||||
document.removeEventListener('scroll', Pushpin._updateElements);
|
||||
}
|
||||
|
||||
_updatePosition() {
|
||||
let scrolled = M.getDocumentScrollTop() + this.options.offset;
|
||||
|
||||
if (
|
||||
this.options.top <= scrolled &&
|
||||
this.options.bottom >= scrolled &&
|
||||
!this.el.classList.contains('pinned')
|
||||
) {
|
||||
this._removePinClasses();
|
||||
this.el.style.top = `${this.options.offset}px`;
|
||||
this.el.classList.add('pinned');
|
||||
|
||||
// onPositionChange callback
|
||||
if (typeof this.options.onPositionChange === 'function') {
|
||||
this.options.onPositionChange.call(this, 'pinned');
|
||||
}
|
||||
}
|
||||
|
||||
// Add pin-top (when scrolled position is above top)
|
||||
if (scrolled < this.options.top && !this.el.classList.contains('pin-top')) {
|
||||
this._removePinClasses();
|
||||
this.el.style.top = 0;
|
||||
this.el.classList.add('pin-top');
|
||||
|
||||
// onPositionChange callback
|
||||
if (typeof this.options.onPositionChange === 'function') {
|
||||
this.options.onPositionChange.call(this, 'pin-top');
|
||||
}
|
||||
}
|
||||
|
||||
// Add pin-bottom (when scrolled position is below bottom)
|
||||
if (scrolled > this.options.bottom && !this.el.classList.contains('pin-bottom')) {
|
||||
this._removePinClasses();
|
||||
this.el.classList.add('pin-bottom');
|
||||
this.el.style.top = `${this.options.bottom - this.originalOffset}px`;
|
||||
|
||||
// onPositionChange callback
|
||||
if (typeof this.options.onPositionChange === 'function') {
|
||||
this.options.onPositionChange.call(this, 'pin-bottom');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_removePinClasses() {
|
||||
// IE 11 bug (can't remove multiple classes in one line)
|
||||
this.el.classList.remove('pin-top');
|
||||
this.el.classList.remove('pinned');
|
||||
this.el.classList.remove('pin-bottom');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof Pushpin
|
||||
*/
|
||||
Pushpin._pushpins = [];
|
||||
|
||||
M.Pushpin = Pushpin;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Pushpin, 'pushpin', 'M_Pushpin');
|
||||
}
|
||||
})(cash);
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
(function($, anim) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class Range extends Component {
|
||||
/**
|
||||
* Construct Range instance
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Range, el, options);
|
||||
|
||||
this.el.M_Range = this;
|
||||
|
||||
/**
|
||||
* Options for the range
|
||||
* @member Range#options
|
||||
*/
|
||||
this.options = $.extend({}, Range.defaults, options);
|
||||
|
||||
this._mousedown = false;
|
||||
|
||||
// Setup
|
||||
this._setupThumb();
|
||||
|
||||
this._setupEventHandlers();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this._removeThumb();
|
||||
this.el.M_Range = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleRangeChangeBound = this._handleRangeChange.bind(this);
|
||||
this._handleRangeMousedownTouchstartBound = this._handleRangeMousedownTouchstart.bind(this);
|
||||
this._handleRangeInputMousemoveTouchmoveBound = this._handleRangeInputMousemoveTouchmove.bind(
|
||||
this
|
||||
);
|
||||
this._handleRangeMouseupTouchendBound = this._handleRangeMouseupTouchend.bind(this);
|
||||
this._handleRangeBlurMouseoutTouchleaveBound = this._handleRangeBlurMouseoutTouchleave.bind(
|
||||
this
|
||||
);
|
||||
|
||||
this.el.addEventListener('change', this._handleRangeChangeBound);
|
||||
|
||||
this.el.addEventListener('mousedown', this._handleRangeMousedownTouchstartBound);
|
||||
this.el.addEventListener('touchstart', this._handleRangeMousedownTouchstartBound);
|
||||
|
||||
this.el.addEventListener('input', this._handleRangeInputMousemoveTouchmoveBound);
|
||||
this.el.addEventListener('mousemove', this._handleRangeInputMousemoveTouchmoveBound);
|
||||
this.el.addEventListener('touchmove', this._handleRangeInputMousemoveTouchmoveBound);
|
||||
|
||||
this.el.addEventListener('mouseup', this._handleRangeMouseupTouchendBound);
|
||||
this.el.addEventListener('touchend', this._handleRangeMouseupTouchendBound);
|
||||
|
||||
this.el.addEventListener('blur', this._handleRangeBlurMouseoutTouchleaveBound);
|
||||
this.el.addEventListener('mouseout', this._handleRangeBlurMouseoutTouchleaveBound);
|
||||
this.el.addEventListener('touchleave', this._handleRangeBlurMouseoutTouchleaveBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
this.el.removeEventListener('change', this._handleRangeChangeBound);
|
||||
|
||||
this.el.removeEventListener('mousedown', this._handleRangeMousedownTouchstartBound);
|
||||
this.el.removeEventListener('touchstart', this._handleRangeMousedownTouchstartBound);
|
||||
|
||||
this.el.removeEventListener('input', this._handleRangeInputMousemoveTouchmoveBound);
|
||||
this.el.removeEventListener('mousemove', this._handleRangeInputMousemoveTouchmoveBound);
|
||||
this.el.removeEventListener('touchmove', this._handleRangeInputMousemoveTouchmoveBound);
|
||||
|
||||
this.el.removeEventListener('mouseup', this._handleRangeMouseupTouchendBound);
|
||||
this.el.removeEventListener('touchend', this._handleRangeMouseupTouchendBound);
|
||||
|
||||
this.el.removeEventListener('blur', this._handleRangeBlurMouseoutTouchleaveBound);
|
||||
this.el.removeEventListener('mouseout', this._handleRangeBlurMouseoutTouchleaveBound);
|
||||
this.el.removeEventListener('touchleave', this._handleRangeBlurMouseoutTouchleaveBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Range Change
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleRangeChange() {
|
||||
$(this.value).html(this.$el.val());
|
||||
|
||||
if (!$(this.thumb).hasClass('active')) {
|
||||
this._showRangeBubble();
|
||||
}
|
||||
|
||||
let offsetLeft = this._calcRangeOffset();
|
||||
$(this.thumb)
|
||||
.addClass('active')
|
||||
.css('left', offsetLeft + 'px');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Range Mousedown and Touchstart
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleRangeMousedownTouchstart(e) {
|
||||
// Set indicator value
|
||||
$(this.value).html(this.$el.val());
|
||||
|
||||
this._mousedown = true;
|
||||
this.$el.addClass('active');
|
||||
|
||||
if (!$(this.thumb).hasClass('active')) {
|
||||
this._showRangeBubble();
|
||||
}
|
||||
|
||||
if (e.type !== 'input') {
|
||||
let offsetLeft = this._calcRangeOffset();
|
||||
$(this.thumb)
|
||||
.addClass('active')
|
||||
.css('left', offsetLeft + 'px');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Range Input, Mousemove and Touchmove
|
||||
*/
|
||||
_handleRangeInputMousemoveTouchmove() {
|
||||
if (this._mousedown) {
|
||||
if (!$(this.thumb).hasClass('active')) {
|
||||
this._showRangeBubble();
|
||||
}
|
||||
|
||||
let offsetLeft = this._calcRangeOffset();
|
||||
$(this.thumb)
|
||||
.addClass('active')
|
||||
.css('left', offsetLeft + 'px');
|
||||
$(this.value).html(this.$el.val());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Range Mouseup and Touchend
|
||||
*/
|
||||
_handleRangeMouseupTouchend() {
|
||||
this._mousedown = false;
|
||||
this.$el.removeClass('active');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Range Blur, Mouseout and Touchleave
|
||||
*/
|
||||
_handleRangeBlurMouseoutTouchleave() {
|
||||
if (!this._mousedown) {
|
||||
let paddingLeft = parseInt(this.$el.css('padding-left'));
|
||||
let marginLeft = 7 + paddingLeft + 'px';
|
||||
|
||||
if ($(this.thumb).hasClass('active')) {
|
||||
anim.remove(this.thumb);
|
||||
anim({
|
||||
targets: this.thumb,
|
||||
height: 0,
|
||||
width: 0,
|
||||
top: 10,
|
||||
easing: 'easeOutQuad',
|
||||
marginLeft: marginLeft,
|
||||
duration: 100
|
||||
});
|
||||
}
|
||||
$(this.thumb).removeClass('active');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup dropdown
|
||||
*/
|
||||
_setupThumb() {
|
||||
this.thumb = document.createElement('span');
|
||||
this.value = document.createElement('span');
|
||||
$(this.thumb).addClass('thumb');
|
||||
$(this.value).addClass('value');
|
||||
$(this.thumb).append(this.value);
|
||||
this.$el.after(this.thumb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove dropdown
|
||||
*/
|
||||
_removeThumb() {
|
||||
$(this.thumb).remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* morph thumb into bubble
|
||||
*/
|
||||
_showRangeBubble() {
|
||||
let paddingLeft = parseInt(
|
||||
$(this.thumb)
|
||||
.parent()
|
||||
.css('padding-left')
|
||||
);
|
||||
let marginLeft = -7 + paddingLeft + 'px'; // TODO: fix magic number?
|
||||
anim.remove(this.thumb);
|
||||
anim({
|
||||
targets: this.thumb,
|
||||
height: 30,
|
||||
width: 30,
|
||||
top: -30,
|
||||
marginLeft: marginLeft,
|
||||
duration: 300,
|
||||
easing: 'easeOutQuint'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the offset of the thumb
|
||||
* @return {Number} offset in pixels
|
||||
*/
|
||||
_calcRangeOffset() {
|
||||
let width = this.$el.width() - 15;
|
||||
let max = parseFloat(this.$el.attr('max')) || 100; // Range default max
|
||||
let min = parseFloat(this.$el.attr('min')) || 0; // Range default min
|
||||
let percent = (parseFloat(this.$el.val()) - min) / (max - min);
|
||||
return percent * width;
|
||||
}
|
||||
}
|
||||
|
||||
M.Range = Range;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Range, 'range', 'M_Range');
|
||||
}
|
||||
|
||||
Range.init($('input[type=range]'));
|
||||
})(cash, M.anime);
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
(function($, anim) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
throttle: 100,
|
||||
scrollOffset: 200, // offset - 200 allows elements near bottom of page to scroll
|
||||
activeClass: 'active',
|
||||
getActiveElement: function(id) {
|
||||
return 'a[href="#' + id + '"]';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class ScrollSpy extends Component {
|
||||
/**
|
||||
* Construct ScrollSpy instance
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(ScrollSpy, el, options);
|
||||
|
||||
this.el.M_ScrollSpy = this;
|
||||
|
||||
/**
|
||||
* Options for the modal
|
||||
* @member Modal#options
|
||||
* @prop {Number} [throttle=100] - Throttle of scroll handler
|
||||
* @prop {Number} [scrollOffset=200] - Offset for centering element when scrolled to
|
||||
* @prop {String} [activeClass='active'] - Class applied to active elements
|
||||
* @prop {Function} [getActiveElement] - Used to find active element
|
||||
*/
|
||||
this.options = $.extend({}, ScrollSpy.defaults, options);
|
||||
|
||||
// setup
|
||||
ScrollSpy._elements.push(this);
|
||||
ScrollSpy._count++;
|
||||
ScrollSpy._increment++;
|
||||
this.tickId = -1;
|
||||
this.id = ScrollSpy._increment;
|
||||
this._setupEventHandlers();
|
||||
this._handleWindowScroll();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_ScrollSpy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
ScrollSpy._elements.splice(ScrollSpy._elements.indexOf(this), 1);
|
||||
ScrollSpy._elementsInView.splice(ScrollSpy._elementsInView.indexOf(this), 1);
|
||||
ScrollSpy._visibleElements.splice(ScrollSpy._visibleElements.indexOf(this.$el), 1);
|
||||
ScrollSpy._count--;
|
||||
this._removeEventHandlers();
|
||||
$(this.options.getActiveElement(this.$el.attr('id'))).removeClass(this.options.activeClass);
|
||||
this.el.M_ScrollSpy = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
let throttledResize = M.throttle(this._handleWindowScroll, 200);
|
||||
this._handleThrottledResizeBound = throttledResize.bind(this);
|
||||
this._handleWindowScrollBound = this._handleWindowScroll.bind(this);
|
||||
if (ScrollSpy._count === 1) {
|
||||
window.addEventListener('scroll', this._handleWindowScrollBound);
|
||||
window.addEventListener('resize', this._handleThrottledResizeBound);
|
||||
document.body.addEventListener('click', this._handleTriggerClick);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
if (ScrollSpy._count === 0) {
|
||||
window.removeEventListener('scroll', this._handleWindowScrollBound);
|
||||
window.removeEventListener('resize', this._handleThrottledResizeBound);
|
||||
document.body.removeEventListener('click', this._handleTriggerClick);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Trigger Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleTriggerClick(e) {
|
||||
let $trigger = $(e.target);
|
||||
for (let i = ScrollSpy._elements.length - 1; i >= 0; i--) {
|
||||
let scrollspy = ScrollSpy._elements[i];
|
||||
if ($trigger.is('a[href="#' + scrollspy.$el.attr('id') + '"]')) {
|
||||
e.preventDefault();
|
||||
let offset = scrollspy.$el.offset().top + 1;
|
||||
|
||||
anim({
|
||||
targets: [document.documentElement, document.body],
|
||||
scrollTop: offset - scrollspy.options.scrollOffset,
|
||||
duration: 400,
|
||||
easing: 'easeOutCubic'
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Window Scroll
|
||||
*/
|
||||
_handleWindowScroll() {
|
||||
// unique tick id
|
||||
ScrollSpy._ticks++;
|
||||
|
||||
// viewport rectangle
|
||||
let top = M.getDocumentScrollTop(),
|
||||
left = M.getDocumentScrollLeft(),
|
||||
right = left + window.innerWidth,
|
||||
bottom = top + window.innerHeight;
|
||||
|
||||
// determine which elements are in view
|
||||
let intersections = ScrollSpy._findElements(top, right, bottom, left);
|
||||
for (let i = 0; i < intersections.length; i++) {
|
||||
let scrollspy = intersections[i];
|
||||
let lastTick = scrollspy.tickId;
|
||||
if (lastTick < 0) {
|
||||
// entered into view
|
||||
scrollspy._enter();
|
||||
}
|
||||
|
||||
// update tick id
|
||||
scrollspy.tickId = ScrollSpy._ticks;
|
||||
}
|
||||
|
||||
for (let i = 0; i < ScrollSpy._elementsInView.length; i++) {
|
||||
let scrollspy = ScrollSpy._elementsInView[i];
|
||||
let lastTick = scrollspy.tickId;
|
||||
if (lastTick >= 0 && lastTick !== ScrollSpy._ticks) {
|
||||
// exited from view
|
||||
scrollspy._exit();
|
||||
scrollspy.tickId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// remember elements in view for next tick
|
||||
ScrollSpy._elementsInView = intersections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find elements that are within the boundary
|
||||
* @param {number} top
|
||||
* @param {number} right
|
||||
* @param {number} bottom
|
||||
* @param {number} left
|
||||
* @return {Array.<ScrollSpy>} A collection of elements
|
||||
*/
|
||||
static _findElements(top, right, bottom, left) {
|
||||
let hits = [];
|
||||
for (let i = 0; i < ScrollSpy._elements.length; i++) {
|
||||
let scrollspy = ScrollSpy._elements[i];
|
||||
let currTop = top + scrollspy.options.scrollOffset || 200;
|
||||
|
||||
if (scrollspy.$el.height() > 0) {
|
||||
let elTop = scrollspy.$el.offset().top,
|
||||
elLeft = scrollspy.$el.offset().left,
|
||||
elRight = elLeft + scrollspy.$el.width(),
|
||||
elBottom = elTop + scrollspy.$el.height();
|
||||
|
||||
let isIntersect = !(
|
||||
elLeft > right ||
|
||||
elRight < left ||
|
||||
elTop > bottom ||
|
||||
elBottom < currTop
|
||||
);
|
||||
|
||||
if (isIntersect) {
|
||||
hits.push(scrollspy);
|
||||
}
|
||||
}
|
||||
}
|
||||
return hits;
|
||||
}
|
||||
|
||||
_enter() {
|
||||
ScrollSpy._visibleElements = ScrollSpy._visibleElements.filter(function(value) {
|
||||
return value.height() != 0;
|
||||
});
|
||||
|
||||
if (ScrollSpy._visibleElements[0]) {
|
||||
$(this.options.getActiveElement(ScrollSpy._visibleElements[0].attr('id'))).removeClass(
|
||||
this.options.activeClass
|
||||
);
|
||||
if (
|
||||
ScrollSpy._visibleElements[0][0].M_ScrollSpy &&
|
||||
this.id < ScrollSpy._visibleElements[0][0].M_ScrollSpy.id
|
||||
) {
|
||||
ScrollSpy._visibleElements.unshift(this.$el);
|
||||
} else {
|
||||
ScrollSpy._visibleElements.push(this.$el);
|
||||
}
|
||||
} else {
|
||||
ScrollSpy._visibleElements.push(this.$el);
|
||||
}
|
||||
|
||||
$(this.options.getActiveElement(ScrollSpy._visibleElements[0].attr('id'))).addClass(
|
||||
this.options.activeClass
|
||||
);
|
||||
}
|
||||
|
||||
_exit() {
|
||||
ScrollSpy._visibleElements = ScrollSpy._visibleElements.filter(function(value) {
|
||||
return value.height() != 0;
|
||||
});
|
||||
|
||||
if (ScrollSpy._visibleElements[0]) {
|
||||
$(this.options.getActiveElement(ScrollSpy._visibleElements[0].attr('id'))).removeClass(
|
||||
this.options.activeClass
|
||||
);
|
||||
|
||||
ScrollSpy._visibleElements = ScrollSpy._visibleElements.filter((el) => {
|
||||
return el.attr('id') != this.$el.attr('id');
|
||||
});
|
||||
if (ScrollSpy._visibleElements[0]) {
|
||||
// Check if empty
|
||||
$(this.options.getActiveElement(ScrollSpy._visibleElements[0].attr('id'))).addClass(
|
||||
this.options.activeClass
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof ScrollSpy
|
||||
* @type {Array.<ScrollSpy>}
|
||||
*/
|
||||
ScrollSpy._elements = [];
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof ScrollSpy
|
||||
* @type {Array.<ScrollSpy>}
|
||||
*/
|
||||
ScrollSpy._elementsInView = [];
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof ScrollSpy
|
||||
* @type {Array.<cash>}
|
||||
*/
|
||||
ScrollSpy._visibleElements = [];
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof ScrollSpy
|
||||
*/
|
||||
ScrollSpy._count = 0;
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof ScrollSpy
|
||||
*/
|
||||
ScrollSpy._increment = 0;
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof ScrollSpy
|
||||
*/
|
||||
ScrollSpy._ticks = 0;
|
||||
|
||||
M.ScrollSpy = ScrollSpy;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(ScrollSpy, 'scrollSpy', 'M_ScrollSpy');
|
||||
}
|
||||
})(cash, M.anime);
|
||||
|
|
@ -0,0 +1,432 @@
|
|||
(function($) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
classes: '',
|
||||
dropdownOptions: {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class FormSelect extends Component {
|
||||
/**
|
||||
* Construct FormSelect instance
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(FormSelect, el, options);
|
||||
|
||||
// Don't init if browser default version
|
||||
if (this.$el.hasClass('browser-default')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.el.M_FormSelect = this;
|
||||
|
||||
/**
|
||||
* Options for the select
|
||||
* @member FormSelect#options
|
||||
*/
|
||||
this.options = $.extend({}, FormSelect.defaults, options);
|
||||
|
||||
this.isMultiple = this.$el.prop('multiple');
|
||||
|
||||
// Setup
|
||||
this.el.tabIndex = -1;
|
||||
this._keysSelected = {};
|
||||
this._valueDict = {}; // Maps key to original and generated option element.
|
||||
this._setupDropdown();
|
||||
|
||||
this._setupEventHandlers();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_FormSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this._removeDropdown();
|
||||
this.el.M_FormSelect = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleSelectChangeBound = this._handleSelectChange.bind(this);
|
||||
this._handleOptionClickBound = this._handleOptionClick.bind(this);
|
||||
this._handleInputClickBound = this._handleInputClick.bind(this);
|
||||
|
||||
$(this.dropdownOptions)
|
||||
.find('li:not(.optgroup)')
|
||||
.each((el) => {
|
||||
el.addEventListener('click', this._handleOptionClickBound);
|
||||
});
|
||||
this.el.addEventListener('change', this._handleSelectChangeBound);
|
||||
this.input.addEventListener('click', this._handleInputClickBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
$(this.dropdownOptions)
|
||||
.find('li:not(.optgroup)')
|
||||
.each((el) => {
|
||||
el.removeEventListener('click', this._handleOptionClickBound);
|
||||
});
|
||||
this.el.removeEventListener('change', this._handleSelectChangeBound);
|
||||
this.input.removeEventListener('click', this._handleInputClickBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Select Change
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleSelectChange(e) {
|
||||
this._setValueToInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Option Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleOptionClick(e) {
|
||||
e.preventDefault();
|
||||
let option = $(e.target).closest('li')[0];
|
||||
let key = option.id;
|
||||
if (!$(option).hasClass('disabled') && !$(option).hasClass('optgroup') && key.length) {
|
||||
let selected = true;
|
||||
|
||||
if (this.isMultiple) {
|
||||
// Deselect placeholder option if still selected.
|
||||
let placeholderOption = $(this.dropdownOptions).find('li.disabled.selected');
|
||||
if (placeholderOption.length) {
|
||||
placeholderOption.removeClass('selected');
|
||||
placeholderOption.find('input[type="checkbox"]').prop('checked', false);
|
||||
this._toggleEntryFromArray(placeholderOption[0].id);
|
||||
}
|
||||
selected = this._toggleEntryFromArray(key);
|
||||
} else {
|
||||
$(this.dropdownOptions)
|
||||
.find('li')
|
||||
.removeClass('selected');
|
||||
$(option).toggleClass('selected', selected);
|
||||
}
|
||||
|
||||
// Set selected on original select option
|
||||
// Only trigger if selected state changed
|
||||
let prevSelected = $(this._valueDict[key].el).prop('selected');
|
||||
if (prevSelected !== selected) {
|
||||
$(this._valueDict[key].el).prop('selected', selected);
|
||||
this.$el.trigger('change');
|
||||
}
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Input Click
|
||||
*/
|
||||
_handleInputClick() {
|
||||
if (this.dropdown && this.dropdown.isOpen) {
|
||||
this._setValueToInput();
|
||||
this._setSelectedStates();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup dropdown
|
||||
*/
|
||||
_setupDropdown() {
|
||||
this.wrapper = document.createElement('div');
|
||||
$(this.wrapper).addClass('select-wrapper ' + this.options.classes);
|
||||
this.$el.before($(this.wrapper));
|
||||
this.wrapper.appendChild(this.el);
|
||||
|
||||
if (this.el.disabled) {
|
||||
this.wrapper.classList.add('disabled');
|
||||
}
|
||||
|
||||
// Create dropdown
|
||||
this.$selectOptions = this.$el.children('option, optgroup');
|
||||
this.dropdownOptions = document.createElement('ul');
|
||||
this.dropdownOptions.id = `select-options-${M.guid()}`;
|
||||
$(this.dropdownOptions).addClass(
|
||||
'dropdown-content select-dropdown ' + (this.isMultiple ? 'multiple-select-dropdown' : '')
|
||||
);
|
||||
|
||||
// Create dropdown structure.
|
||||
if (this.$selectOptions.length) {
|
||||
this.$selectOptions.each((el) => {
|
||||
if ($(el).is('option')) {
|
||||
// Direct descendant option.
|
||||
let optionEl;
|
||||
if (this.isMultiple) {
|
||||
optionEl = this._appendOptionWithIcon(this.$el, el, 'multiple');
|
||||
} else {
|
||||
optionEl = this._appendOptionWithIcon(this.$el, el);
|
||||
}
|
||||
|
||||
this._addOptionToValueDict(el, optionEl);
|
||||
} else if ($(el).is('optgroup')) {
|
||||
// Optgroup.
|
||||
let selectOptions = $(el).children('option');
|
||||
$(this.dropdownOptions).append(
|
||||
$('<li class="optgroup"><span>' + el.getAttribute('label') + '</span></li>')[0]
|
||||
);
|
||||
|
||||
selectOptions.each((el) => {
|
||||
let optionEl = this._appendOptionWithIcon(this.$el, el, 'optgroup-option');
|
||||
this._addOptionToValueDict(el, optionEl);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.$el.after(this.dropdownOptions);
|
||||
|
||||
// Add input dropdown
|
||||
this.input = document.createElement('input');
|
||||
$(this.input).addClass('select-dropdown dropdown-trigger');
|
||||
this.input.setAttribute('type', 'text');
|
||||
this.input.setAttribute('readonly', 'true');
|
||||
this.input.setAttribute('data-target', this.dropdownOptions.id);
|
||||
if (this.el.disabled) {
|
||||
$(this.input).prop('disabled', 'true');
|
||||
}
|
||||
|
||||
this.$el.before(this.input);
|
||||
this._setValueToInput();
|
||||
|
||||
// Add caret
|
||||
let dropdownIcon = $(
|
||||
'<svg class="caret" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M7 10l5 5 5-5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>'
|
||||
);
|
||||
this.$el.before(dropdownIcon[0]);
|
||||
|
||||
// Initialize dropdown
|
||||
if (!this.el.disabled) {
|
||||
let dropdownOptions = $.extend({}, this.options.dropdownOptions);
|
||||
|
||||
// Add callback for centering selected option when dropdown content is scrollable
|
||||
dropdownOptions.onOpenEnd = (el) => {
|
||||
let selectedOption = $(this.dropdownOptions)
|
||||
.find('.selected')
|
||||
.first();
|
||||
|
||||
if (selectedOption.length) {
|
||||
// Focus selected option in dropdown
|
||||
M.keyDown = true;
|
||||
this.dropdown.focusedIndex = selectedOption.index();
|
||||
this.dropdown._focusFocusedItem();
|
||||
M.keyDown = false;
|
||||
|
||||
// Handle scrolling to selected option
|
||||
if (this.dropdown.isScrollable) {
|
||||
let scrollOffset =
|
||||
selectedOption[0].getBoundingClientRect().top -
|
||||
this.dropdownOptions.getBoundingClientRect().top; // scroll to selected option
|
||||
scrollOffset -= this.dropdownOptions.clientHeight / 2; // center in dropdown
|
||||
this.dropdownOptions.scrollTop = scrollOffset;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (this.isMultiple) {
|
||||
dropdownOptions.closeOnClick = false;
|
||||
}
|
||||
this.dropdown = M.Dropdown.init(this.input, dropdownOptions);
|
||||
}
|
||||
|
||||
// Add initial selections
|
||||
this._setSelectedStates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add option to value dict
|
||||
* @param {Element} el original option element
|
||||
* @param {Element} optionEl generated option element
|
||||
*/
|
||||
_addOptionToValueDict(el, optionEl) {
|
||||
let index = Object.keys(this._valueDict).length;
|
||||
let key = this.dropdownOptions.id + index;
|
||||
let obj = {};
|
||||
optionEl.id = key;
|
||||
|
||||
obj.el = el;
|
||||
obj.optionEl = optionEl;
|
||||
this._valueDict[key] = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove dropdown
|
||||
*/
|
||||
_removeDropdown() {
|
||||
$(this.wrapper)
|
||||
.find('.caret')
|
||||
.remove();
|
||||
$(this.input).remove();
|
||||
$(this.dropdownOptions).remove();
|
||||
$(this.wrapper).before(this.$el);
|
||||
$(this.wrapper).remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup dropdown
|
||||
* @param {Element} select select element
|
||||
* @param {Element} option option element from select
|
||||
* @param {String} type
|
||||
* @return {Element} option element added
|
||||
*/
|
||||
_appendOptionWithIcon(select, option, type) {
|
||||
// Add disabled attr if disabled
|
||||
let disabledClass = option.disabled ? 'disabled ' : '';
|
||||
let optgroupClass = type === 'optgroup-option' ? 'optgroup-option ' : '';
|
||||
let multipleCheckbox = this.isMultiple
|
||||
? `<label><input type="checkbox"${disabledClass}"/><span>${option.innerHTML}</span></label>`
|
||||
: option.innerHTML;
|
||||
let liEl = $('<li></li>');
|
||||
let spanEl = $('<span></span>');
|
||||
spanEl.html(multipleCheckbox);
|
||||
liEl.addClass(`${disabledClass} ${optgroupClass}`);
|
||||
liEl.append(spanEl);
|
||||
|
||||
// add icons
|
||||
let iconUrl = option.getAttribute('data-icon');
|
||||
if (!!iconUrl) {
|
||||
let imgEl = $(`<img alt="" src="${iconUrl}">`);
|
||||
liEl.prepend(imgEl);
|
||||
}
|
||||
|
||||
// Check for multiple type.
|
||||
$(this.dropdownOptions).append(liEl[0]);
|
||||
return liEl[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle entry from option
|
||||
* @param {String} key Option key
|
||||
* @return {Boolean} if entry was added or removed
|
||||
*/
|
||||
_toggleEntryFromArray(key) {
|
||||
let notAdded = !this._keysSelected.hasOwnProperty(key);
|
||||
let $optionLi = $(this._valueDict[key].optionEl);
|
||||
|
||||
if (notAdded) {
|
||||
this._keysSelected[key] = true;
|
||||
} else {
|
||||
delete this._keysSelected[key];
|
||||
}
|
||||
|
||||
$optionLi.toggleClass('selected', notAdded);
|
||||
|
||||
// Set checkbox checked value
|
||||
$optionLi.find('input[type="checkbox"]').prop('checked', notAdded);
|
||||
|
||||
// use notAdded instead of true (to detect if the option is selected or not)
|
||||
$optionLi.prop('selected', notAdded);
|
||||
|
||||
return notAdded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text value to input
|
||||
*/
|
||||
_setValueToInput() {
|
||||
let values = [];
|
||||
let options = this.$el.find('option');
|
||||
|
||||
options.each((el) => {
|
||||
if ($(el).prop('selected')) {
|
||||
let text = $(el).text();
|
||||
values.push(text);
|
||||
}
|
||||
});
|
||||
|
||||
if (!values.length) {
|
||||
let firstDisabled = this.$el.find('option:disabled').eq(0);
|
||||
if (firstDisabled.length && firstDisabled[0].value === '') {
|
||||
values.push(firstDisabled.text());
|
||||
}
|
||||
}
|
||||
|
||||
this.input.value = values.join(', ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set selected state of dropdown to match actual select element
|
||||
*/
|
||||
_setSelectedStates() {
|
||||
this._keysSelected = {};
|
||||
|
||||
for (let key in this._valueDict) {
|
||||
let option = this._valueDict[key];
|
||||
let optionIsSelected = $(option.el).prop('selected');
|
||||
$(option.optionEl)
|
||||
.find('input[type="checkbox"]')
|
||||
.prop('checked', optionIsSelected);
|
||||
if (optionIsSelected) {
|
||||
this._activateOption($(this.dropdownOptions), $(option.optionEl));
|
||||
this._keysSelected[key] = true;
|
||||
} else {
|
||||
$(option.optionEl).removeClass('selected');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make option as selected and scroll to selected position
|
||||
* @param {jQuery} collection Select options jQuery element
|
||||
* @param {Element} newOption element of the new option
|
||||
*/
|
||||
_activateOption(collection, newOption) {
|
||||
if (newOption) {
|
||||
if (!this.isMultiple) {
|
||||
collection.find('li.selected').removeClass('selected');
|
||||
}
|
||||
let option = $(newOption);
|
||||
option.addClass('selected');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Selected Values
|
||||
* @return {Array} Array of selected values
|
||||
*/
|
||||
getSelectedValues() {
|
||||
let selectedValues = [];
|
||||
for (let key in this._keysSelected) {
|
||||
selectedValues.push(this._valueDict[key].el.value);
|
||||
}
|
||||
return selectedValues;
|
||||
}
|
||||
}
|
||||
|
||||
M.FormSelect = FormSelect;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(FormSelect, 'formSelect', 'M_FormSelect');
|
||||
}
|
||||
})(cash);
|
||||
|
|
@ -0,0 +1,580 @@
|
|||
(function($, anim) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
edge: 'left',
|
||||
draggable: true,
|
||||
inDuration: 250,
|
||||
outDuration: 200,
|
||||
onOpenStart: null,
|
||||
onOpenEnd: null,
|
||||
onCloseStart: null,
|
||||
onCloseEnd: null,
|
||||
preventScrolling: true
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
class Sidenav extends Component {
|
||||
/**
|
||||
* Construct Sidenav instance and set up overlay
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Sidenav, el, options);
|
||||
|
||||
this.el.M_Sidenav = this;
|
||||
this.id = this.$el.attr('id');
|
||||
|
||||
/**
|
||||
* Options for the Sidenav
|
||||
* @member Sidenav#options
|
||||
* @prop {String} [edge='left'] - Side of screen on which Sidenav appears
|
||||
* @prop {Boolean} [draggable=true] - Allow swipe gestures to open/close Sidenav
|
||||
* @prop {Number} [inDuration=250] - Length in ms of enter transition
|
||||
* @prop {Number} [outDuration=200] - Length in ms of exit transition
|
||||
* @prop {Function} onOpenStart - Function called when sidenav starts entering
|
||||
* @prop {Function} onOpenEnd - Function called when sidenav finishes entering
|
||||
* @prop {Function} onCloseStart - Function called when sidenav starts exiting
|
||||
* @prop {Function} onCloseEnd - Function called when sidenav finishes exiting
|
||||
*/
|
||||
this.options = $.extend({}, Sidenav.defaults, options);
|
||||
|
||||
/**
|
||||
* Describes open/close state of Sidenav
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.isOpen = false;
|
||||
|
||||
/**
|
||||
* Describes if Sidenav is fixed
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.isFixed = this.el.classList.contains('sidenav-fixed');
|
||||
|
||||
/**
|
||||
* Describes if Sidenav is being draggeed
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.isDragged = false;
|
||||
|
||||
// Window size variables for window resize checks
|
||||
this.lastWindowWidth = window.innerWidth;
|
||||
this.lastWindowHeight = window.innerHeight;
|
||||
|
||||
this._createOverlay();
|
||||
this._createDragTarget();
|
||||
this._setupEventHandlers();
|
||||
this._setupClasses();
|
||||
this._setupFixed();
|
||||
|
||||
Sidenav._sidenavs.push(this);
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Sidenav;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this._enableBodyScrolling();
|
||||
this._overlay.parentNode.removeChild(this._overlay);
|
||||
this.dragTarget.parentNode.removeChild(this.dragTarget);
|
||||
this.el.M_Sidenav = undefined;
|
||||
this.el.style.transform = '';
|
||||
|
||||
let index = Sidenav._sidenavs.indexOf(this);
|
||||
if (index >= 0) {
|
||||
Sidenav._sidenavs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
_createOverlay() {
|
||||
let overlay = document.createElement('div');
|
||||
this._closeBound = this.close.bind(this);
|
||||
overlay.classList.add('sidenav-overlay');
|
||||
|
||||
overlay.addEventListener('click', this._closeBound);
|
||||
|
||||
document.body.appendChild(overlay);
|
||||
this._overlay = overlay;
|
||||
}
|
||||
|
||||
_setupEventHandlers() {
|
||||
if (Sidenav._sidenavs.length === 0) {
|
||||
document.body.addEventListener('click', this._handleTriggerClick);
|
||||
}
|
||||
|
||||
this._handleDragTargetDragBound = this._handleDragTargetDrag.bind(this);
|
||||
this._handleDragTargetReleaseBound = this._handleDragTargetRelease.bind(this);
|
||||
this._handleCloseDragBound = this._handleCloseDrag.bind(this);
|
||||
this._handleCloseReleaseBound = this._handleCloseRelease.bind(this);
|
||||
this._handleCloseTriggerClickBound = this._handleCloseTriggerClick.bind(this);
|
||||
|
||||
this.dragTarget.addEventListener('touchmove', this._handleDragTargetDragBound);
|
||||
this.dragTarget.addEventListener('touchend', this._handleDragTargetReleaseBound);
|
||||
this._overlay.addEventListener('touchmove', this._handleCloseDragBound);
|
||||
this._overlay.addEventListener('touchend', this._handleCloseReleaseBound);
|
||||
this.el.addEventListener('touchmove', this._handleCloseDragBound);
|
||||
this.el.addEventListener('touchend', this._handleCloseReleaseBound);
|
||||
this.el.addEventListener('click', this._handleCloseTriggerClickBound);
|
||||
|
||||
// Add resize for side nav fixed
|
||||
if (this.isFixed) {
|
||||
this._handleWindowResizeBound = this._handleWindowResize.bind(this);
|
||||
window.addEventListener('resize', this._handleWindowResizeBound);
|
||||
}
|
||||
}
|
||||
|
||||
_removeEventHandlers() {
|
||||
if (Sidenav._sidenavs.length === 1) {
|
||||
document.body.removeEventListener('click', this._handleTriggerClick);
|
||||
}
|
||||
|
||||
this.dragTarget.removeEventListener('touchmove', this._handleDragTargetDragBound);
|
||||
this.dragTarget.removeEventListener('touchend', this._handleDragTargetReleaseBound);
|
||||
this._overlay.removeEventListener('touchmove', this._handleCloseDragBound);
|
||||
this._overlay.removeEventListener('touchend', this._handleCloseReleaseBound);
|
||||
this.el.removeEventListener('touchmove', this._handleCloseDragBound);
|
||||
this.el.removeEventListener('touchend', this._handleCloseReleaseBound);
|
||||
this.el.removeEventListener('click', this._handleCloseTriggerClickBound);
|
||||
|
||||
// Remove resize for side nav fixed
|
||||
if (this.isFixed) {
|
||||
window.removeEventListener('resize', this._handleWindowResizeBound);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Trigger Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleTriggerClick(e) {
|
||||
let $trigger = $(e.target).closest('.sidenav-trigger');
|
||||
if (e.target && $trigger.length) {
|
||||
let sidenavId = M.getIdFromTrigger($trigger[0]);
|
||||
|
||||
let sidenavInstance = document.getElementById(sidenavId).M_Sidenav;
|
||||
if (sidenavInstance) {
|
||||
sidenavInstance.open($trigger);
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set variables needed at the beggining of drag
|
||||
* and stop any current transition.
|
||||
* @param {Event} e
|
||||
*/
|
||||
_startDrag(e) {
|
||||
let clientX = e.targetTouches[0].clientX;
|
||||
this.isDragged = true;
|
||||
this._startingXpos = clientX;
|
||||
this._xPos = this._startingXpos;
|
||||
this._time = Date.now();
|
||||
this._width = this.el.getBoundingClientRect().width;
|
||||
this._overlay.style.display = 'block';
|
||||
this._initialScrollTop = this.isOpen ? this.el.scrollTop : M.getDocumentScrollTop();
|
||||
this._verticallyScrolling = false;
|
||||
anim.remove(this.el);
|
||||
anim.remove(this._overlay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set variables needed at each drag move update tick
|
||||
* @param {Event} e
|
||||
*/
|
||||
_dragMoveUpdate(e) {
|
||||
let clientX = e.targetTouches[0].clientX;
|
||||
let currentScrollTop = this.isOpen ? this.el.scrollTop : M.getDocumentScrollTop();
|
||||
this.deltaX = Math.abs(this._xPos - clientX);
|
||||
this._xPos = clientX;
|
||||
this.velocityX = this.deltaX / (Date.now() - this._time);
|
||||
this._time = Date.now();
|
||||
if (this._initialScrollTop !== currentScrollTop) {
|
||||
this._verticallyScrolling = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Dragging of Sidenav
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleDragTargetDrag(e) {
|
||||
// Check if draggable
|
||||
if (!this.options.draggable || this._isCurrentlyFixed() || this._verticallyScrolling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If not being dragged, set initial drag start variables
|
||||
if (!this.isDragged) {
|
||||
this._startDrag(e);
|
||||
}
|
||||
|
||||
// Run touchmove updates
|
||||
this._dragMoveUpdate(e);
|
||||
|
||||
// Calculate raw deltaX
|
||||
let totalDeltaX = this._xPos - this._startingXpos;
|
||||
|
||||
// dragDirection is the attempted user drag direction
|
||||
let dragDirection = totalDeltaX > 0 ? 'right' : 'left';
|
||||
|
||||
// Don't allow totalDeltaX to exceed Sidenav width or be dragged in the opposite direction
|
||||
totalDeltaX = Math.min(this._width, Math.abs(totalDeltaX));
|
||||
if (this.options.edge === dragDirection) {
|
||||
totalDeltaX = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* transformX is the drag displacement
|
||||
* transformPrefix is the initial transform placement
|
||||
* Invert values if Sidenav is right edge
|
||||
*/
|
||||
let transformX = totalDeltaX;
|
||||
let transformPrefix = 'translateX(-100%)';
|
||||
if (this.options.edge === 'right') {
|
||||
transformPrefix = 'translateX(100%)';
|
||||
transformX = -transformX;
|
||||
}
|
||||
|
||||
// Calculate open/close percentage of sidenav, with open = 1 and close = 0
|
||||
this.percentOpen = Math.min(1, totalDeltaX / this._width);
|
||||
|
||||
// Set transform and opacity styles
|
||||
this.el.style.transform = `${transformPrefix} translateX(${transformX}px)`;
|
||||
this._overlay.style.opacity = this.percentOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Drag Target Release
|
||||
*/
|
||||
_handleDragTargetRelease() {
|
||||
if (this.isDragged) {
|
||||
if (this.percentOpen > 0.2) {
|
||||
this.open();
|
||||
} else {
|
||||
this._animateOut();
|
||||
}
|
||||
|
||||
this.isDragged = false;
|
||||
this._verticallyScrolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Close Drag
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleCloseDrag(e) {
|
||||
if (this.isOpen) {
|
||||
// Check if draggable
|
||||
if (!this.options.draggable || this._isCurrentlyFixed() || this._verticallyScrolling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If not being dragged, set initial drag start variables
|
||||
if (!this.isDragged) {
|
||||
this._startDrag(e);
|
||||
}
|
||||
|
||||
// Run touchmove updates
|
||||
this._dragMoveUpdate(e);
|
||||
|
||||
// Calculate raw deltaX
|
||||
let totalDeltaX = this._xPos - this._startingXpos;
|
||||
|
||||
// dragDirection is the attempted user drag direction
|
||||
let dragDirection = totalDeltaX > 0 ? 'right' : 'left';
|
||||
|
||||
// Don't allow totalDeltaX to exceed Sidenav width or be dragged in the opposite direction
|
||||
totalDeltaX = Math.min(this._width, Math.abs(totalDeltaX));
|
||||
if (this.options.edge !== dragDirection) {
|
||||
totalDeltaX = 0;
|
||||
}
|
||||
|
||||
let transformX = -totalDeltaX;
|
||||
if (this.options.edge === 'right') {
|
||||
transformX = -transformX;
|
||||
}
|
||||
|
||||
// Calculate open/close percentage of sidenav, with open = 1 and close = 0
|
||||
this.percentOpen = Math.min(1, 1 - totalDeltaX / this._width);
|
||||
|
||||
// Set transform and opacity styles
|
||||
this.el.style.transform = `translateX(${transformX}px)`;
|
||||
this._overlay.style.opacity = this.percentOpen;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Close Release
|
||||
*/
|
||||
_handleCloseRelease() {
|
||||
if (this.isOpen && this.isDragged) {
|
||||
if (this.percentOpen > 0.8) {
|
||||
this._animateIn();
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
|
||||
this.isDragged = false;
|
||||
this._verticallyScrolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles closing of Sidenav when element with class .sidenav-close
|
||||
*/
|
||||
_handleCloseTriggerClick(e) {
|
||||
let $closeTrigger = $(e.target).closest('.sidenav-close');
|
||||
if ($closeTrigger.length && !this._isCurrentlyFixed()) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Window Resize
|
||||
*/
|
||||
_handleWindowResize() {
|
||||
// Only handle horizontal resizes
|
||||
if (this.lastWindowWidth !== window.innerWidth) {
|
||||
if (window.innerWidth > 992) {
|
||||
this.open();
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
this.lastWindowWidth = window.innerWidth;
|
||||
this.lastWindowHeight = window.innerHeight;
|
||||
}
|
||||
|
||||
_setupClasses() {
|
||||
if (this.options.edge === 'right') {
|
||||
this.el.classList.add('right-aligned');
|
||||
this.dragTarget.classList.add('right-aligned');
|
||||
}
|
||||
}
|
||||
|
||||
_removeClasses() {
|
||||
this.el.classList.remove('right-aligned');
|
||||
this.dragTarget.classList.remove('right-aligned');
|
||||
}
|
||||
|
||||
_setupFixed() {
|
||||
if (this._isCurrentlyFixed()) {
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
|
||||
_isCurrentlyFixed() {
|
||||
return this.isFixed && window.innerWidth > 992;
|
||||
}
|
||||
|
||||
_createDragTarget() {
|
||||
let dragTarget = document.createElement('div');
|
||||
dragTarget.classList.add('drag-target');
|
||||
document.body.appendChild(dragTarget);
|
||||
this.dragTarget = dragTarget;
|
||||
}
|
||||
|
||||
_preventBodyScrolling() {
|
||||
let body = document.body;
|
||||
body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
_enableBodyScrolling() {
|
||||
let body = document.body;
|
||||
body.style.overflow = '';
|
||||
}
|
||||
|
||||
open() {
|
||||
if (this.isOpen === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isOpen = true;
|
||||
|
||||
// Run onOpenStart callback
|
||||
if (typeof this.options.onOpenStart === 'function') {
|
||||
this.options.onOpenStart.call(this, this.el);
|
||||
}
|
||||
|
||||
// Handle fixed Sidenav
|
||||
if (this._isCurrentlyFixed()) {
|
||||
anim.remove(this.el);
|
||||
anim({
|
||||
targets: this.el,
|
||||
translateX: 0,
|
||||
duration: 0,
|
||||
easing: 'easeOutQuad'
|
||||
});
|
||||
this._enableBodyScrolling();
|
||||
this._overlay.style.display = 'none';
|
||||
|
||||
// Handle non-fixed Sidenav
|
||||
} else {
|
||||
if (this.options.preventScrolling) {
|
||||
this._preventBodyScrolling();
|
||||
}
|
||||
|
||||
if (!this.isDragged || this.percentOpen != 1) {
|
||||
this._animateIn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.isOpen === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isOpen = false;
|
||||
|
||||
// Run onCloseStart callback
|
||||
if (typeof this.options.onCloseStart === 'function') {
|
||||
this.options.onCloseStart.call(this, this.el);
|
||||
}
|
||||
|
||||
// Handle fixed Sidenav
|
||||
if (this._isCurrentlyFixed()) {
|
||||
let transformX = this.options.edge === 'left' ? '-105%' : '105%';
|
||||
this.el.style.transform = `translateX(${transformX})`;
|
||||
|
||||
// Handle non-fixed Sidenav
|
||||
} else {
|
||||
this._enableBodyScrolling();
|
||||
|
||||
if (!this.isDragged || this.percentOpen != 0) {
|
||||
this._animateOut();
|
||||
} else {
|
||||
this._overlay.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_animateIn() {
|
||||
this._animateSidenavIn();
|
||||
this._animateOverlayIn();
|
||||
}
|
||||
|
||||
_animateSidenavIn() {
|
||||
let slideOutPercent = this.options.edge === 'left' ? -1 : 1;
|
||||
if (this.isDragged) {
|
||||
slideOutPercent =
|
||||
this.options.edge === 'left'
|
||||
? slideOutPercent + this.percentOpen
|
||||
: slideOutPercent - this.percentOpen;
|
||||
}
|
||||
|
||||
anim.remove(this.el);
|
||||
anim({
|
||||
targets: this.el,
|
||||
translateX: [`${slideOutPercent * 100}%`, 0],
|
||||
duration: this.options.inDuration,
|
||||
easing: 'easeOutQuad',
|
||||
complete: () => {
|
||||
// Run onOpenEnd callback
|
||||
if (typeof this.options.onOpenEnd === 'function') {
|
||||
this.options.onOpenEnd.call(this, this.el);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_animateOverlayIn() {
|
||||
let start = 0;
|
||||
if (this.isDragged) {
|
||||
start = this.percentOpen;
|
||||
} else {
|
||||
$(this._overlay).css({
|
||||
display: 'block'
|
||||
});
|
||||
}
|
||||
|
||||
anim.remove(this._overlay);
|
||||
anim({
|
||||
targets: this._overlay,
|
||||
opacity: [start, 1],
|
||||
duration: this.options.inDuration,
|
||||
easing: 'easeOutQuad'
|
||||
});
|
||||
}
|
||||
|
||||
_animateOut() {
|
||||
this._animateSidenavOut();
|
||||
this._animateOverlayOut();
|
||||
}
|
||||
|
||||
_animateSidenavOut() {
|
||||
let endPercent = this.options.edge === 'left' ? -1 : 1;
|
||||
let slideOutPercent = 0;
|
||||
if (this.isDragged) {
|
||||
slideOutPercent =
|
||||
this.options.edge === 'left'
|
||||
? endPercent + this.percentOpen
|
||||
: endPercent - this.percentOpen;
|
||||
}
|
||||
|
||||
anim.remove(this.el);
|
||||
anim({
|
||||
targets: this.el,
|
||||
translateX: [`${slideOutPercent * 100}%`, `${endPercent * 105}%`],
|
||||
duration: this.options.outDuration,
|
||||
easing: 'easeOutQuad',
|
||||
complete: () => {
|
||||
// Run onOpenEnd callback
|
||||
if (typeof this.options.onCloseEnd === 'function') {
|
||||
this.options.onCloseEnd.call(this, this.el);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_animateOverlayOut() {
|
||||
anim.remove(this._overlay);
|
||||
anim({
|
||||
targets: this._overlay,
|
||||
opacity: 0,
|
||||
duration: this.options.outDuration,
|
||||
easing: 'easeOutQuad',
|
||||
complete: () => {
|
||||
$(this._overlay).css('display', 'none');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof Sidenav
|
||||
* @type {Array.<Sidenav>}
|
||||
*/
|
||||
Sidenav._sidenavs = [];
|
||||
|
||||
M.Sidenav = Sidenav;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Sidenav, 'sidenav', 'M_Sidenav');
|
||||
}
|
||||
})(cash, M.anime);
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
(function($, anim) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
indicators: true,
|
||||
height: 400,
|
||||
duration: 500,
|
||||
interval: 6000
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class Slider extends Component {
|
||||
/**
|
||||
* Construct Slider instance and set up overlay
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Slider, el, options);
|
||||
|
||||
this.el.M_Slider = this;
|
||||
|
||||
/**
|
||||
* Options for the modal
|
||||
* @member Slider#options
|
||||
* @prop {Boolean} [indicators=true] - Show indicators
|
||||
* @prop {Number} [height=400] - height of slider
|
||||
* @prop {Number} [duration=500] - Length in ms of slide transition
|
||||
* @prop {Number} [interval=6000] - Length in ms of slide interval
|
||||
*/
|
||||
this.options = $.extend({}, Slider.defaults, options);
|
||||
|
||||
// setup
|
||||
this.$slider = this.$el.find('.slides');
|
||||
this.$slides = this.$slider.children('li');
|
||||
this.activeIndex = this.$slides
|
||||
.filter(function(item) {
|
||||
return $(item).hasClass('active');
|
||||
})
|
||||
.first()
|
||||
.index();
|
||||
if (this.activeIndex != -1) {
|
||||
this.$active = this.$slides.eq(this.activeIndex);
|
||||
}
|
||||
|
||||
this._setSliderHeight();
|
||||
|
||||
// Set initial positions of captions
|
||||
this.$slides.find('.caption').each((el) => {
|
||||
this._animateCaptionIn(el, 0);
|
||||
});
|
||||
|
||||
// Move img src into background-image
|
||||
this.$slides.find('img').each((el) => {
|
||||
let placeholderBase64 =
|
||||
'data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
|
||||
if ($(el).attr('src') !== placeholderBase64) {
|
||||
$(el).css('background-image', 'url("' + $(el).attr('src') + '")');
|
||||
$(el).attr('src', placeholderBase64);
|
||||
}
|
||||
});
|
||||
|
||||
this._setupIndicators();
|
||||
|
||||
// Show active slide
|
||||
if (this.$active) {
|
||||
this.$active.css('display', 'block');
|
||||
} else {
|
||||
this.$slides.first().addClass('active');
|
||||
anim({
|
||||
targets: this.$slides.first()[0],
|
||||
opacity: 1,
|
||||
duration: this.options.duration,
|
||||
easing: 'easeOutQuad'
|
||||
});
|
||||
|
||||
this.activeIndex = 0;
|
||||
this.$active = this.$slides.eq(this.activeIndex);
|
||||
|
||||
// Update indicators
|
||||
if (this.options.indicators) {
|
||||
this.$indicators.eq(this.activeIndex).addClass('active');
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust height to current slide
|
||||
this.$active.find('img').each((el) => {
|
||||
anim({
|
||||
targets: this.$active.find('.caption')[0],
|
||||
opacity: 1,
|
||||
translateX: 0,
|
||||
translateY: 0,
|
||||
duration: this.options.duration,
|
||||
easing: 'easeOutQuad'
|
||||
});
|
||||
});
|
||||
|
||||
this._setupEventHandlers();
|
||||
|
||||
// auto scroll
|
||||
this.start();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Slider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this.pause();
|
||||
this._removeIndicators();
|
||||
this._removeEventHandlers();
|
||||
this.el.M_Slider = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleIntervalBound = this._handleInterval.bind(this);
|
||||
this._handleIndicatorClickBound = this._handleIndicatorClick.bind(this);
|
||||
|
||||
if (this.options.indicators) {
|
||||
this.$indicators.each((el) => {
|
||||
el.addEventListener('click', this._handleIndicatorClickBound);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
if (this.options.indicators) {
|
||||
this.$indicators.each((el) => {
|
||||
el.removeEventListener('click', this._handleIndicatorClickBound);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle indicator click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleIndicatorClick(e) {
|
||||
let currIndex = $(e.target).index();
|
||||
this.set(currIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Interval
|
||||
*/
|
||||
_handleInterval() {
|
||||
let newActiveIndex = this.$slider.find('.active').index();
|
||||
if (this.$slides.length === newActiveIndex + 1) newActiveIndex = 0;
|
||||
// loop to start
|
||||
else newActiveIndex += 1;
|
||||
|
||||
this.set(newActiveIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate in caption
|
||||
* @param {Element} caption
|
||||
* @param {Number} duration
|
||||
*/
|
||||
_animateCaptionIn(caption, duration) {
|
||||
let animOptions = {
|
||||
targets: caption,
|
||||
opacity: 0,
|
||||
duration: duration,
|
||||
easing: 'easeOutQuad'
|
||||
};
|
||||
|
||||
if ($(caption).hasClass('center-align')) {
|
||||
animOptions.translateY = -100;
|
||||
} else if ($(caption).hasClass('right-align')) {
|
||||
animOptions.translateX = 100;
|
||||
} else if ($(caption).hasClass('left-align')) {
|
||||
animOptions.translateX = -100;
|
||||
}
|
||||
|
||||
anim(animOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set height of slider
|
||||
*/
|
||||
_setSliderHeight() {
|
||||
// If fullscreen, do nothing
|
||||
if (!this.$el.hasClass('fullscreen')) {
|
||||
if (this.options.indicators) {
|
||||
// Add height if indicators are present
|
||||
this.$el.css('height', this.options.height + 40 + 'px');
|
||||
} else {
|
||||
this.$el.css('height', this.options.height + 'px');
|
||||
}
|
||||
this.$slider.css('height', this.options.height + 'px');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup indicators
|
||||
*/
|
||||
_setupIndicators() {
|
||||
if (this.options.indicators) {
|
||||
this.$indicators = $('<ul class="indicators"></ul>');
|
||||
this.$slides.each((el, index) => {
|
||||
let $indicator = $('<li class="indicator-item"></li>');
|
||||
this.$indicators.append($indicator[0]);
|
||||
});
|
||||
this.$el.append(this.$indicators[0]);
|
||||
this.$indicators = this.$indicators.children('li.indicator-item');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove indicators
|
||||
*/
|
||||
_removeIndicators() {
|
||||
this.$el.find('ul.indicators').remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycle to nth item
|
||||
* @param {Number} index
|
||||
*/
|
||||
set(index) {
|
||||
// Wrap around indices.
|
||||
if (index >= this.$slides.length) index = 0;
|
||||
else if (index < 0) index = this.$slides.length - 1;
|
||||
|
||||
// Only do if index changes
|
||||
if (this.activeIndex != index) {
|
||||
this.$active = this.$slides.eq(this.activeIndex);
|
||||
let $caption = this.$active.find('.caption');
|
||||
this.$active.removeClass('active');
|
||||
|
||||
anim({
|
||||
targets: this.$active[0],
|
||||
opacity: 0,
|
||||
duration: this.options.duration,
|
||||
easing: 'easeOutQuad',
|
||||
complete: () => {
|
||||
this.$slides.not('.active').each((el) => {
|
||||
anim({
|
||||
targets: el,
|
||||
opacity: 0,
|
||||
translateX: 0,
|
||||
translateY: 0,
|
||||
duration: 0,
|
||||
easing: 'easeOutQuad'
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this._animateCaptionIn($caption[0], this.options.duration);
|
||||
|
||||
// Update indicators
|
||||
if (this.options.indicators) {
|
||||
this.$indicators.eq(this.activeIndex).removeClass('active');
|
||||
this.$indicators.eq(index).addClass('active');
|
||||
}
|
||||
|
||||
anim({
|
||||
targets: this.$slides.eq(index)[0],
|
||||
opacity: 1,
|
||||
duration: this.options.duration,
|
||||
easing: 'easeOutQuad'
|
||||
});
|
||||
|
||||
anim({
|
||||
targets: this.$slides.eq(index).find('.caption')[0],
|
||||
opacity: 1,
|
||||
translateX: 0,
|
||||
translateY: 0,
|
||||
duration: this.options.duration,
|
||||
delay: this.options.duration,
|
||||
easing: 'easeOutQuad'
|
||||
});
|
||||
|
||||
this.$slides.eq(index).addClass('active');
|
||||
this.activeIndex = index;
|
||||
|
||||
// Reset interval
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause slider interval
|
||||
*/
|
||||
pause() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start slider interval
|
||||
*/
|
||||
start() {
|
||||
clearInterval(this.interval);
|
||||
this.interval = setInterval(
|
||||
this._handleIntervalBound,
|
||||
this.options.duration + this.options.interval
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move to next slide
|
||||
*/
|
||||
next() {
|
||||
let newIndex = this.activeIndex + 1;
|
||||
|
||||
// Wrap around indices.
|
||||
if (newIndex >= this.$slides.length) newIndex = 0;
|
||||
else if (newIndex < 0) newIndex = this.$slides.length - 1;
|
||||
|
||||
this.set(newIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move to previous slide
|
||||
*/
|
||||
prev() {
|
||||
let newIndex = this.activeIndex - 1;
|
||||
|
||||
// Wrap around indices.
|
||||
if (newIndex >= this.$slides.length) newIndex = 0;
|
||||
else if (newIndex < 0) newIndex = this.$slides.length - 1;
|
||||
|
||||
this.set(newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
M.Slider = Slider;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Slider, 'slider', 'M_Slider');
|
||||
}
|
||||
})(cash, M.anime);
|
||||
|
|
@ -0,0 +1,402 @@
|
|||
(function($, anim) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
duration: 300,
|
||||
onShow: null,
|
||||
swipeable: false,
|
||||
responsiveThreshold: Infinity // breakpoint for swipeable
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class Tabs extends Component {
|
||||
/**
|
||||
* Construct Tabs instance
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Tabs, el, options);
|
||||
|
||||
this.el.M_Tabs = this;
|
||||
|
||||
/**
|
||||
* Options for the Tabs
|
||||
* @member Tabs#options
|
||||
* @prop {Number} duration
|
||||
* @prop {Function} onShow
|
||||
* @prop {Boolean} swipeable
|
||||
* @prop {Number} responsiveThreshold
|
||||
*/
|
||||
this.options = $.extend({}, Tabs.defaults, options);
|
||||
|
||||
// Setup
|
||||
this.$tabLinks = this.$el.children('li.tab').children('a');
|
||||
this.index = 0;
|
||||
this._setupActiveTabLink();
|
||||
|
||||
// Setup tabs content
|
||||
if (this.options.swipeable) {
|
||||
this._setupSwipeableTabs();
|
||||
} else {
|
||||
this._setupNormalTabs();
|
||||
}
|
||||
|
||||
// Setup tabs indicator after content to ensure accurate widths
|
||||
this._setTabsAndTabWidth();
|
||||
this._createIndicator();
|
||||
|
||||
this._setupEventHandlers();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this._indicator.parentNode.removeChild(this._indicator);
|
||||
|
||||
if (this.options.swipeable) {
|
||||
this._teardownSwipeableTabs();
|
||||
} else {
|
||||
this._teardownNormalTabs();
|
||||
}
|
||||
|
||||
this.$el[0].M_Tabs = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleWindowResizeBound = this._handleWindowResize.bind(this);
|
||||
window.addEventListener('resize', this._handleWindowResizeBound);
|
||||
|
||||
this._handleTabClickBound = this._handleTabClick.bind(this);
|
||||
this.el.addEventListener('click', this._handleTabClickBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
window.removeEventListener('resize', this._handleWindowResizeBound);
|
||||
this.el.removeEventListener('click', this._handleTabClickBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle window Resize
|
||||
*/
|
||||
_handleWindowResize() {
|
||||
this._setTabsAndTabWidth();
|
||||
|
||||
if (this.tabWidth !== 0 && this.tabsWidth !== 0) {
|
||||
this._indicator.style.left = this._calcLeftPos(this.$activeTabLink) + 'px';
|
||||
this._indicator.style.right = this._calcRightPos(this.$activeTabLink) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle tab click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleTabClick(e) {
|
||||
let tab = $(e.target).closest('li.tab');
|
||||
let tabLink = $(e.target).closest('a');
|
||||
|
||||
// Handle click on tab link only
|
||||
if (!tabLink.length || !tabLink.parent().hasClass('tab')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tab.hasClass('disabled')) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// Act as regular link if target attribute is specified.
|
||||
if (!!tabLink.attr('target')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make the old tab inactive.
|
||||
this.$activeTabLink.removeClass('active');
|
||||
let $oldContent = this.$content;
|
||||
|
||||
// Update the variables with the new link and content
|
||||
this.$activeTabLink = tabLink;
|
||||
this.$content = $(M.escapeHash(tabLink[0].hash));
|
||||
this.$tabLinks = this.$el.children('li.tab').children('a');
|
||||
|
||||
// Make the tab active.
|
||||
this.$activeTabLink.addClass('active');
|
||||
let prevIndex = this.index;
|
||||
this.index = Math.max(this.$tabLinks.index(tabLink), 0);
|
||||
|
||||
// Swap content
|
||||
if (this.options.swipeable) {
|
||||
if (this._tabsCarousel) {
|
||||
this._tabsCarousel.set(this.index, () => {
|
||||
if (typeof this.options.onShow === 'function') {
|
||||
this.options.onShow.call(this, this.$content[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (this.$content.length) {
|
||||
this.$content[0].style.display = 'block';
|
||||
this.$content.addClass('active');
|
||||
if (typeof this.options.onShow === 'function') {
|
||||
this.options.onShow.call(this, this.$content[0]);
|
||||
}
|
||||
|
||||
if ($oldContent.length && !$oldContent.is(this.$content)) {
|
||||
$oldContent[0].style.display = 'none';
|
||||
$oldContent.removeClass('active');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update widths after content is swapped (scrollbar bugfix)
|
||||
this._setTabsAndTabWidth();
|
||||
|
||||
// Update indicator
|
||||
this._animateIndicator(prevIndex);
|
||||
|
||||
// Prevent the anchor's default click action
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate elements for tab indicator.
|
||||
*/
|
||||
_createIndicator() {
|
||||
let indicator = document.createElement('li');
|
||||
indicator.classList.add('indicator');
|
||||
|
||||
this.el.appendChild(indicator);
|
||||
this._indicator = indicator;
|
||||
|
||||
setTimeout(() => {
|
||||
this._indicator.style.left = this._calcLeftPos(this.$activeTabLink) + 'px';
|
||||
this._indicator.style.right = this._calcRightPos(this.$activeTabLink) + 'px';
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup first active tab link.
|
||||
*/
|
||||
_setupActiveTabLink() {
|
||||
// If the location.hash matches one of the links, use that as the active tab.
|
||||
this.$activeTabLink = $(this.$tabLinks.filter('[href="' + location.hash + '"]'));
|
||||
|
||||
// If no match is found, use the first link or any with class 'active' as the initial active tab.
|
||||
if (this.$activeTabLink.length === 0) {
|
||||
this.$activeTabLink = this.$el
|
||||
.children('li.tab')
|
||||
.children('a.active')
|
||||
.first();
|
||||
}
|
||||
if (this.$activeTabLink.length === 0) {
|
||||
this.$activeTabLink = this.$el
|
||||
.children('li.tab')
|
||||
.children('a')
|
||||
.first();
|
||||
}
|
||||
|
||||
this.$tabLinks.removeClass('active');
|
||||
this.$activeTabLink[0].classList.add('active');
|
||||
|
||||
this.index = Math.max(this.$tabLinks.index(this.$activeTabLink), 0);
|
||||
|
||||
if (this.$activeTabLink.length) {
|
||||
this.$content = $(M.escapeHash(this.$activeTabLink[0].hash));
|
||||
this.$content.addClass('active');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup swipeable tabs
|
||||
*/
|
||||
_setupSwipeableTabs() {
|
||||
// Change swipeable according to responsive threshold
|
||||
if (window.innerWidth > this.options.responsiveThreshold) {
|
||||
this.options.swipeable = false;
|
||||
}
|
||||
|
||||
let $tabsContent = $();
|
||||
this.$tabLinks.each((link) => {
|
||||
let $currContent = $(M.escapeHash(link.hash));
|
||||
$currContent.addClass('carousel-item');
|
||||
$tabsContent = $tabsContent.add($currContent);
|
||||
});
|
||||
|
||||
let $tabsWrapper = $('<div class="tabs-content carousel carousel-slider"></div>');
|
||||
$tabsContent.first().before($tabsWrapper);
|
||||
$tabsWrapper.append($tabsContent);
|
||||
$tabsContent[0].style.display = '';
|
||||
|
||||
// Keep active tab index to set initial carousel slide
|
||||
let activeTabIndex = this.$activeTabLink.closest('.tab').index();
|
||||
|
||||
this._tabsCarousel = M.Carousel.init($tabsWrapper[0], {
|
||||
fullWidth: true,
|
||||
noWrap: true,
|
||||
onCycleTo: (item) => {
|
||||
let prevIndex = this.index;
|
||||
this.index = $(item).index();
|
||||
this.$activeTabLink.removeClass('active');
|
||||
this.$activeTabLink = this.$tabLinks.eq(this.index);
|
||||
this.$activeTabLink.addClass('active');
|
||||
this._animateIndicator(prevIndex);
|
||||
if (typeof this.options.onShow === 'function') {
|
||||
this.options.onShow.call(this, this.$content[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Set initial carousel slide to active tab
|
||||
this._tabsCarousel.set(activeTabIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown normal tabs.
|
||||
*/
|
||||
_teardownSwipeableTabs() {
|
||||
let $tabsWrapper = this._tabsCarousel.$el;
|
||||
this._tabsCarousel.destroy();
|
||||
|
||||
// Unwrap
|
||||
$tabsWrapper.after($tabsWrapper.children());
|
||||
$tabsWrapper.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup normal tabs.
|
||||
*/
|
||||
_setupNormalTabs() {
|
||||
// Hide Tabs Content
|
||||
this.$tabLinks.not(this.$activeTabLink).each((link) => {
|
||||
if (!!link.hash) {
|
||||
let $currContent = $(M.escapeHash(link.hash));
|
||||
if ($currContent.length) {
|
||||
$currContent[0].style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown normal tabs.
|
||||
*/
|
||||
_teardownNormalTabs() {
|
||||
// show Tabs Content
|
||||
this.$tabLinks.each((link) => {
|
||||
if (!!link.hash) {
|
||||
let $currContent = $(M.escapeHash(link.hash));
|
||||
if ($currContent.length) {
|
||||
$currContent[0].style.display = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* set tabs and tab width
|
||||
*/
|
||||
_setTabsAndTabWidth() {
|
||||
this.tabsWidth = this.$el.width();
|
||||
this.tabWidth = Math.max(this.tabsWidth, this.el.scrollWidth) / this.$tabLinks.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds right attribute for indicator based on active tab.
|
||||
* @param {cash} el
|
||||
*/
|
||||
_calcRightPos(el) {
|
||||
return Math.ceil(this.tabsWidth - el.position().left - el[0].getBoundingClientRect().width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds left attribute for indicator based on active tab.
|
||||
* @param {cash} el
|
||||
*/
|
||||
_calcLeftPos(el) {
|
||||
return Math.floor(el.position().left);
|
||||
}
|
||||
|
||||
updateTabIndicator() {
|
||||
this._setTabsAndTabWidth();
|
||||
this._animateIndicator(this.index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates Indicator to active tab.
|
||||
* @param {Number} prevIndex
|
||||
*/
|
||||
_animateIndicator(prevIndex) {
|
||||
let leftDelay = 0,
|
||||
rightDelay = 0;
|
||||
|
||||
if (this.index - prevIndex >= 0) {
|
||||
leftDelay = 90;
|
||||
} else {
|
||||
rightDelay = 90;
|
||||
}
|
||||
|
||||
// Animate
|
||||
let animOptions = {
|
||||
targets: this._indicator,
|
||||
left: {
|
||||
value: this._calcLeftPos(this.$activeTabLink),
|
||||
delay: leftDelay
|
||||
},
|
||||
right: {
|
||||
value: this._calcRightPos(this.$activeTabLink),
|
||||
delay: rightDelay
|
||||
},
|
||||
duration: this.options.duration,
|
||||
easing: 'easeOutQuad'
|
||||
};
|
||||
anim.remove(this._indicator);
|
||||
anim(animOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select tab.
|
||||
* @param {String} tabId
|
||||
*/
|
||||
select(tabId) {
|
||||
let tab = this.$tabLinks.filter('[href="#' + tabId + '"]');
|
||||
if (tab.length) {
|
||||
tab.trigger('click');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
M.Tabs = Tabs;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Tabs, 'tabs', 'M_Tabs');
|
||||
}
|
||||
})(cash, M.anime);
|
||||
|
|
@ -0,0 +1,314 @@
|
|||
(function($) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
onOpen: undefined,
|
||||
onClose: undefined
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class TapTarget extends Component {
|
||||
/**
|
||||
* Construct TapTarget instance
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(TapTarget, el, options);
|
||||
|
||||
this.el.M_TapTarget = this;
|
||||
|
||||
/**
|
||||
* Options for the select
|
||||
* @member TapTarget#options
|
||||
* @prop {Function} onOpen - Callback function called when feature discovery is opened
|
||||
* @prop {Function} onClose - Callback function called when feature discovery is closed
|
||||
*/
|
||||
this.options = $.extend({}, TapTarget.defaults, options);
|
||||
|
||||
this.isOpen = false;
|
||||
|
||||
// setup
|
||||
this.$origin = $('#' + this.$el.attr('data-target'));
|
||||
this._setup();
|
||||
|
||||
this._calculatePositioning();
|
||||
this._setupEventHandlers();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_TapTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this.el.TapTarget = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleDocumentClickBound = this._handleDocumentClick.bind(this);
|
||||
this._handleTargetClickBound = this._handleTargetClick.bind(this);
|
||||
this._handleOriginClickBound = this._handleOriginClick.bind(this);
|
||||
|
||||
this.el.addEventListener('click', this._handleTargetClickBound);
|
||||
this.originEl.addEventListener('click', this._handleOriginClickBound);
|
||||
|
||||
// Resize
|
||||
let throttledResize = M.throttle(this._handleResize, 200);
|
||||
this._handleThrottledResizeBound = throttledResize.bind(this);
|
||||
|
||||
window.addEventListener('resize', this._handleThrottledResizeBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Event Handlers
|
||||
*/
|
||||
_removeEventHandlers() {
|
||||
this.el.removeEventListener('click', this._handleTargetClickBound);
|
||||
this.originEl.removeEventListener('click', this._handleOriginClickBound);
|
||||
window.removeEventListener('resize', this._handleThrottledResizeBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Target Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleTargetClick(e) {
|
||||
this.open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Origin Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleOriginClick(e) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Resize
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleResize(e) {
|
||||
this._calculatePositioning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Resize
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleDocumentClick(e) {
|
||||
if (!$(e.target).closest('.tap-target-wrapper').length) {
|
||||
this.close();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Tap Target
|
||||
*/
|
||||
_setup() {
|
||||
// Creating tap target
|
||||
this.wrapper = this.$el.parent()[0];
|
||||
this.waveEl = $(this.wrapper).find('.tap-target-wave')[0];
|
||||
this.originEl = $(this.wrapper).find('.tap-target-origin')[0];
|
||||
this.contentEl = this.$el.find('.tap-target-content')[0];
|
||||
|
||||
// Creating wrapper
|
||||
if (!$(this.wrapper).hasClass('.tap-target-wrapper')) {
|
||||
this.wrapper = document.createElement('div');
|
||||
this.wrapper.classList.add('tap-target-wrapper');
|
||||
this.$el.before($(this.wrapper));
|
||||
this.wrapper.append(this.el);
|
||||
}
|
||||
|
||||
// Creating content
|
||||
if (!this.contentEl) {
|
||||
this.contentEl = document.createElement('div');
|
||||
this.contentEl.classList.add('tap-target-content');
|
||||
this.$el.append(this.contentEl);
|
||||
}
|
||||
|
||||
// Creating foreground wave
|
||||
if (!this.waveEl) {
|
||||
this.waveEl = document.createElement('div');
|
||||
this.waveEl.classList.add('tap-target-wave');
|
||||
|
||||
// Creating origin
|
||||
if (!this.originEl) {
|
||||
this.originEl = this.$origin.clone(true, true);
|
||||
this.originEl.addClass('tap-target-origin');
|
||||
this.originEl.removeAttr('id');
|
||||
this.originEl.removeAttr('style');
|
||||
this.originEl = this.originEl[0];
|
||||
this.waveEl.append(this.originEl);
|
||||
}
|
||||
|
||||
this.wrapper.append(this.waveEl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate positioning
|
||||
*/
|
||||
_calculatePositioning() {
|
||||
// Element or parent is fixed position?
|
||||
let isFixed = this.$origin.css('position') === 'fixed';
|
||||
if (!isFixed) {
|
||||
let parents = this.$origin.parents();
|
||||
for (let i = 0; i < parents.length; i++) {
|
||||
isFixed = $(parents[i]).css('position') == 'fixed';
|
||||
if (isFixed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculating origin
|
||||
let originWidth = this.$origin.outerWidth();
|
||||
let originHeight = this.$origin.outerHeight();
|
||||
let originTop = isFixed
|
||||
? this.$origin.offset().top - M.getDocumentScrollTop()
|
||||
: this.$origin.offset().top;
|
||||
let originLeft = isFixed
|
||||
? this.$origin.offset().left - M.getDocumentScrollLeft()
|
||||
: this.$origin.offset().left;
|
||||
|
||||
// Calculating screen
|
||||
let windowWidth = window.innerWidth;
|
||||
let windowHeight = window.innerHeight;
|
||||
let centerX = windowWidth / 2;
|
||||
let centerY = windowHeight / 2;
|
||||
let isLeft = originLeft <= centerX;
|
||||
let isRight = originLeft > centerX;
|
||||
let isTop = originTop <= centerY;
|
||||
let isBottom = originTop > centerY;
|
||||
let isCenterX = originLeft >= windowWidth * 0.25 && originLeft <= windowWidth * 0.75;
|
||||
|
||||
// Calculating tap target
|
||||
let tapTargetWidth = this.$el.outerWidth();
|
||||
let tapTargetHeight = this.$el.outerHeight();
|
||||
let tapTargetTop = originTop + originHeight / 2 - tapTargetHeight / 2;
|
||||
let tapTargetLeft = originLeft + originWidth / 2 - tapTargetWidth / 2;
|
||||
let tapTargetPosition = isFixed ? 'fixed' : 'absolute';
|
||||
|
||||
// Calculating content
|
||||
let tapTargetTextWidth = isCenterX ? tapTargetWidth : tapTargetWidth / 2 + originWidth;
|
||||
let tapTargetTextHeight = tapTargetHeight / 2;
|
||||
let tapTargetTextTop = isTop ? tapTargetHeight / 2 : 0;
|
||||
let tapTargetTextBottom = 0;
|
||||
let tapTargetTextLeft = isLeft && !isCenterX ? tapTargetWidth / 2 - originWidth : 0;
|
||||
let tapTargetTextRight = 0;
|
||||
let tapTargetTextPadding = originWidth;
|
||||
let tapTargetTextAlign = isBottom ? 'bottom' : 'top';
|
||||
|
||||
// Calculating wave
|
||||
let tapTargetWaveWidth = originWidth > originHeight ? originWidth * 2 : originWidth * 2;
|
||||
let tapTargetWaveHeight = tapTargetWaveWidth;
|
||||
let tapTargetWaveTop = tapTargetHeight / 2 - tapTargetWaveHeight / 2;
|
||||
let tapTargetWaveLeft = tapTargetWidth / 2 - tapTargetWaveWidth / 2;
|
||||
|
||||
// Setting tap target
|
||||
let tapTargetWrapperCssObj = {};
|
||||
tapTargetWrapperCssObj.top = isTop ? tapTargetTop + 'px' : '';
|
||||
tapTargetWrapperCssObj.right = isRight
|
||||
? windowWidth - tapTargetLeft - tapTargetWidth + 'px'
|
||||
: '';
|
||||
tapTargetWrapperCssObj.bottom = isBottom
|
||||
? windowHeight - tapTargetTop - tapTargetHeight + 'px'
|
||||
: '';
|
||||
tapTargetWrapperCssObj.left = isLeft ? tapTargetLeft + 'px' : '';
|
||||
tapTargetWrapperCssObj.position = tapTargetPosition;
|
||||
$(this.wrapper).css(tapTargetWrapperCssObj);
|
||||
|
||||
// Setting content
|
||||
$(this.contentEl).css({
|
||||
width: tapTargetTextWidth + 'px',
|
||||
height: tapTargetTextHeight + 'px',
|
||||
top: tapTargetTextTop + 'px',
|
||||
right: tapTargetTextRight + 'px',
|
||||
bottom: tapTargetTextBottom + 'px',
|
||||
left: tapTargetTextLeft + 'px',
|
||||
padding: tapTargetTextPadding + 'px',
|
||||
verticalAlign: tapTargetTextAlign
|
||||
});
|
||||
|
||||
// Setting wave
|
||||
$(this.waveEl).css({
|
||||
top: tapTargetWaveTop + 'px',
|
||||
left: tapTargetWaveLeft + 'px',
|
||||
width: tapTargetWaveWidth + 'px',
|
||||
height: tapTargetWaveHeight + 'px'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Open TapTarget
|
||||
*/
|
||||
open() {
|
||||
if (this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
// onOpen callback
|
||||
if (typeof this.options.onOpen === 'function') {
|
||||
this.options.onOpen.call(this, this.$origin[0]);
|
||||
}
|
||||
|
||||
this.isOpen = true;
|
||||
this.wrapper.classList.add('open');
|
||||
|
||||
document.body.addEventListener('click', this._handleDocumentClickBound, true);
|
||||
document.body.addEventListener('touchend', this._handleDocumentClickBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close Tap Target
|
||||
*/
|
||||
close() {
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
// onClose callback
|
||||
if (typeof this.options.onClose === 'function') {
|
||||
this.options.onClose.call(this, this.$origin[0]);
|
||||
}
|
||||
|
||||
this.isOpen = false;
|
||||
this.wrapper.classList.remove('open');
|
||||
|
||||
document.body.removeEventListener('click', this._handleDocumentClickBound, true);
|
||||
document.body.removeEventListener('touchend', this._handleDocumentClickBound);
|
||||
}
|
||||
}
|
||||
|
||||
M.TapTarget = TapTarget;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(TapTarget, 'tapTarget', 'M_TapTarget');
|
||||
}
|
||||
})(cash);
|
||||
|
|
@ -0,0 +1,647 @@
|
|||
(function($) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
dialRadius: 135,
|
||||
outerRadius: 105,
|
||||
innerRadius: 70,
|
||||
tickRadius: 20,
|
||||
duration: 350,
|
||||
container: null,
|
||||
defaultTime: 'now', // default time, 'now' or '13:14' e.g.
|
||||
fromNow: 0, // Millisecond offset from the defaultTime
|
||||
showClearBtn: false,
|
||||
|
||||
// internationalization
|
||||
i18n: {
|
||||
cancel: 'Cancel',
|
||||
clear: 'Clear',
|
||||
done: 'Ok'
|
||||
},
|
||||
|
||||
autoClose: false, // auto close when minute is selected
|
||||
twelveHour: true, // change to 12 hour AM/PM clock from 24 hour
|
||||
vibrate: true, // vibrate the device when dragging clock hand
|
||||
|
||||
// Callbacks
|
||||
onOpenStart: null,
|
||||
onOpenEnd: null,
|
||||
onCloseStart: null,
|
||||
onCloseEnd: null,
|
||||
onSelect: null
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class Timepicker extends Component {
|
||||
constructor(el, options) {
|
||||
super(Timepicker, el, options);
|
||||
|
||||
this.el.M_Timepicker = this;
|
||||
|
||||
this.options = $.extend({}, Timepicker.defaults, options);
|
||||
|
||||
this.id = M.guid();
|
||||
this._insertHTMLIntoDOM();
|
||||
this._setupModal();
|
||||
this._setupVariables();
|
||||
this._setupEventHandlers();
|
||||
|
||||
this._clockSetup();
|
||||
this._pickerSetup();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
static _addLeadingZero(num) {
|
||||
return (num < 10 ? '0' : '') + num;
|
||||
}
|
||||
|
||||
static _createSVGEl(name) {
|
||||
let svgNS = 'http://www.w3.org/2000/svg';
|
||||
return document.createElementNS(svgNS, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} Point
|
||||
* @property {number} x The X Coordinate
|
||||
* @property {number} y The Y Coordinate
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get x position of mouse or touch event
|
||||
* @param {Event} e
|
||||
* @return {Point} x and y location
|
||||
*/
|
||||
static _Pos(e) {
|
||||
if (e.targetTouches && e.targetTouches.length >= 1) {
|
||||
return { x: e.targetTouches[0].clientX, y: e.targetTouches[0].clientY };
|
||||
}
|
||||
// mouse event
|
||||
return { x: e.clientX, y: e.clientY };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Timepicker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this.modal.destroy();
|
||||
$(this.modalEl).remove();
|
||||
this.el.M_Timepicker = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Event Handlers
|
||||
*/
|
||||
_setupEventHandlers() {
|
||||
this._handleInputKeydownBound = this._handleInputKeydown.bind(this);
|
||||
this._handleInputClickBound = this._handleInputClick.bind(this);
|
||||
this._handleClockClickStartBound = this._handleClockClickStart.bind(this);
|
||||
this._handleDocumentClickMoveBound = this._handleDocumentClickMove.bind(this);
|
||||
this._handleDocumentClickEndBound = this._handleDocumentClickEnd.bind(this);
|
||||
|
||||
this.el.addEventListener('click', this._handleInputClickBound);
|
||||
this.el.addEventListener('keydown', this._handleInputKeydownBound);
|
||||
this.plate.addEventListener('mousedown', this._handleClockClickStartBound);
|
||||
this.plate.addEventListener('touchstart', this._handleClockClickStartBound);
|
||||
|
||||
$(this.spanHours).on('click', this.showView.bind(this, 'hours'));
|
||||
$(this.spanMinutes).on('click', this.showView.bind(this, 'minutes'));
|
||||
}
|
||||
|
||||
_removeEventHandlers() {
|
||||
this.el.removeEventListener('click', this._handleInputClickBound);
|
||||
this.el.removeEventListener('keydown', this._handleInputKeydownBound);
|
||||
}
|
||||
|
||||
_handleInputClick() {
|
||||
this.open();
|
||||
}
|
||||
|
||||
_handleInputKeydown(e) {
|
||||
if (e.which === M.keys.ENTER) {
|
||||
e.preventDefault();
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
|
||||
_handleClockClickStart(e) {
|
||||
e.preventDefault();
|
||||
let clockPlateBR = this.plate.getBoundingClientRect();
|
||||
let offset = { x: clockPlateBR.left, y: clockPlateBR.top };
|
||||
|
||||
this.x0 = offset.x + this.options.dialRadius;
|
||||
this.y0 = offset.y + this.options.dialRadius;
|
||||
this.moved = false;
|
||||
let clickPos = Timepicker._Pos(e);
|
||||
this.dx = clickPos.x - this.x0;
|
||||
this.dy = clickPos.y - this.y0;
|
||||
|
||||
// Set clock hands
|
||||
this.setHand(this.dx, this.dy, false);
|
||||
|
||||
// Mousemove on document
|
||||
document.addEventListener('mousemove', this._handleDocumentClickMoveBound);
|
||||
document.addEventListener('touchmove', this._handleDocumentClickMoveBound);
|
||||
|
||||
// Mouseup on document
|
||||
document.addEventListener('mouseup', this._handleDocumentClickEndBound);
|
||||
document.addEventListener('touchend', this._handleDocumentClickEndBound);
|
||||
}
|
||||
|
||||
_handleDocumentClickMove(e) {
|
||||
e.preventDefault();
|
||||
let clickPos = Timepicker._Pos(e);
|
||||
let x = clickPos.x - this.x0;
|
||||
let y = clickPos.y - this.y0;
|
||||
this.moved = true;
|
||||
this.setHand(x, y, false, true);
|
||||
}
|
||||
|
||||
_handleDocumentClickEnd(e) {
|
||||
e.preventDefault();
|
||||
document.removeEventListener('mouseup', this._handleDocumentClickEndBound);
|
||||
document.removeEventListener('touchend', this._handleDocumentClickEndBound);
|
||||
let clickPos = Timepicker._Pos(e);
|
||||
let x = clickPos.x - this.x0;
|
||||
let y = clickPos.y - this.y0;
|
||||
if (this.moved && x === this.dx && y === this.dy) {
|
||||
this.setHand(x, y);
|
||||
}
|
||||
|
||||
if (this.currentView === 'hours') {
|
||||
this.showView('minutes', this.options.duration / 2);
|
||||
} else if (this.options.autoClose) {
|
||||
$(this.minutesView).addClass('timepicker-dial-out');
|
||||
setTimeout(() => {
|
||||
this.done();
|
||||
}, this.options.duration / 2);
|
||||
}
|
||||
|
||||
if (typeof this.options.onSelect === 'function') {
|
||||
this.options.onSelect.call(this, this.hours, this.minutes);
|
||||
}
|
||||
|
||||
// Unbind mousemove event
|
||||
document.removeEventListener('mousemove', this._handleDocumentClickMoveBound);
|
||||
document.removeEventListener('touchmove', this._handleDocumentClickMoveBound);
|
||||
}
|
||||
|
||||
_insertHTMLIntoDOM() {
|
||||
this.$modalEl = $(Timepicker._template);
|
||||
this.modalEl = this.$modalEl[0];
|
||||
this.modalEl.id = 'modal-' + this.id;
|
||||
|
||||
// Append popover to input by default
|
||||
let containerEl = document.querySelector(this.options.container);
|
||||
if (this.options.container && !!containerEl) {
|
||||
this.$modalEl.appendTo(containerEl);
|
||||
} else {
|
||||
this.$modalEl.insertBefore(this.el);
|
||||
}
|
||||
}
|
||||
|
||||
_setupModal() {
|
||||
this.modal = M.Modal.init(this.modalEl, {
|
||||
onOpenStart: this.options.onOpenStart,
|
||||
onOpenEnd: this.options.onOpenEnd,
|
||||
onCloseStart: this.options.onCloseStart,
|
||||
onCloseEnd: () => {
|
||||
if (typeof this.options.onCloseEnd === 'function') {
|
||||
this.options.onCloseEnd.call(this);
|
||||
}
|
||||
this.isOpen = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_setupVariables() {
|
||||
this.currentView = 'hours';
|
||||
this.vibrate = navigator.vibrate
|
||||
? 'vibrate'
|
||||
: navigator.webkitVibrate
|
||||
? 'webkitVibrate'
|
||||
: null;
|
||||
|
||||
this._canvas = this.modalEl.querySelector('.timepicker-canvas');
|
||||
this.plate = this.modalEl.querySelector('.timepicker-plate');
|
||||
|
||||
this.hoursView = this.modalEl.querySelector('.timepicker-hours');
|
||||
this.minutesView = this.modalEl.querySelector('.timepicker-minutes');
|
||||
this.spanHours = this.modalEl.querySelector('.timepicker-span-hours');
|
||||
this.spanMinutes = this.modalEl.querySelector('.timepicker-span-minutes');
|
||||
this.spanAmPm = this.modalEl.querySelector('.timepicker-span-am-pm');
|
||||
this.footer = this.modalEl.querySelector('.timepicker-footer');
|
||||
this.amOrPm = 'PM';
|
||||
}
|
||||
|
||||
_pickerSetup() {
|
||||
let $clearBtn = $(
|
||||
`<button class="btn-flat timepicker-clear waves-effect" style="visibility: hidden;" type="button" tabindex="${
|
||||
this.options.twelveHour ? '3' : '1'
|
||||
}">${this.options.i18n.clear}</button>`
|
||||
)
|
||||
.appendTo(this.footer)
|
||||
.on('click', this.clear.bind(this));
|
||||
if (this.options.showClearBtn) {
|
||||
$clearBtn.css({ visibility: '' });
|
||||
}
|
||||
|
||||
let confirmationBtnsContainer = $('<div class="confirmation-btns"></div>');
|
||||
$(
|
||||
'<button class="btn-flat timepicker-close waves-effect" type="button" tabindex="' +
|
||||
(this.options.twelveHour ? '3' : '1') +
|
||||
'">' +
|
||||
this.options.i18n.cancel +
|
||||
'</button>'
|
||||
)
|
||||
.appendTo(confirmationBtnsContainer)
|
||||
.on('click', this.close.bind(this));
|
||||
$(
|
||||
'<button class="btn-flat timepicker-close waves-effect" type="button" tabindex="' +
|
||||
(this.options.twelveHour ? '3' : '1') +
|
||||
'">' +
|
||||
this.options.i18n.done +
|
||||
'</button>'
|
||||
)
|
||||
.appendTo(confirmationBtnsContainer)
|
||||
.on('click', this.done.bind(this));
|
||||
confirmationBtnsContainer.appendTo(this.footer);
|
||||
}
|
||||
|
||||
_clockSetup() {
|
||||
if (this.options.twelveHour) {
|
||||
this.$amBtn = $('<div class="am-btn">AM</div>');
|
||||
this.$pmBtn = $('<div class="pm-btn">PM</div>');
|
||||
this.$amBtn.on('click', this._handleAmPmClick.bind(this)).appendTo(this.spanAmPm);
|
||||
this.$pmBtn.on('click', this._handleAmPmClick.bind(this)).appendTo(this.spanAmPm);
|
||||
}
|
||||
|
||||
this._buildHoursView();
|
||||
this._buildMinutesView();
|
||||
this._buildSVGClock();
|
||||
}
|
||||
|
||||
_buildSVGClock() {
|
||||
// Draw clock hands and others
|
||||
let dialRadius = this.options.dialRadius;
|
||||
let tickRadius = this.options.tickRadius;
|
||||
let diameter = dialRadius * 2;
|
||||
|
||||
let svg = Timepicker._createSVGEl('svg');
|
||||
svg.setAttribute('class', 'timepicker-svg');
|
||||
svg.setAttribute('width', diameter);
|
||||
svg.setAttribute('height', diameter);
|
||||
let g = Timepicker._createSVGEl('g');
|
||||
g.setAttribute('transform', 'translate(' + dialRadius + ',' + dialRadius + ')');
|
||||
let bearing = Timepicker._createSVGEl('circle');
|
||||
bearing.setAttribute('class', 'timepicker-canvas-bearing');
|
||||
bearing.setAttribute('cx', 0);
|
||||
bearing.setAttribute('cy', 0);
|
||||
bearing.setAttribute('r', 4);
|
||||
let hand = Timepicker._createSVGEl('line');
|
||||
hand.setAttribute('x1', 0);
|
||||
hand.setAttribute('y1', 0);
|
||||
let bg = Timepicker._createSVGEl('circle');
|
||||
bg.setAttribute('class', 'timepicker-canvas-bg');
|
||||
bg.setAttribute('r', tickRadius);
|
||||
g.appendChild(hand);
|
||||
g.appendChild(bg);
|
||||
g.appendChild(bearing);
|
||||
svg.appendChild(g);
|
||||
this._canvas.appendChild(svg);
|
||||
|
||||
this.hand = hand;
|
||||
this.bg = bg;
|
||||
this.bearing = bearing;
|
||||
this.g = g;
|
||||
}
|
||||
|
||||
_buildHoursView() {
|
||||
let $tick = $('<div class="timepicker-tick"></div>');
|
||||
// Hours view
|
||||
if (this.options.twelveHour) {
|
||||
for (let i = 1; i < 13; i += 1) {
|
||||
let tick = $tick.clone();
|
||||
let radian = i / 6 * Math.PI;
|
||||
let radius = this.options.outerRadius;
|
||||
tick.css({
|
||||
left:
|
||||
this.options.dialRadius + Math.sin(radian) * radius - this.options.tickRadius + 'px',
|
||||
top:
|
||||
this.options.dialRadius - Math.cos(radian) * radius - this.options.tickRadius + 'px'
|
||||
});
|
||||
tick.html(i === 0 ? '00' : i);
|
||||
this.hoursView.appendChild(tick[0]);
|
||||
// tick.on(mousedownEvent, mousedown);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < 24; i += 1) {
|
||||
let tick = $tick.clone();
|
||||
let radian = i / 6 * Math.PI;
|
||||
let inner = i > 0 && i < 13;
|
||||
let radius = inner ? this.options.innerRadius : this.options.outerRadius;
|
||||
tick.css({
|
||||
left:
|
||||
this.options.dialRadius + Math.sin(radian) * radius - this.options.tickRadius + 'px',
|
||||
top:
|
||||
this.options.dialRadius - Math.cos(radian) * radius - this.options.tickRadius + 'px'
|
||||
});
|
||||
tick.html(i === 0 ? '00' : i);
|
||||
this.hoursView.appendChild(tick[0]);
|
||||
// tick.on(mousedownEvent, mousedown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_buildMinutesView() {
|
||||
let $tick = $('<div class="timepicker-tick"></div>');
|
||||
// Minutes view
|
||||
for (let i = 0; i < 60; i += 5) {
|
||||
let tick = $tick.clone();
|
||||
let radian = i / 30 * Math.PI;
|
||||
tick.css({
|
||||
left:
|
||||
this.options.dialRadius +
|
||||
Math.sin(radian) * this.options.outerRadius -
|
||||
this.options.tickRadius +
|
||||
'px',
|
||||
top:
|
||||
this.options.dialRadius -
|
||||
Math.cos(radian) * this.options.outerRadius -
|
||||
this.options.tickRadius +
|
||||
'px'
|
||||
});
|
||||
tick.html(Timepicker._addLeadingZero(i));
|
||||
this.minutesView.appendChild(tick[0]);
|
||||
}
|
||||
}
|
||||
|
||||
_handleAmPmClick(e) {
|
||||
let $btnClicked = $(e.target);
|
||||
this.amOrPm = $btnClicked.hasClass('am-btn') ? 'AM' : 'PM';
|
||||
this._updateAmPmView();
|
||||
}
|
||||
|
||||
_updateAmPmView() {
|
||||
if (this.options.twelveHour) {
|
||||
this.$amBtn.toggleClass('text-primary', this.amOrPm === 'AM');
|
||||
this.$pmBtn.toggleClass('text-primary', this.amOrPm === 'PM');
|
||||
}
|
||||
}
|
||||
|
||||
_updateTimeFromInput() {
|
||||
// Get the time
|
||||
let value = ((this.el.value || this.options.defaultTime || '') + '').split(':');
|
||||
if (this.options.twelveHour && !(typeof value[1] === 'undefined')) {
|
||||
if (value[1].toUpperCase().indexOf('AM') > 0) {
|
||||
this.amOrPm = 'AM';
|
||||
} else {
|
||||
this.amOrPm = 'PM';
|
||||
}
|
||||
value[1] = value[1].replace('AM', '').replace('PM', '');
|
||||
}
|
||||
if (value[0] === 'now') {
|
||||
let now = new Date(+new Date() + this.options.fromNow);
|
||||
value = [now.getHours(), now.getMinutes()];
|
||||
if (this.options.twelveHour) {
|
||||
this.amOrPm = value[0] >= 12 && value[0] < 24 ? 'PM' : 'AM';
|
||||
}
|
||||
}
|
||||
this.hours = +value[0] || 0;
|
||||
this.minutes = +value[1] || 0;
|
||||
this.spanHours.innerHTML = this.hours;
|
||||
this.spanMinutes.innerHTML = Timepicker._addLeadingZero(this.minutes);
|
||||
|
||||
this._updateAmPmView();
|
||||
}
|
||||
|
||||
showView(view, delay) {
|
||||
if (view === 'minutes' && $(this.hoursView).css('visibility') === 'visible') {
|
||||
// raiseCallback(this.options.beforeHourSelect);
|
||||
}
|
||||
let isHours = view === 'hours',
|
||||
nextView = isHours ? this.hoursView : this.minutesView,
|
||||
hideView = isHours ? this.minutesView : this.hoursView;
|
||||
this.currentView = view;
|
||||
|
||||
$(this.spanHours).toggleClass('text-primary', isHours);
|
||||
$(this.spanMinutes).toggleClass('text-primary', !isHours);
|
||||
|
||||
// Transition view
|
||||
hideView.classList.add('timepicker-dial-out');
|
||||
$(nextView)
|
||||
.css('visibility', 'visible')
|
||||
.removeClass('timepicker-dial-out');
|
||||
|
||||
// Reset clock hand
|
||||
this.resetClock(delay);
|
||||
|
||||
// After transitions ended
|
||||
clearTimeout(this.toggleViewTimer);
|
||||
this.toggleViewTimer = setTimeout(() => {
|
||||
$(hideView).css('visibility', 'hidden');
|
||||
}, this.options.duration);
|
||||
}
|
||||
|
||||
resetClock(delay) {
|
||||
let view = this.currentView,
|
||||
value = this[view],
|
||||
isHours = view === 'hours',
|
||||
unit = Math.PI / (isHours ? 6 : 30),
|
||||
radian = value * unit,
|
||||
radius =
|
||||
isHours && value > 0 && value < 13 ? this.options.innerRadius : this.options.outerRadius,
|
||||
x = Math.sin(radian) * radius,
|
||||
y = -Math.cos(radian) * radius,
|
||||
self = this;
|
||||
|
||||
if (delay) {
|
||||
$(this.canvas).addClass('timepicker-canvas-out');
|
||||
setTimeout(() => {
|
||||
$(self.canvas).removeClass('timepicker-canvas-out');
|
||||
self.setHand(x, y);
|
||||
}, delay);
|
||||
} else {
|
||||
this.setHand(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
setHand(x, y, roundBy5) {
|
||||
let radian = Math.atan2(x, -y),
|
||||
isHours = this.currentView === 'hours',
|
||||
unit = Math.PI / (isHours || roundBy5 ? 6 : 30),
|
||||
z = Math.sqrt(x * x + y * y),
|
||||
inner = isHours && z < (this.options.outerRadius + this.options.innerRadius) / 2,
|
||||
radius = inner ? this.options.innerRadius : this.options.outerRadius;
|
||||
|
||||
if (this.options.twelveHour) {
|
||||
radius = this.options.outerRadius;
|
||||
}
|
||||
|
||||
// Radian should in range [0, 2PI]
|
||||
if (radian < 0) {
|
||||
radian = Math.PI * 2 + radian;
|
||||
}
|
||||
|
||||
// Get the round value
|
||||
let value = Math.round(radian / unit);
|
||||
|
||||
// Get the round radian
|
||||
radian = value * unit;
|
||||
|
||||
// Correct the hours or minutes
|
||||
if (this.options.twelveHour) {
|
||||
if (isHours) {
|
||||
if (value === 0) value = 12;
|
||||
} else {
|
||||
if (roundBy5) value *= 5;
|
||||
if (value === 60) value = 0;
|
||||
}
|
||||
} else {
|
||||
if (isHours) {
|
||||
if (value === 12) {
|
||||
value = 0;
|
||||
}
|
||||
value = inner ? (value === 0 ? 12 : value) : value === 0 ? 0 : value + 12;
|
||||
} else {
|
||||
if (roundBy5) {
|
||||
value *= 5;
|
||||
}
|
||||
if (value === 60) {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Once hours or minutes changed, vibrate the device
|
||||
if (this[this.currentView] !== value) {
|
||||
if (this.vibrate && this.options.vibrate) {
|
||||
// Do not vibrate too frequently
|
||||
if (!this.vibrateTimer) {
|
||||
navigator[this.vibrate](10);
|
||||
this.vibrateTimer = setTimeout(() => {
|
||||
this.vibrateTimer = null;
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this[this.currentView] = value;
|
||||
if (isHours) {
|
||||
this['spanHours'].innerHTML = value;
|
||||
} else {
|
||||
this['spanMinutes'].innerHTML = Timepicker._addLeadingZero(value);
|
||||
}
|
||||
|
||||
// Set clock hand and others' position
|
||||
let cx1 = Math.sin(radian) * (radius - this.options.tickRadius),
|
||||
cy1 = -Math.cos(radian) * (radius - this.options.tickRadius),
|
||||
cx2 = Math.sin(radian) * radius,
|
||||
cy2 = -Math.cos(radian) * radius;
|
||||
this.hand.setAttribute('x2', cx1);
|
||||
this.hand.setAttribute('y2', cy1);
|
||||
this.bg.setAttribute('cx', cx2);
|
||||
this.bg.setAttribute('cy', cy2);
|
||||
}
|
||||
|
||||
open() {
|
||||
if (this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isOpen = true;
|
||||
this._updateTimeFromInput();
|
||||
this.showView('hours');
|
||||
|
||||
this.modal.open();
|
||||
}
|
||||
|
||||
close() {
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isOpen = false;
|
||||
this.modal.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish timepicker selection.
|
||||
*/
|
||||
done(e, clearValue) {
|
||||
// Set input value
|
||||
let last = this.el.value;
|
||||
let value = clearValue
|
||||
? ''
|
||||
: Timepicker._addLeadingZero(this.hours) + ':' + Timepicker._addLeadingZero(this.minutes);
|
||||
this.time = value;
|
||||
if (!clearValue && this.options.twelveHour) {
|
||||
value = `${value} ${this.amOrPm}`;
|
||||
}
|
||||
this.el.value = value;
|
||||
|
||||
// Trigger change event
|
||||
if (value !== last) {
|
||||
this.$el.trigger('change');
|
||||
}
|
||||
|
||||
this.close();
|
||||
this.el.focus();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.done(null, true);
|
||||
}
|
||||
}
|
||||
|
||||
Timepicker._template = [
|
||||
'<div class= "modal timepicker-modal">',
|
||||
'<div class="modal-content timepicker-container">',
|
||||
'<div class="timepicker-digital-display">',
|
||||
'<div class="timepicker-text-container">',
|
||||
'<div class="timepicker-display-column">',
|
||||
'<span class="timepicker-span-hours text-primary"></span>',
|
||||
':',
|
||||
'<span class="timepicker-span-minutes"></span>',
|
||||
'</div>',
|
||||
'<div class="timepicker-display-column timepicker-display-am-pm">',
|
||||
'<div class="timepicker-span-am-pm"></div>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
'<div class="timepicker-analog-display">',
|
||||
'<div class="timepicker-plate">',
|
||||
'<div class="timepicker-canvas"></div>',
|
||||
'<div class="timepicker-dial timepicker-hours"></div>',
|
||||
'<div class="timepicker-dial timepicker-minutes timepicker-dial-out"></div>',
|
||||
'</div>',
|
||||
'<div class="timepicker-footer"></div>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join('');
|
||||
|
||||
M.Timepicker = Timepicker;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Timepicker, 'timepicker', 'M_Timepicker');
|
||||
}
|
||||
})(cash);
|
||||
|
|
@ -0,0 +1,310 @@
|
|||
(function($, anim) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
html: '',
|
||||
displayLength: 4000,
|
||||
inDuration: 300,
|
||||
outDuration: 375,
|
||||
classes: '',
|
||||
completeCallback: null,
|
||||
activationPercent: 0.8
|
||||
};
|
||||
|
||||
class Toast {
|
||||
constructor(options) {
|
||||
/**
|
||||
* Options for the toast
|
||||
* @member Toast#options
|
||||
*/
|
||||
this.options = $.extend({}, Toast.defaults, options);
|
||||
this.message = this.options.html;
|
||||
|
||||
/**
|
||||
* Describes current pan state toast
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.panning = false;
|
||||
|
||||
/**
|
||||
* Time remaining until toast is removed
|
||||
*/
|
||||
this.timeRemaining = this.options.displayLength;
|
||||
|
||||
if (Toast._toasts.length === 0) {
|
||||
Toast._createContainer();
|
||||
}
|
||||
|
||||
// Create new toast
|
||||
Toast._toasts.push(this);
|
||||
let toastElement = this._createToast();
|
||||
toastElement.M_Toast = this;
|
||||
this.el = toastElement;
|
||||
this.$el = $(toastElement);
|
||||
this._animateIn();
|
||||
this._setTimer();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Toast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append toast container and add event handlers
|
||||
*/
|
||||
static _createContainer() {
|
||||
let container = document.createElement('div');
|
||||
container.setAttribute('id', 'toast-container');
|
||||
|
||||
// Add event handler
|
||||
container.addEventListener('touchstart', Toast._onDragStart);
|
||||
container.addEventListener('touchmove', Toast._onDragMove);
|
||||
container.addEventListener('touchend', Toast._onDragEnd);
|
||||
|
||||
container.addEventListener('mousedown', Toast._onDragStart);
|
||||
document.addEventListener('mousemove', Toast._onDragMove);
|
||||
document.addEventListener('mouseup', Toast._onDragEnd);
|
||||
|
||||
document.body.appendChild(container);
|
||||
Toast._container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove toast container and event handlers
|
||||
*/
|
||||
static _removeContainer() {
|
||||
// Add event handler
|
||||
document.removeEventListener('mousemove', Toast._onDragMove);
|
||||
document.removeEventListener('mouseup', Toast._onDragEnd);
|
||||
|
||||
$(Toast._container).remove();
|
||||
Toast._container = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin drag handler
|
||||
* @param {Event} e
|
||||
*/
|
||||
static _onDragStart(e) {
|
||||
if (e.target && $(e.target).closest('.toast').length) {
|
||||
let $toast = $(e.target).closest('.toast');
|
||||
let toast = $toast[0].M_Toast;
|
||||
toast.panning = true;
|
||||
Toast._draggedToast = toast;
|
||||
toast.el.classList.add('panning');
|
||||
toast.el.style.transition = '';
|
||||
toast.startingXPos = Toast._xPos(e);
|
||||
toast.time = Date.now();
|
||||
toast.xPos = Toast._xPos(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag move handler
|
||||
* @param {Event} e
|
||||
*/
|
||||
static _onDragMove(e) {
|
||||
if (!!Toast._draggedToast) {
|
||||
e.preventDefault();
|
||||
let toast = Toast._draggedToast;
|
||||
toast.deltaX = Math.abs(toast.xPos - Toast._xPos(e));
|
||||
toast.xPos = Toast._xPos(e);
|
||||
toast.velocityX = toast.deltaX / (Date.now() - toast.time);
|
||||
toast.time = Date.now();
|
||||
|
||||
let totalDeltaX = toast.xPos - toast.startingXPos;
|
||||
let activationDistance = toast.el.offsetWidth * toast.options.activationPercent;
|
||||
toast.el.style.transform = `translateX(${totalDeltaX}px)`;
|
||||
toast.el.style.opacity = 1 - Math.abs(totalDeltaX / activationDistance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End drag handler
|
||||
*/
|
||||
static _onDragEnd() {
|
||||
if (!!Toast._draggedToast) {
|
||||
let toast = Toast._draggedToast;
|
||||
toast.panning = false;
|
||||
toast.el.classList.remove('panning');
|
||||
|
||||
let totalDeltaX = toast.xPos - toast.startingXPos;
|
||||
let activationDistance = toast.el.offsetWidth * toast.options.activationPercent;
|
||||
let shouldBeDismissed = Math.abs(totalDeltaX) > activationDistance || toast.velocityX > 1;
|
||||
|
||||
// Remove toast
|
||||
if (shouldBeDismissed) {
|
||||
toast.wasSwiped = true;
|
||||
toast.dismiss();
|
||||
|
||||
// Animate toast back to original position
|
||||
} else {
|
||||
toast.el.style.transition = 'transform .2s, opacity .2s';
|
||||
toast.el.style.transform = '';
|
||||
toast.el.style.opacity = '';
|
||||
}
|
||||
Toast._draggedToast = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get x position of mouse or touch event
|
||||
* @param {Event} e
|
||||
*/
|
||||
static _xPos(e) {
|
||||
if (e.targetTouches && e.targetTouches.length >= 1) {
|
||||
return e.targetTouches[0].clientX;
|
||||
}
|
||||
// mouse event
|
||||
return e.clientX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all toasts
|
||||
*/
|
||||
static dismissAll() {
|
||||
for (let toastIndex in Toast._toasts) {
|
||||
Toast._toasts[toastIndex].dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create toast and append it to toast container
|
||||
*/
|
||||
_createToast() {
|
||||
let toast = document.createElement('div');
|
||||
toast.classList.add('toast');
|
||||
|
||||
// Add custom classes onto toast
|
||||
if (!!this.options.classes.length) {
|
||||
$(toast).addClass(this.options.classes);
|
||||
}
|
||||
|
||||
// Set content
|
||||
if (
|
||||
typeof HTMLElement === 'object'
|
||||
? this.message instanceof HTMLElement
|
||||
: this.message &&
|
||||
typeof this.message === 'object' &&
|
||||
this.message !== null &&
|
||||
this.message.nodeType === 1 &&
|
||||
typeof this.message.nodeName === 'string'
|
||||
) {
|
||||
toast.appendChild(this.message);
|
||||
|
||||
// Check if it is jQuery object
|
||||
} else if (!!this.message.jquery) {
|
||||
$(toast).append(this.message[0]);
|
||||
|
||||
// Insert as html;
|
||||
} else {
|
||||
toast.innerHTML = this.message;
|
||||
}
|
||||
|
||||
// Append toasft
|
||||
Toast._container.appendChild(toast);
|
||||
return toast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate in toast
|
||||
*/
|
||||
_animateIn() {
|
||||
// Animate toast in
|
||||
anim({
|
||||
targets: this.el,
|
||||
top: 0,
|
||||
opacity: 1,
|
||||
duration: this.options.inDuration,
|
||||
easing: 'easeOutCubic'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create setInterval which automatically removes toast when timeRemaining >= 0
|
||||
* has been reached
|
||||
*/
|
||||
_setTimer() {
|
||||
if (this.timeRemaining !== Infinity) {
|
||||
this.counterInterval = setInterval(() => {
|
||||
// If toast is not being dragged, decrease its time remaining
|
||||
if (!this.panning) {
|
||||
this.timeRemaining -= 20;
|
||||
}
|
||||
|
||||
// Animate toast out
|
||||
if (this.timeRemaining <= 0) {
|
||||
this.dismiss();
|
||||
}
|
||||
}, 20);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss toast with animation
|
||||
*/
|
||||
dismiss() {
|
||||
window.clearInterval(this.counterInterval);
|
||||
let activationDistance = this.el.offsetWidth * this.options.activationPercent;
|
||||
|
||||
if (this.wasSwiped) {
|
||||
this.el.style.transition = 'transform .05s, opacity .05s';
|
||||
this.el.style.transform = `translateX(${activationDistance}px)`;
|
||||
this.el.style.opacity = 0;
|
||||
}
|
||||
|
||||
anim({
|
||||
targets: this.el,
|
||||
opacity: 0,
|
||||
marginTop: -40,
|
||||
duration: this.options.outDuration,
|
||||
easing: 'easeOutExpo',
|
||||
complete: () => {
|
||||
// Call the optional callback
|
||||
if (typeof this.options.completeCallback === 'function') {
|
||||
this.options.completeCallback();
|
||||
}
|
||||
// Remove toast from DOM
|
||||
this.$el.remove();
|
||||
Toast._toasts.splice(Toast._toasts.indexOf(this), 1);
|
||||
if (Toast._toasts.length === 0) {
|
||||
Toast._removeContainer();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof Toast
|
||||
* @type {Array.<Toast>}
|
||||
*/
|
||||
Toast._toasts = [];
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof Toast
|
||||
*/
|
||||
Toast._container = null;
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof Toast
|
||||
* @type {Toast}
|
||||
*/
|
||||
Toast._draggedToast = null;
|
||||
|
||||
M.Toast = Toast;
|
||||
M.toast = function(options) {
|
||||
return new Toast(options);
|
||||
};
|
||||
})(cash, M.anime);
|
||||
|
|
@ -0,0 +1,303 @@
|
|||
(function($, anim) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
exitDelay: 200,
|
||||
enterDelay: 0,
|
||||
html: null,
|
||||
margin: 5,
|
||||
inDuration: 250,
|
||||
outDuration: 200,
|
||||
position: 'bottom',
|
||||
transitionMovement: 10
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
*/
|
||||
class Tooltip extends Component {
|
||||
/**
|
||||
* Construct Tooltip instance
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Tooltip, el, options);
|
||||
|
||||
this.el.M_Tooltip = this;
|
||||
this.options = $.extend({}, Tooltip.defaults, options);
|
||||
|
||||
this.isOpen = false;
|
||||
this.isHovered = false;
|
||||
this.isFocused = false;
|
||||
this._appendTooltipEl();
|
||||
this._setupEventHandlers();
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Tooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
$(this.tooltipEl).remove();
|
||||
this._removeEventHandlers();
|
||||
this.el.M_Tooltip = undefined;
|
||||
}
|
||||
|
||||
_appendTooltipEl() {
|
||||
let tooltipEl = document.createElement('div');
|
||||
tooltipEl.classList.add('material-tooltip');
|
||||
this.tooltipEl = tooltipEl;
|
||||
|
||||
let tooltipContentEl = document.createElement('div');
|
||||
tooltipContentEl.classList.add('tooltip-content');
|
||||
tooltipContentEl.innerHTML = this.options.html;
|
||||
tooltipEl.appendChild(tooltipContentEl);
|
||||
document.body.appendChild(tooltipEl);
|
||||
}
|
||||
|
||||
_updateTooltipContent() {
|
||||
this.tooltipEl.querySelector('.tooltip-content').innerHTML = this.options.html;
|
||||
}
|
||||
|
||||
_setupEventHandlers() {
|
||||
this._handleMouseEnterBound = this._handleMouseEnter.bind(this);
|
||||
this._handleMouseLeaveBound = this._handleMouseLeave.bind(this);
|
||||
this._handleFocusBound = this._handleFocus.bind(this);
|
||||
this._handleBlurBound = this._handleBlur.bind(this);
|
||||
this.el.addEventListener('mouseenter', this._handleMouseEnterBound);
|
||||
this.el.addEventListener('mouseleave', this._handleMouseLeaveBound);
|
||||
this.el.addEventListener('focus', this._handleFocusBound, true);
|
||||
this.el.addEventListener('blur', this._handleBlurBound, true);
|
||||
}
|
||||
|
||||
_removeEventHandlers() {
|
||||
this.el.removeEventListener('mouseenter', this._handleMouseEnterBound);
|
||||
this.el.removeEventListener('mouseleave', this._handleMouseLeaveBound);
|
||||
this.el.removeEventListener('focus', this._handleFocusBound, true);
|
||||
this.el.removeEventListener('blur', this._handleBlurBound, true);
|
||||
}
|
||||
|
||||
open(isManual) {
|
||||
if (this.isOpen) {
|
||||
return;
|
||||
}
|
||||
isManual = isManual === undefined ? true : undefined; // Default value true
|
||||
this.isOpen = true;
|
||||
// Update tooltip content with HTML attribute options
|
||||
this.options = $.extend({}, this.options, this._getAttributeOptions());
|
||||
this._updateTooltipContent();
|
||||
this._setEnterDelayTimeout(isManual);
|
||||
}
|
||||
|
||||
close() {
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isHovered = false;
|
||||
this.isFocused = false;
|
||||
this.isOpen = false;
|
||||
this._setExitDelayTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create timeout which delays when the tooltip closes
|
||||
*/
|
||||
_setExitDelayTimeout() {
|
||||
clearTimeout(this._exitDelayTimeout);
|
||||
|
||||
this._exitDelayTimeout = setTimeout(() => {
|
||||
if (this.isHovered || this.isFocused) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._animateOut();
|
||||
}, this.options.exitDelay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create timeout which delays when the toast closes
|
||||
*/
|
||||
_setEnterDelayTimeout(isManual) {
|
||||
clearTimeout(this._enterDelayTimeout);
|
||||
|
||||
this._enterDelayTimeout = setTimeout(() => {
|
||||
if (!this.isHovered && !this.isFocused && !isManual) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._animateIn();
|
||||
}, this.options.enterDelay);
|
||||
}
|
||||
|
||||
_positionTooltip() {
|
||||
let origin = this.el,
|
||||
tooltip = this.tooltipEl,
|
||||
originHeight = origin.offsetHeight,
|
||||
originWidth = origin.offsetWidth,
|
||||
tooltipHeight = tooltip.offsetHeight,
|
||||
tooltipWidth = tooltip.offsetWidth,
|
||||
newCoordinates,
|
||||
margin = this.options.margin,
|
||||
targetTop,
|
||||
targetLeft;
|
||||
|
||||
(this.xMovement = 0), (this.yMovement = 0);
|
||||
|
||||
targetTop = origin.getBoundingClientRect().top + M.getDocumentScrollTop();
|
||||
targetLeft = origin.getBoundingClientRect().left + M.getDocumentScrollLeft();
|
||||
|
||||
if (this.options.position === 'top') {
|
||||
targetTop += -tooltipHeight - margin;
|
||||
targetLeft += originWidth / 2 - tooltipWidth / 2;
|
||||
this.yMovement = -this.options.transitionMovement;
|
||||
} else if (this.options.position === 'right') {
|
||||
targetTop += originHeight / 2 - tooltipHeight / 2;
|
||||
targetLeft += originWidth + margin;
|
||||
this.xMovement = this.options.transitionMovement;
|
||||
} else if (this.options.position === 'left') {
|
||||
targetTop += originHeight / 2 - tooltipHeight / 2;
|
||||
targetLeft += -tooltipWidth - margin;
|
||||
this.xMovement = -this.options.transitionMovement;
|
||||
} else {
|
||||
targetTop += originHeight + margin;
|
||||
targetLeft += originWidth / 2 - tooltipWidth / 2;
|
||||
this.yMovement = this.options.transitionMovement;
|
||||
}
|
||||
|
||||
newCoordinates = this._repositionWithinScreen(
|
||||
targetLeft,
|
||||
targetTop,
|
||||
tooltipWidth,
|
||||
tooltipHeight
|
||||
);
|
||||
$(tooltip).css({
|
||||
top: newCoordinates.y + 'px',
|
||||
left: newCoordinates.x + 'px'
|
||||
});
|
||||
}
|
||||
|
||||
_repositionWithinScreen(x, y, width, height) {
|
||||
let scrollLeft = M.getDocumentScrollLeft();
|
||||
let scrollTop = M.getDocumentScrollTop();
|
||||
let newX = x - scrollLeft;
|
||||
let newY = y - scrollTop;
|
||||
|
||||
let bounding = {
|
||||
left: newX,
|
||||
top: newY,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
|
||||
let offset = this.options.margin + this.options.transitionMovement;
|
||||
let edges = M.checkWithinContainer(document.body, bounding, offset);
|
||||
|
||||
if (edges.left) {
|
||||
newX = offset;
|
||||
} else if (edges.right) {
|
||||
newX -= newX + width - window.innerWidth;
|
||||
}
|
||||
|
||||
if (edges.top) {
|
||||
newY = offset;
|
||||
} else if (edges.bottom) {
|
||||
newY -= newY + height - window.innerHeight;
|
||||
}
|
||||
|
||||
return {
|
||||
x: newX + scrollLeft,
|
||||
y: newY + scrollTop
|
||||
};
|
||||
}
|
||||
|
||||
_animateIn() {
|
||||
this._positionTooltip();
|
||||
this.tooltipEl.style.visibility = 'visible';
|
||||
anim.remove(this.tooltipEl);
|
||||
anim({
|
||||
targets: this.tooltipEl,
|
||||
opacity: 1,
|
||||
translateX: this.xMovement,
|
||||
translateY: this.yMovement,
|
||||
duration: this.options.inDuration,
|
||||
easing: 'easeOutCubic'
|
||||
});
|
||||
}
|
||||
|
||||
_animateOut() {
|
||||
anim.remove(this.tooltipEl);
|
||||
anim({
|
||||
targets: this.tooltipEl,
|
||||
opacity: 0,
|
||||
translateX: 0,
|
||||
translateY: 0,
|
||||
duration: this.options.outDuration,
|
||||
easing: 'easeOutCubic'
|
||||
});
|
||||
}
|
||||
|
||||
_handleMouseEnter() {
|
||||
this.isHovered = true;
|
||||
this.isFocused = false; // Allows close of tooltip when opened by focus.
|
||||
this.open(false);
|
||||
}
|
||||
|
||||
_handleMouseLeave() {
|
||||
this.isHovered = false;
|
||||
this.isFocused = false; // Allows close of tooltip when opened by focus.
|
||||
this.close();
|
||||
}
|
||||
|
||||
_handleFocus() {
|
||||
if (M.tabPressed) {
|
||||
this.isFocused = true;
|
||||
this.open(false);
|
||||
}
|
||||
}
|
||||
|
||||
_handleBlur() {
|
||||
this.isFocused = false;
|
||||
this.close();
|
||||
}
|
||||
|
||||
_getAttributeOptions() {
|
||||
let attributeOptions = {};
|
||||
let tooltipTextOption = this.el.getAttribute('data-tooltip');
|
||||
let positionOption = this.el.getAttribute('data-position');
|
||||
|
||||
if (tooltipTextOption) {
|
||||
attributeOptions.html = tooltipTextOption;
|
||||
}
|
||||
|
||||
if (positionOption) {
|
||||
attributeOptions.position = positionOption;
|
||||
}
|
||||
return attributeOptions;
|
||||
}
|
||||
}
|
||||
|
||||
M.Tooltip = Tooltip;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Tooltip, 'tooltip', 'M_Tooltip');
|
||||
}
|
||||
})(cash, M.anime);
|
||||
|
|
@ -0,0 +1,335 @@
|
|||
/*!
|
||||
* Waves v0.6.4
|
||||
* http://fian.my.id/Waves
|
||||
*
|
||||
* Copyright 2014 Alfiana E. Sibuea and other contributors
|
||||
* Released under the MIT license
|
||||
* https://github.com/fians/Waves/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
;(function(window) {
|
||||
'use strict';
|
||||
|
||||
var Waves = Waves || {};
|
||||
var $$ = document.querySelectorAll.bind(document);
|
||||
|
||||
// Find exact position of element
|
||||
function isWindow(obj) {
|
||||
return obj !== null && obj === obj.window;
|
||||
}
|
||||
|
||||
function getWindow(elem) {
|
||||
return isWindow(elem) ? elem : elem.nodeType === 9 && elem.defaultView;
|
||||
}
|
||||
|
||||
function offset(elem) {
|
||||
var docElem, win,
|
||||
box = {top: 0, left: 0},
|
||||
doc = elem && elem.ownerDocument;
|
||||
|
||||
docElem = doc.documentElement;
|
||||
|
||||
if (typeof elem.getBoundingClientRect !== typeof undefined) {
|
||||
box = elem.getBoundingClientRect();
|
||||
}
|
||||
win = getWindow(doc);
|
||||
return {
|
||||
top: box.top + win.pageYOffset - docElem.clientTop,
|
||||
left: box.left + win.pageXOffset - docElem.clientLeft
|
||||
};
|
||||
}
|
||||
|
||||
function convertStyle(obj) {
|
||||
var style = '';
|
||||
|
||||
for (var a in obj) {
|
||||
if (obj.hasOwnProperty(a)) {
|
||||
style += (a + ':' + obj[a] + ';');
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
var Effect = {
|
||||
|
||||
// Effect delay
|
||||
duration: 750,
|
||||
|
||||
show: function(e, element) {
|
||||
|
||||
// Disable right click
|
||||
if (e.button === 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var el = element || this;
|
||||
|
||||
// Create ripple
|
||||
var ripple = document.createElement('div');
|
||||
ripple.className = 'waves-ripple';
|
||||
el.appendChild(ripple);
|
||||
|
||||
// Get click coordinate and element witdh
|
||||
var pos = offset(el);
|
||||
var relativeY = (e.pageY - pos.top);
|
||||
var relativeX = (e.pageX - pos.left);
|
||||
var scale = 'scale('+((el.clientWidth / 100) * 10)+')';
|
||||
|
||||
// Support for touch devices
|
||||
if ('touches' in e) {
|
||||
relativeY = (e.touches[0].pageY - pos.top);
|
||||
relativeX = (e.touches[0].pageX - pos.left);
|
||||
}
|
||||
|
||||
// Attach data to element
|
||||
ripple.setAttribute('data-hold', Date.now());
|
||||
ripple.setAttribute('data-scale', scale);
|
||||
ripple.setAttribute('data-x', relativeX);
|
||||
ripple.setAttribute('data-y', relativeY);
|
||||
|
||||
// Set ripple position
|
||||
var rippleStyle = {
|
||||
'top': relativeY+'px',
|
||||
'left': relativeX+'px'
|
||||
};
|
||||
|
||||
ripple.className = ripple.className + ' waves-notransition';
|
||||
ripple.setAttribute('style', convertStyle(rippleStyle));
|
||||
ripple.className = ripple.className.replace('waves-notransition', '');
|
||||
|
||||
// Scale the ripple
|
||||
rippleStyle['-webkit-transform'] = scale;
|
||||
rippleStyle['-moz-transform'] = scale;
|
||||
rippleStyle['-ms-transform'] = scale;
|
||||
rippleStyle['-o-transform'] = scale;
|
||||
rippleStyle.transform = scale;
|
||||
rippleStyle.opacity = '1';
|
||||
|
||||
rippleStyle['-webkit-transition-duration'] = Effect.duration + 'ms';
|
||||
rippleStyle['-moz-transition-duration'] = Effect.duration + 'ms';
|
||||
rippleStyle['-o-transition-duration'] = Effect.duration + 'ms';
|
||||
rippleStyle['transition-duration'] = Effect.duration + 'ms';
|
||||
|
||||
rippleStyle['-webkit-transition-timing-function'] = 'cubic-bezier(0.250, 0.460, 0.450, 0.940)';
|
||||
rippleStyle['-moz-transition-timing-function'] = 'cubic-bezier(0.250, 0.460, 0.450, 0.940)';
|
||||
rippleStyle['-o-transition-timing-function'] = 'cubic-bezier(0.250, 0.460, 0.450, 0.940)';
|
||||
rippleStyle['transition-timing-function'] = 'cubic-bezier(0.250, 0.460, 0.450, 0.940)';
|
||||
|
||||
ripple.setAttribute('style', convertStyle(rippleStyle));
|
||||
},
|
||||
|
||||
hide: function(e) {
|
||||
TouchHandler.touchup(e);
|
||||
|
||||
var el = this;
|
||||
var width = el.clientWidth * 1.4;
|
||||
|
||||
// Get first ripple
|
||||
var ripple = null;
|
||||
var ripples = el.getElementsByClassName('waves-ripple');
|
||||
if (ripples.length > 0) {
|
||||
ripple = ripples[ripples.length - 1];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
var relativeX = ripple.getAttribute('data-x');
|
||||
var relativeY = ripple.getAttribute('data-y');
|
||||
var scale = ripple.getAttribute('data-scale');
|
||||
|
||||
// Get delay beetween mousedown and mouse leave
|
||||
var diff = Date.now() - Number(ripple.getAttribute('data-hold'));
|
||||
var delay = 350 - diff;
|
||||
|
||||
if (delay < 0) {
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
// Fade out ripple after delay
|
||||
setTimeout(function() {
|
||||
var style = {
|
||||
'top': relativeY+'px',
|
||||
'left': relativeX+'px',
|
||||
'opacity': '0',
|
||||
|
||||
// Duration
|
||||
'-webkit-transition-duration': Effect.duration + 'ms',
|
||||
'-moz-transition-duration': Effect.duration + 'ms',
|
||||
'-o-transition-duration': Effect.duration + 'ms',
|
||||
'transition-duration': Effect.duration + 'ms',
|
||||
'-webkit-transform': scale,
|
||||
'-moz-transform': scale,
|
||||
'-ms-transform': scale,
|
||||
'-o-transform': scale,
|
||||
'transform': scale,
|
||||
};
|
||||
|
||||
ripple.setAttribute('style', convertStyle(style));
|
||||
|
||||
setTimeout(function() {
|
||||
try {
|
||||
el.removeChild(ripple);
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}, Effect.duration);
|
||||
}, delay);
|
||||
},
|
||||
|
||||
// Little hack to make <input> can perform waves effect
|
||||
wrapInput: function(elements) {
|
||||
for (var a = 0; a < elements.length; a++) {
|
||||
var el = elements[a];
|
||||
|
||||
if (el.tagName.toLowerCase() === 'input') {
|
||||
var parent = el.parentNode;
|
||||
|
||||
// If input already have parent just pass through
|
||||
if (parent.tagName.toLowerCase() === 'i' && parent.className.indexOf('waves-effect') !== -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Put element class and style to the specified parent
|
||||
var wrapper = document.createElement('i');
|
||||
wrapper.className = el.className + ' waves-input-wrapper';
|
||||
|
||||
var elementStyle = el.getAttribute('style');
|
||||
|
||||
if (!elementStyle) {
|
||||
elementStyle = '';
|
||||
}
|
||||
|
||||
wrapper.setAttribute('style', elementStyle);
|
||||
|
||||
el.className = 'waves-button-input';
|
||||
el.removeAttribute('style');
|
||||
|
||||
// Put element as child
|
||||
parent.replaceChild(wrapper, el);
|
||||
wrapper.appendChild(el);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Disable mousedown event for 500ms during and after touch
|
||||
*/
|
||||
var TouchHandler = {
|
||||
/* uses an integer rather than bool so there's no issues with
|
||||
* needing to clear timeouts if another touch event occurred
|
||||
* within the 500ms. Cannot mouseup between touchstart and
|
||||
* touchend, nor in the 500ms after touchend. */
|
||||
touches: 0,
|
||||
allowEvent: function(e) {
|
||||
var allow = true;
|
||||
|
||||
if (e.type === 'touchstart') {
|
||||
TouchHandler.touches += 1; //push
|
||||
} else if (e.type === 'touchend' || e.type === 'touchcancel') {
|
||||
setTimeout(function() {
|
||||
if (TouchHandler.touches > 0) {
|
||||
TouchHandler.touches -= 1; //pop after 500ms
|
||||
}
|
||||
}, 500);
|
||||
} else if (e.type === 'mousedown' && TouchHandler.touches > 0) {
|
||||
allow = false;
|
||||
}
|
||||
|
||||
return allow;
|
||||
},
|
||||
touchup: function(e) {
|
||||
TouchHandler.allowEvent(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Delegated click handler for .waves-effect element.
|
||||
* returns null when .waves-effect element not in "click tree"
|
||||
*/
|
||||
function getWavesEffectElement(e) {
|
||||
if (TouchHandler.allowEvent(e) === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var element = null;
|
||||
var target = e.target || e.srcElement;
|
||||
|
||||
while (target.parentNode !== null) {
|
||||
if (!(target instanceof SVGElement) && target.className.indexOf('waves-effect') !== -1) {
|
||||
element = target;
|
||||
break;
|
||||
}
|
||||
target = target.parentNode;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bubble the click and show effect if .waves-effect elem was found
|
||||
*/
|
||||
function showEffect(e) {
|
||||
var element = getWavesEffectElement(e);
|
||||
|
||||
if (element !== null) {
|
||||
Effect.show(e, element);
|
||||
|
||||
if ('ontouchstart' in window) {
|
||||
element.addEventListener('touchend', Effect.hide, false);
|
||||
element.addEventListener('touchcancel', Effect.hide, false);
|
||||
}
|
||||
|
||||
element.addEventListener('mouseup', Effect.hide, false);
|
||||
element.addEventListener('mouseleave', Effect.hide, false);
|
||||
element.addEventListener('dragend', Effect.hide, false);
|
||||
}
|
||||
}
|
||||
|
||||
Waves.displayEffect = function(options) {
|
||||
options = options || {};
|
||||
|
||||
if ('duration' in options) {
|
||||
Effect.duration = options.duration;
|
||||
}
|
||||
|
||||
//Wrap input inside <i> tag
|
||||
Effect.wrapInput($$('.waves-effect'));
|
||||
|
||||
if ('ontouchstart' in window) {
|
||||
document.body.addEventListener('touchstart', showEffect, false);
|
||||
}
|
||||
|
||||
document.body.addEventListener('mousedown', showEffect, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Attach Waves to an input element (or any element which doesn't
|
||||
* bubble mouseup/mousedown events).
|
||||
* Intended to be used with dynamically loaded forms/inputs, or
|
||||
* where the user doesn't want a delegated click handler.
|
||||
*/
|
||||
Waves.attach = function(element) {
|
||||
//FUTURE: automatically add waves classes and allow users
|
||||
// to specify them with an options param? Eg. light/classic/button
|
||||
if (element.tagName.toLowerCase() === 'input') {
|
||||
Effect.wrapInput([element]);
|
||||
element = element.parentNode;
|
||||
}
|
||||
|
||||
if ('ontouchstart' in window) {
|
||||
element.addEventListener('touchstart', showEffect, false);
|
||||
}
|
||||
|
||||
element.addEventListener('mousedown', showEffect, false);
|
||||
};
|
||||
|
||||
window.Waves = Waves;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
Waves.displayEffect();
|
||||
}, false);
|
||||
|
||||
})(window);
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"name": "materialize-css",
|
||||
"description": "Builds Materialize distribution packages",
|
||||
"author": "Alvin Wang, Alan Chang",
|
||||
"homepage": "http://materializecss.com/",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/js/materialize.js",
|
||||
"style": "dist/css/materialize.css",
|
||||
"sass": "sass/materialize.scss",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/Dogfalo/materialize.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/Dogfalo/materialize/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "grunt monitor",
|
||||
"test": "grunt travis",
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"js/*.js": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"dependencies": {},
|
||||
"engine": "node >= 6",
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^7.1.1",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-babel": "^6.0.0",
|
||||
"grunt-banner": "^0.6.0",
|
||||
"grunt-browser-sync": "^2.2.0",
|
||||
"grunt-concurrent": "^2.3.1",
|
||||
"grunt-contrib-clean": "^1.1.0",
|
||||
"grunt-contrib-compress": "^1.4.1",
|
||||
"grunt-contrib-concat": "^1.0.1",
|
||||
"grunt-contrib-jade": "^1.0.0",
|
||||
"grunt-contrib-jasmine": "^1.1.0",
|
||||
"grunt-contrib-uglify": "^3.0.1",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"grunt-notify": "^0.4.5",
|
||||
"grunt-postcss": "^0.8.0",
|
||||
"grunt-remove-logging": "^0.2.0",
|
||||
"grunt-rename-util": "^1.0.0",
|
||||
"grunt-sass": "^2.0.0",
|
||||
"grunt-text-replace": "^0.4.0",
|
||||
"husky": "^0.14.3",
|
||||
"jasmine": "^2.6.0",
|
||||
"jasmine-jquery": "^2.1.1",
|
||||
"jquery": "^3.2.1",
|
||||
"lint-staged": "^7.0.4",
|
||||
"node-archiver": "^0.3.0",
|
||||
"node-sass": "^4.7.2",
|
||||
"phantomjs-prebuilt": "^2.1.14",
|
||||
"prettier": "^1.12.1"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"extras",
|
||||
"js/**/*.js",
|
||||
"sass/**/*.scss",
|
||||
"Gruntfile.js",
|
||||
"LICENSE"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// Badges
|
||||
span.badge {
|
||||
min-width: 3rem;
|
||||
padding: 0 6px;
|
||||
margin-left: 14px;
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
line-height: $badge-height;
|
||||
height: $badge-height;
|
||||
color: color('grey', 'darken-1');
|
||||
float: right;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.new {
|
||||
font-weight: 300;
|
||||
font-size: 0.8rem;
|
||||
color: #fff;
|
||||
background-color: $badge-bg-color;
|
||||
border-radius: 2px;
|
||||
}
|
||||
&.new:after {
|
||||
content: " new";
|
||||
}
|
||||
|
||||
&[data-badge-caption]::after {
|
||||
content: " " attr(data-badge-caption);
|
||||
}
|
||||
}
|
||||
|
||||
// Special cases
|
||||
nav ul a span.badge {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
margin-left: 4px;
|
||||
line-height: $badge-height;
|
||||
height: $badge-height;
|
||||
-webkit-font-smoothing: auto;
|
||||
}
|
||||
|
||||
// Line height centering
|
||||
.collection-item span.badge {
|
||||
margin-top: calc(#{$collection-line-height / 2} - #{$badge-height / 2});
|
||||
}
|
||||
.collapsible span.badge {
|
||||
margin-left: auto;
|
||||
}
|
||||
.sidenav span.badge {
|
||||
margin-top: calc(#{$sidenav-line-height / 2} - #{$badge-height / 2});
|
||||
}
|
||||
|
||||
table span.badge {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
// shared styles
|
||||
.btn,
|
||||
.btn-flat {
|
||||
border: $button-border;
|
||||
border-radius: $button-radius;
|
||||
display: inline-block;
|
||||
height: $button-height;
|
||||
line-height: $button-height;
|
||||
padding: $button-padding;
|
||||
text-transform: uppercase;
|
||||
vertical-align: middle;
|
||||
-webkit-tap-highlight-color: transparent; // Gets rid of tap active state
|
||||
}
|
||||
|
||||
// Disabled shared style
|
||||
.btn.disabled,
|
||||
.btn-floating.disabled,
|
||||
.btn-large.disabled,
|
||||
.btn-small.disabled,
|
||||
.btn-flat.disabled,
|
||||
.btn:disabled,
|
||||
.btn-floating:disabled,
|
||||
.btn-large:disabled,
|
||||
.btn-small:disabled,
|
||||
.btn-flat:disabled,
|
||||
.btn[disabled],
|
||||
.btn-floating[disabled],
|
||||
.btn-large[disabled],
|
||||
.btn-small[disabled],
|
||||
.btn-flat[disabled] {
|
||||
pointer-events: none;
|
||||
background-color: $button-disabled-background !important;
|
||||
box-shadow: none;
|
||||
color: $button-disabled-color !important;
|
||||
cursor: default;
|
||||
&:hover {
|
||||
background-color: $button-disabled-background !important;
|
||||
color: $button-disabled-color !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Shared icon styles
|
||||
.btn,
|
||||
.btn-floating,
|
||||
.btn-large,
|
||||
.btn-small,
|
||||
.btn-flat {
|
||||
font-size: $button-font-size;
|
||||
outline: 0;
|
||||
i {
|
||||
font-size: $button-icon-font-size;
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
// Shared focus button style
|
||||
.btn,
|
||||
.btn-floating {
|
||||
&:focus {
|
||||
background-color: darken($button-raised-background, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
// Raised Button
|
||||
.btn {
|
||||
text-decoration: none;
|
||||
color: $button-raised-color;
|
||||
background-color: $button-raised-background;
|
||||
text-align: center;
|
||||
letter-spacing: .5px;
|
||||
@extend .z-depth-1;
|
||||
transition: background-color .2s ease-out;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: $button-raised-background-hover;
|
||||
@extend .z-depth-1-half;
|
||||
}
|
||||
}
|
||||
|
||||
// Floating button
|
||||
.btn-floating {
|
||||
&:hover {
|
||||
background-color: $button-floating-background-hover;
|
||||
@extend .z-depth-1-half;
|
||||
}
|
||||
&:before {
|
||||
border-radius: 0;
|
||||
}
|
||||
&.btn-large {
|
||||
&.halfway-fab {
|
||||
bottom: -$button-floating-large-size / 2;
|
||||
}
|
||||
width: $button-floating-large-size;
|
||||
height: $button-floating-large-size;
|
||||
padding: 0;
|
||||
i {
|
||||
line-height: $button-floating-large-size;
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-small {
|
||||
&.halfway-fab {
|
||||
bottom: -$button-floating-small-size / 2;
|
||||
}
|
||||
width: $button-floating-small-size;
|
||||
height: $button-floating-small-size;
|
||||
i {
|
||||
line-height: $button-floating-small-size;
|
||||
}
|
||||
}
|
||||
|
||||
&.halfway-fab {
|
||||
&.left {
|
||||
right: auto;
|
||||
left: 24px;
|
||||
}
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
bottom: -$button-floating-size / 2;
|
||||
}
|
||||
display: inline-block;
|
||||
color: $button-floating-color;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
z-index: 1;
|
||||
width: $button-floating-size;
|
||||
height: $button-floating-size;
|
||||
line-height: $button-floating-size;
|
||||
padding: 0;
|
||||
background-color: $button-floating-background;
|
||||
border-radius: $button-floating-radius;
|
||||
@extend .z-depth-1;
|
||||
transition: background-color .3s;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
i {
|
||||
width: inherit;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
color: $button-floating-color;
|
||||
font-size: $button-large-icon-font-size;
|
||||
line-height: $button-floating-size;
|
||||
}
|
||||
}
|
||||
|
||||
// button fix
|
||||
button.btn-floating {
|
||||
border: $button-border;
|
||||
}
|
||||
|
||||
// Fixed Action Button
|
||||
.fixed-action-btn {
|
||||
&.active {
|
||||
ul {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
// Directions
|
||||
&.direction-left,
|
||||
&.direction-right {
|
||||
padding: 0 0 0 15px;
|
||||
ul {
|
||||
text-align: right;
|
||||
right: 64px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
height: 100%;
|
||||
left: auto;
|
||||
/*width 100% only goes to width of button container */
|
||||
width: 500px;
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 7.5px 15px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.direction-right {
|
||||
padding: 0 15px 0 0;
|
||||
ul {
|
||||
text-align: left;
|
||||
direction: rtl;
|
||||
left: 64px;
|
||||
right: auto;
|
||||
li {
|
||||
margin: 7.5px 0 0 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.direction-bottom {
|
||||
padding: 0 0 15px 0;
|
||||
ul {
|
||||
top: 64px;
|
||||
bottom: auto;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
li {
|
||||
margin: 15px 0 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.toolbar {
|
||||
&.active {
|
||||
&>a i {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
padding: 0;
|
||||
height: $button-floating-large-size;
|
||||
ul {
|
||||
display: flex;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
li {
|
||||
flex: 1;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
transition: none;
|
||||
a {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
color: #fff;
|
||||
line-height: $button-floating-large-size;
|
||||
z-index: 1;
|
||||
i {
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
position: fixed;
|
||||
right: 23px;
|
||||
bottom: 23px;
|
||||
padding-top: 15px;
|
||||
margin-bottom: 0;
|
||||
z-index: 997;
|
||||
ul {
|
||||
left: 0;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
bottom: 64px;
|
||||
margin: 0;
|
||||
visibility: hidden;
|
||||
li {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
a.btn-floating {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.fab-backdrop {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
width: $button-floating-size;
|
||||
height: $button-floating-size;
|
||||
background-color: $button-floating-background;
|
||||
border-radius: $button-floating-radius;
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Flat button
|
||||
.btn-flat {
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
color: $button-flat-color;
|
||||
cursor: pointer;
|
||||
transition: background-color .2s;
|
||||
&:focus,
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
&:focus {
|
||||
background-color: rgba(0, 0, 0, .1);
|
||||
}
|
||||
&.disabled,
|
||||
&.btn-flat[disabled] {
|
||||
background-color: transparent !important;
|
||||
color: $button-flat-disabled-color !important;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
// Large button
|
||||
.btn-large {
|
||||
@extend .btn;
|
||||
height: $button-large-height;
|
||||
line-height: $button-large-height;
|
||||
font-size: $button-large-font-size;
|
||||
padding: 0 28px;
|
||||
|
||||
i {
|
||||
font-size: $button-large-icon-font-size;
|
||||
}
|
||||
}
|
||||
|
||||
// Small button
|
||||
.btn-small {
|
||||
@extend .btn;
|
||||
height: $button-small-height;
|
||||
line-height: $button-small-height;
|
||||
font-size: $button-small-font-size;
|
||||
i {
|
||||
font-size: $button-small-icon-font-size;
|
||||
}
|
||||
}
|
||||
|
||||
// Block button
|
||||
.btn-block {
|
||||
display: block;
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
|
||||
|
||||
.card-panel {
|
||||
transition: box-shadow .25s;
|
||||
padding: $card-padding;
|
||||
margin: $element-top-margin 0 $element-bottom-margin 0;
|
||||
border-radius: 2px;
|
||||
@extend .z-depth-1;
|
||||
background-color: $card-bg-color;
|
||||
}
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
margin: $element-top-margin 0 $element-bottom-margin 0;
|
||||
background-color: $card-bg-color;
|
||||
transition: box-shadow .25s;
|
||||
border-radius: 2px;
|
||||
@extend .z-depth-1;
|
||||
|
||||
|
||||
.card-title {
|
||||
font-size: 24px;
|
||||
font-weight: 300;
|
||||
&.activator {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
// Card Sizes
|
||||
&.small, &.medium, &.large {
|
||||
position: relative;
|
||||
|
||||
.card-image {
|
||||
max-height: 60%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.card-image + .card-content {
|
||||
max-height: 40%;
|
||||
}
|
||||
.card-content {
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.card-action {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.small {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
&.medium {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
&.large {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
// Horizontal Cards
|
||||
&.horizontal {
|
||||
&.small, &.medium, &.large {
|
||||
.card-image {
|
||||
height: 100%;
|
||||
max-height: none;
|
||||
overflow: visible;
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
display: flex;
|
||||
|
||||
.card-image {
|
||||
max-width: 50%;
|
||||
img {
|
||||
border-radius: 2px 0 0 2px;
|
||||
max-width: 100%;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.card-stacked {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
|
||||
.card-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sticky Action Section
|
||||
&.sticky-action {
|
||||
.card-action {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.card-reveal {
|
||||
z-index: 1;
|
||||
padding-bottom: 64px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.card-image {
|
||||
position: relative;
|
||||
|
||||
// Image background for content
|
||||
img {
|
||||
display: block;
|
||||
border-radius: 2px 2px 0 0;
|
||||
position: relative;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
color: $card-bg-color;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
max-width: 100%;
|
||||
padding: $card-padding;
|
||||
}
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: $card-padding;
|
||||
border-radius: 0 0 2px 2px;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
.card-title {
|
||||
display: block;
|
||||
line-height: 32px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
i {
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-action {
|
||||
&:last-child {
|
||||
border-radius: 0 0 2px 2px;
|
||||
}
|
||||
background-color: inherit; // Use inherit to inherit color classes
|
||||
border-top: 1px solid rgba(160,160,160,.2);
|
||||
position: relative;
|
||||
padding: 16px $card-padding;
|
||||
|
||||
a:not(.btn):not(.btn-large):not(.btn-floating) {
|
||||
color: $card-link-color;
|
||||
margin-right: $card-padding;
|
||||
transition: color .3s ease;
|
||||
text-transform: uppercase;
|
||||
|
||||
&:hover { color: $card-link-color-light; }
|
||||
}
|
||||
}
|
||||
|
||||
.card-reveal {
|
||||
padding: $card-padding;
|
||||
position: absolute;
|
||||
background-color: $card-bg-color;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
left: 0;
|
||||
top: 100%;
|
||||
height: 100%;
|
||||
z-index: 3;
|
||||
display: none;
|
||||
|
||||
.card-title {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
.carousel {
|
||||
&.carousel-slider {
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
.carousel-fixed-item {
|
||||
&.with-indicators {
|
||||
bottom: 68px;
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 20px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.carousel-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: $carousel-height;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: $carousel-height;
|
||||
perspective: 500px;
|
||||
transform-style: preserve-3d;
|
||||
transform-origin: 0% 50%;
|
||||
|
||||
.carousel-item {
|
||||
visibility: hidden;
|
||||
width: $carousel-item-width;
|
||||
height: $carousel-item-height;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
& > img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.indicators {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
|
||||
.indicator-item {
|
||||
&.active {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
margin: 24px 4px;
|
||||
background-color: rgba(255,255,255,.5);
|
||||
|
||||
transition: background-color .3s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
// Materialbox compatibility
|
||||
&.scrolling .carousel-item .materialboxed,
|
||||
.carousel-item:not(.active) .materialboxed {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
.chip {
|
||||
&:focus {
|
||||
outline: none;
|
||||
background-color: $chip-selected-color;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
display: inline-block;
|
||||
height: 32px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: rgba(0,0,0,.6);
|
||||
line-height: 32px;
|
||||
padding: 0 12px;
|
||||
border-radius: 16px;
|
||||
background-color: $chip-bg-color;
|
||||
margin-bottom: $chip-margin;
|
||||
margin-right: $chip-margin;
|
||||
|
||||
> img {
|
||||
float: left;
|
||||
margin: 0 8px 0 -12px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.close {
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.chips {
|
||||
border: none;
|
||||
border-bottom: 1px solid $chip-border-color;
|
||||
box-shadow: none;
|
||||
margin: $input-margin;
|
||||
min-height: 45px;
|
||||
outline: none;
|
||||
transition: all .3s;
|
||||
|
||||
&.focus {
|
||||
border-bottom: 1px solid $chip-selected-color;
|
||||
box-shadow: 0 1px 0 0 $chip-selected-color;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.input {
|
||||
background: none;
|
||||
border: 0;
|
||||
color: rgba(0,0,0,.6);
|
||||
display: inline-block;
|
||||
font-size: $input-font-size;
|
||||
height: $input-height;
|
||||
line-height: 32px;
|
||||
outline: 0;
|
||||
margin: 0;
|
||||
padding: 0 !important;
|
||||
width: 120px !important;
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
border: 0 !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
// Autocomplete
|
||||
.autocomplete-content {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Form prefix
|
||||
.prefix ~ .chips {
|
||||
margin-left: 3rem;
|
||||
width: 92%;
|
||||
width: calc(100% - 3rem);
|
||||
}
|
||||
.chips:empty ~ label {
|
||||
font-size: 0.8rem;
|
||||
transform: translateY(-140%);
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
.collapsible {
|
||||
border-top: 1px solid $collapsible-border-color;
|
||||
border-right: 1px solid $collapsible-border-color;
|
||||
border-left: 1px solid $collapsible-border-color;
|
||||
margin: $element-top-margin 0 $element-bottom-margin 0;
|
||||
@extend .z-depth-1;
|
||||
}
|
||||
|
||||
.collapsible-header {
|
||||
&:focus {
|
||||
outline: 0
|
||||
}
|
||||
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
line-height: 1.5;
|
||||
padding: 1rem;
|
||||
background-color: $collapsible-header-color;
|
||||
border-bottom: 1px solid $collapsible-border-color;
|
||||
|
||||
i {
|
||||
width: 2rem;
|
||||
font-size: 1.6rem;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
.keyboard-focused .collapsible-header:focus {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.collapsible-body {
|
||||
display: none;
|
||||
border-bottom: 1px solid $collapsible-border-color;
|
||||
box-sizing: border-box;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
// Sidenav collapsible styling
|
||||
.sidenav,
|
||||
.sidenav.fixed {
|
||||
|
||||
.collapsible {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
|
||||
li { padding: 0; }
|
||||
}
|
||||
|
||||
.collapsible-header {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
line-height: inherit;
|
||||
height: inherit;
|
||||
padding: 0 $sidenav-padding;
|
||||
|
||||
&:hover { background-color: rgba(0,0,0,.05); }
|
||||
i { line-height: inherit; }
|
||||
}
|
||||
|
||||
.collapsible-body {
|
||||
border: 0;
|
||||
background-color: $collapsible-header-color;
|
||||
|
||||
li a {
|
||||
padding: 0 (7.5px + $sidenav-padding)
|
||||
0 (15px + $sidenav-padding);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Popout Collapsible
|
||||
|
||||
.collapsible.popout {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
> li {
|
||||
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
|
||||
// transform: scaleX(.92);
|
||||
margin: 0 24px;
|
||||
transition: margin .35s cubic-bezier(0.250, 0.460, 0.450, 0.940);
|
||||
}
|
||||
> li.active {
|
||||
box-shadow: 0 5px 11px 0 rgba(0, 0, 0, 0.18), 0 4px 15px 0 rgba(0, 0, 0, 0.15);
|
||||
margin: 16px 0;
|
||||
// transform: scaleX(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Color Classes
|
||||
|
||||
@each $color_name, $color in $colors {
|
||||
@each $color_type, $color_value in $color {
|
||||
@if $color_type == "base" {
|
||||
.#{$color_name} {
|
||||
background-color: $color_value !important;
|
||||
}
|
||||
.#{$color_name}-text {
|
||||
color: $color_value !important;
|
||||
}
|
||||
}
|
||||
@else if $color_name != "shades" {
|
||||
.#{$color_name}.#{$color_type} {
|
||||
background-color: $color_value !important;
|
||||
}
|
||||
.#{$color_name}-text.text-#{$color_type} {
|
||||
color: $color_value !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shade classes
|
||||
@each $color, $color_value in $shades {
|
||||
.#{$color} {
|
||||
background-color: $color_value !important;
|
||||
}
|
||||
.#{$color}-text {
|
||||
color: $color_value !important;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,370 @@
|
|||
// Google Color Palette defined: http://www.google.com/design/spec/style/color.html
|
||||
|
||||
$materialize-red: (
|
||||
"base": #e51c23,
|
||||
"lighten-5": #fdeaeb,
|
||||
"lighten-4": #f8c1c3,
|
||||
"lighten-3": #f3989b,
|
||||
"lighten-2": #ee6e73,
|
||||
"lighten-1": #ea454b,
|
||||
"darken-1": #d0181e,
|
||||
"darken-2": #b9151b,
|
||||
"darken-3": #a21318,
|
||||
"darken-4": #8b1014,
|
||||
);
|
||||
|
||||
$red: (
|
||||
"base": #F44336,
|
||||
"lighten-5": #FFEBEE,
|
||||
"lighten-4": #FFCDD2,
|
||||
"lighten-3": #EF9A9A,
|
||||
"lighten-2": #E57373,
|
||||
"lighten-1": #EF5350,
|
||||
"darken-1": #E53935,
|
||||
"darken-2": #D32F2F,
|
||||
"darken-3": #C62828,
|
||||
"darken-4": #B71C1C,
|
||||
"accent-1": #FF8A80,
|
||||
"accent-2": #FF5252,
|
||||
"accent-3": #FF1744,
|
||||
"accent-4": #D50000
|
||||
);
|
||||
|
||||
$pink: (
|
||||
"base": #e91e63,
|
||||
"lighten-5": #fce4ec,
|
||||
"lighten-4": #f8bbd0,
|
||||
"lighten-3": #f48fb1,
|
||||
"lighten-2": #f06292,
|
||||
"lighten-1": #ec407a,
|
||||
"darken-1": #d81b60,
|
||||
"darken-2": #c2185b,
|
||||
"darken-3": #ad1457,
|
||||
"darken-4": #880e4f,
|
||||
"accent-1": #ff80ab,
|
||||
"accent-2": #ff4081,
|
||||
"accent-3": #f50057,
|
||||
"accent-4": #c51162
|
||||
);
|
||||
|
||||
$purple: (
|
||||
"base": #9c27b0,
|
||||
"lighten-5": #f3e5f5,
|
||||
"lighten-4": #e1bee7,
|
||||
"lighten-3": #ce93d8,
|
||||
"lighten-2": #ba68c8,
|
||||
"lighten-1": #ab47bc,
|
||||
"darken-1": #8e24aa,
|
||||
"darken-2": #7b1fa2,
|
||||
"darken-3": #6a1b9a,
|
||||
"darken-4": #4a148c,
|
||||
"accent-1": #ea80fc,
|
||||
"accent-2": #e040fb,
|
||||
"accent-3": #d500f9,
|
||||
"accent-4": #aa00ff
|
||||
);
|
||||
|
||||
$deep-purple: (
|
||||
"base": #673ab7,
|
||||
"lighten-5": #ede7f6,
|
||||
"lighten-4": #d1c4e9,
|
||||
"lighten-3": #b39ddb,
|
||||
"lighten-2": #9575cd,
|
||||
"lighten-1": #7e57c2,
|
||||
"darken-1": #5e35b1,
|
||||
"darken-2": #512da8,
|
||||
"darken-3": #4527a0,
|
||||
"darken-4": #311b92,
|
||||
"accent-1": #b388ff,
|
||||
"accent-2": #7c4dff,
|
||||
"accent-3": #651fff,
|
||||
"accent-4": #6200ea
|
||||
);
|
||||
|
||||
$indigo: (
|
||||
"base": #3f51b5,
|
||||
"lighten-5": #e8eaf6,
|
||||
"lighten-4": #c5cae9,
|
||||
"lighten-3": #9fa8da,
|
||||
"lighten-2": #7986cb,
|
||||
"lighten-1": #5c6bc0,
|
||||
"darken-1": #3949ab,
|
||||
"darken-2": #303f9f,
|
||||
"darken-3": #283593,
|
||||
"darken-4": #1a237e,
|
||||
"accent-1": #8c9eff,
|
||||
"accent-2": #536dfe,
|
||||
"accent-3": #3d5afe,
|
||||
"accent-4": #304ffe
|
||||
);
|
||||
|
||||
$blue: (
|
||||
"base": #2196F3,
|
||||
"lighten-5": #E3F2FD,
|
||||
"lighten-4": #BBDEFB,
|
||||
"lighten-3": #90CAF9,
|
||||
"lighten-2": #64B5F6,
|
||||
"lighten-1": #42A5F5,
|
||||
"darken-1": #1E88E5,
|
||||
"darken-2": #1976D2,
|
||||
"darken-3": #1565C0,
|
||||
"darken-4": #0D47A1,
|
||||
"accent-1": #82B1FF,
|
||||
"accent-2": #448AFF,
|
||||
"accent-3": #2979FF,
|
||||
"accent-4": #2962FF
|
||||
);
|
||||
|
||||
$light-blue: (
|
||||
"base": #03a9f4,
|
||||
"lighten-5": #e1f5fe,
|
||||
"lighten-4": #b3e5fc,
|
||||
"lighten-3": #81d4fa,
|
||||
"lighten-2": #4fc3f7,
|
||||
"lighten-1": #29b6f6,
|
||||
"darken-1": #039be5,
|
||||
"darken-2": #0288d1,
|
||||
"darken-3": #0277bd,
|
||||
"darken-4": #01579b,
|
||||
"accent-1": #80d8ff,
|
||||
"accent-2": #40c4ff,
|
||||
"accent-3": #00b0ff,
|
||||
"accent-4": #0091ea
|
||||
);
|
||||
|
||||
$cyan: (
|
||||
"base": #00bcd4,
|
||||
"lighten-5": #e0f7fa,
|
||||
"lighten-4": #b2ebf2,
|
||||
"lighten-3": #80deea,
|
||||
"lighten-2": #4dd0e1,
|
||||
"lighten-1": #26c6da,
|
||||
"darken-1": #00acc1,
|
||||
"darken-2": #0097a7,
|
||||
"darken-3": #00838f,
|
||||
"darken-4": #006064,
|
||||
"accent-1": #84ffff,
|
||||
"accent-2": #18ffff,
|
||||
"accent-3": #00e5ff,
|
||||
"accent-4": #00b8d4
|
||||
);
|
||||
|
||||
$teal: (
|
||||
"base": #009688,
|
||||
"lighten-5": #e0f2f1,
|
||||
"lighten-4": #b2dfdb,
|
||||
"lighten-3": #80cbc4,
|
||||
"lighten-2": #4db6ac,
|
||||
"lighten-1": #26a69a,
|
||||
"darken-1": #00897b,
|
||||
"darken-2": #00796b,
|
||||
"darken-3": #00695c,
|
||||
"darken-4": #004d40,
|
||||
"accent-1": #a7ffeb,
|
||||
"accent-2": #64ffda,
|
||||
"accent-3": #1de9b6,
|
||||
"accent-4": #00bfa5
|
||||
);
|
||||
|
||||
$green: (
|
||||
"base": #4CAF50,
|
||||
"lighten-5": #E8F5E9,
|
||||
"lighten-4": #C8E6C9,
|
||||
"lighten-3": #A5D6A7,
|
||||
"lighten-2": #81C784,
|
||||
"lighten-1": #66BB6A,
|
||||
"darken-1": #43A047,
|
||||
"darken-2": #388E3C,
|
||||
"darken-3": #2E7D32,
|
||||
"darken-4": #1B5E20,
|
||||
"accent-1": #B9F6CA,
|
||||
"accent-2": #69F0AE,
|
||||
"accent-3": #00E676,
|
||||
"accent-4": #00C853
|
||||
);
|
||||
|
||||
$light-green: (
|
||||
"base": #8bc34a,
|
||||
"lighten-5": #f1f8e9,
|
||||
"lighten-4": #dcedc8,
|
||||
"lighten-3": #c5e1a5,
|
||||
"lighten-2": #aed581,
|
||||
"lighten-1": #9ccc65,
|
||||
"darken-1": #7cb342,
|
||||
"darken-2": #689f38,
|
||||
"darken-3": #558b2f,
|
||||
"darken-4": #33691e,
|
||||
"accent-1": #ccff90,
|
||||
"accent-2": #b2ff59,
|
||||
"accent-3": #76ff03,
|
||||
"accent-4": #64dd17
|
||||
);
|
||||
|
||||
$lime: (
|
||||
"base": #cddc39,
|
||||
"lighten-5": #f9fbe7,
|
||||
"lighten-4": #f0f4c3,
|
||||
"lighten-3": #e6ee9c,
|
||||
"lighten-2": #dce775,
|
||||
"lighten-1": #d4e157,
|
||||
"darken-1": #c0ca33,
|
||||
"darken-2": #afb42b,
|
||||
"darken-3": #9e9d24,
|
||||
"darken-4": #827717,
|
||||
"accent-1": #f4ff81,
|
||||
"accent-2": #eeff41,
|
||||
"accent-3": #c6ff00,
|
||||
"accent-4": #aeea00
|
||||
);
|
||||
|
||||
$yellow: (
|
||||
"base": #ffeb3b,
|
||||
"lighten-5": #fffde7,
|
||||
"lighten-4": #fff9c4,
|
||||
"lighten-3": #fff59d,
|
||||
"lighten-2": #fff176,
|
||||
"lighten-1": #ffee58,
|
||||
"darken-1": #fdd835,
|
||||
"darken-2": #fbc02d,
|
||||
"darken-3": #f9a825,
|
||||
"darken-4": #f57f17,
|
||||
"accent-1": #ffff8d,
|
||||
"accent-2": #ffff00,
|
||||
"accent-3": #ffea00,
|
||||
"accent-4": #ffd600
|
||||
);
|
||||
|
||||
$amber: (
|
||||
"base": #ffc107,
|
||||
"lighten-5": #fff8e1,
|
||||
"lighten-4": #ffecb3,
|
||||
"lighten-3": #ffe082,
|
||||
"lighten-2": #ffd54f,
|
||||
"lighten-1": #ffca28,
|
||||
"darken-1": #ffb300,
|
||||
"darken-2": #ffa000,
|
||||
"darken-3": #ff8f00,
|
||||
"darken-4": #ff6f00,
|
||||
"accent-1": #ffe57f,
|
||||
"accent-2": #ffd740,
|
||||
"accent-3": #ffc400,
|
||||
"accent-4": #ffab00
|
||||
);
|
||||
|
||||
$orange: (
|
||||
"base": #ff9800,
|
||||
"lighten-5": #fff3e0,
|
||||
"lighten-4": #ffe0b2,
|
||||
"lighten-3": #ffcc80,
|
||||
"lighten-2": #ffb74d,
|
||||
"lighten-1": #ffa726,
|
||||
"darken-1": #fb8c00,
|
||||
"darken-2": #f57c00,
|
||||
"darken-3": #ef6c00,
|
||||
"darken-4": #e65100,
|
||||
"accent-1": #ffd180,
|
||||
"accent-2": #ffab40,
|
||||
"accent-3": #ff9100,
|
||||
"accent-4": #ff6d00
|
||||
);
|
||||
|
||||
$deep-orange: (
|
||||
"base": #ff5722,
|
||||
"lighten-5": #fbe9e7,
|
||||
"lighten-4": #ffccbc,
|
||||
"lighten-3": #ffab91,
|
||||
"lighten-2": #ff8a65,
|
||||
"lighten-1": #ff7043,
|
||||
"darken-1": #f4511e,
|
||||
"darken-2": #e64a19,
|
||||
"darken-3": #d84315,
|
||||
"darken-4": #bf360c,
|
||||
"accent-1": #ff9e80,
|
||||
"accent-2": #ff6e40,
|
||||
"accent-3": #ff3d00,
|
||||
"accent-4": #dd2c00
|
||||
);
|
||||
|
||||
$brown: (
|
||||
"base": #795548,
|
||||
"lighten-5": #efebe9,
|
||||
"lighten-4": #d7ccc8,
|
||||
"lighten-3": #bcaaa4,
|
||||
"lighten-2": #a1887f,
|
||||
"lighten-1": #8d6e63,
|
||||
"darken-1": #6d4c41,
|
||||
"darken-2": #5d4037,
|
||||
"darken-3": #4e342e,
|
||||
"darken-4": #3e2723
|
||||
);
|
||||
|
||||
$blue-grey: (
|
||||
"base": #607d8b,
|
||||
"lighten-5": #eceff1,
|
||||
"lighten-4": #cfd8dc,
|
||||
"lighten-3": #b0bec5,
|
||||
"lighten-2": #90a4ae,
|
||||
"lighten-1": #78909c,
|
||||
"darken-1": #546e7a,
|
||||
"darken-2": #455a64,
|
||||
"darken-3": #37474f,
|
||||
"darken-4": #263238
|
||||
);
|
||||
|
||||
$grey: (
|
||||
"base": #9e9e9e,
|
||||
"lighten-5": #fafafa,
|
||||
"lighten-4": #f5f5f5,
|
||||
"lighten-3": #eeeeee,
|
||||
"lighten-2": #e0e0e0,
|
||||
"lighten-1": #bdbdbd,
|
||||
"darken-1": #757575,
|
||||
"darken-2": #616161,
|
||||
"darken-3": #424242,
|
||||
"darken-4": #212121
|
||||
);
|
||||
|
||||
$shades: (
|
||||
"black": #000000,
|
||||
"white": #FFFFFF,
|
||||
"transparent": transparent
|
||||
);
|
||||
|
||||
$colors: (
|
||||
"materialize-red": $materialize-red,
|
||||
"red": $red,
|
||||
"pink": $pink,
|
||||
"purple": $purple,
|
||||
"deep-purple": $deep-purple,
|
||||
"indigo": $indigo,
|
||||
"blue": $blue,
|
||||
"light-blue": $light-blue,
|
||||
"cyan": $cyan,
|
||||
"teal": $teal,
|
||||
"green": $green,
|
||||
"light-green": $light-green,
|
||||
"lime": $lime,
|
||||
"yellow": $yellow,
|
||||
"amber": $amber,
|
||||
"orange": $orange,
|
||||
"deep-orange": $deep-orange,
|
||||
"brown": $brown,
|
||||
"blue-grey": $blue-grey,
|
||||
"grey": $grey,
|
||||
"shades": $shades
|
||||
) !default;
|
||||
|
||||
|
||||
// usage: color("name_of_color", "type_of_color")
|
||||
// to avoid to repeating map-get($colors, ...)
|
||||
|
||||
@function color($color, $type) {
|
||||
@if map-has-key($colors, $color) {
|
||||
$curr_color: map-get($colors, $color);
|
||||
@if map-has-key($curr_color, $type) {
|
||||
@return map-get($curr_color, $type);
|
||||
}
|
||||
}
|
||||
@warn "Unknown `#{$color}` - `#{$type}` in $colors.";
|
||||
@return null;
|
||||
}
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
/* Modal */
|
||||
.datepicker-modal {
|
||||
max-width: 325px;
|
||||
min-width: 300px;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.datepicker-container.modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.datepicker-controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 280px;
|
||||
margin: 0 auto;
|
||||
|
||||
.selects-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
input {
|
||||
&:focus {
|
||||
border-bottom: none;
|
||||
}
|
||||
border-bottom: none;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.caret {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.select-year input {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.select-month input {
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
.month-prev, .month-next {
|
||||
margin-top: 4px;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
/* Date Display */
|
||||
.datepicker-date-display {
|
||||
flex: 1 auto;
|
||||
background-color: $secondary-color;
|
||||
color: #fff;
|
||||
padding: 20px 22px;
|
||||
font-weight: 500;
|
||||
|
||||
.year-text {
|
||||
display: block;
|
||||
font-size: 1.5rem;
|
||||
line-height: 25px;
|
||||
color: $datepicker-year;
|
||||
}
|
||||
|
||||
.date-text {
|
||||
display: block;
|
||||
font-size: 2.8rem;
|
||||
line-height: 47px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Calendar */
|
||||
.datepicker-calendar-container {
|
||||
flex: 2.5 auto;
|
||||
}
|
||||
|
||||
.datepicker-table {
|
||||
width: 280px;
|
||||
font-size: 1rem;
|
||||
margin: 0 auto;
|
||||
|
||||
thead {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 10px 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
tr {
|
||||
border: none;
|
||||
}
|
||||
|
||||
abbr {
|
||||
text-decoration: none;
|
||||
color: $datepicker-calendar-header-color;
|
||||
}
|
||||
|
||||
td {
|
||||
&.is-today {
|
||||
color: $secondary-color;
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
background-color: $secondary-color;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.is-outside-current-month,
|
||||
&.is-disabled {
|
||||
color: $datepicker-disabled-day-color;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
border-radius: 50%;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-day-button {
|
||||
&:focus {
|
||||
background-color: $datepicker-day-focus;
|
||||
}
|
||||
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
line-height: 38px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
border-radius: 50%;
|
||||
padding: 0 5px;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
|
||||
/* Footer */
|
||||
.datepicker-footer {
|
||||
width: 280px;
|
||||
margin: 0 auto;
|
||||
padding-bottom: 5px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.datepicker-cancel,
|
||||
.datepicker-clear,
|
||||
.datepicker-today,
|
||||
.datepicker-done {
|
||||
color: $secondary-color;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.datepicker-clear {
|
||||
color: $error-color;
|
||||
}
|
||||
|
||||
|
||||
/* Media Queries */
|
||||
@media #{$medium-and-up} {
|
||||
.datepicker-modal {
|
||||
max-width: 625px;
|
||||
}
|
||||
|
||||
.datepicker-container.modal-content {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.datepicker-date-display {
|
||||
flex: 0 1 270px;
|
||||
}
|
||||
|
||||
.datepicker-controls,
|
||||
.datepicker-table,
|
||||
.datepicker-footer {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.datepicker-day-button {
|
||||
line-height: 44px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
.dropdown-content {
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
|
||||
@extend .z-depth-1;
|
||||
background-color: $dropdown-bg-color;
|
||||
margin: 0;
|
||||
display: none;
|
||||
min-width: 100px;
|
||||
overflow-y: auto;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 9999; // TODO: Check if this doesn't break other things
|
||||
transform-origin: 0 0;
|
||||
|
||||
|
||||
li {
|
||||
&:hover, &.active {
|
||||
background-color: $dropdown-hover-bg-color;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&.divider {
|
||||
min-height: 0;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
& > a, & > span {
|
||||
font-size: 16px;
|
||||
color: $dropdown-color;
|
||||
display: block;
|
||||
line-height: 22px;
|
||||
padding: (($dropdown-item-height - 22) / 2) 16px;
|
||||
}
|
||||
|
||||
& > span > label {
|
||||
top: 1px;
|
||||
left: 0;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
// Icon alignment override
|
||||
& > a > i {
|
||||
height: inherit;
|
||||
line-height: inherit;
|
||||
float: left;
|
||||
margin: 0 24px 0 0;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
|
||||
clear: both;
|
||||
color: $off-black;
|
||||
cursor: pointer;
|
||||
min-height: $dropdown-item-height;
|
||||
line-height: 1.5rem;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
body.keyboard-focused {
|
||||
.dropdown-content li:focus {
|
||||
background-color: darken($dropdown-hover-bg-color, 8%);
|
||||
}
|
||||
}
|
||||
|
||||
// Input field specificity bugfix
|
||||
.input-field.col .dropdown-content [type="checkbox"] + label {
|
||||
top: 1px;
|
||||
left: 0;
|
||||
height: 18px;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.dropdown-trigger {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
@ -0,0 +1,769 @@
|
|||
//Default styles
|
||||
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
*, *:before, *:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
// display: flex;
|
||||
// min-height: 100vh;
|
||||
// flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
// flex: 1 0 auto;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: $font-stack;
|
||||
}
|
||||
|
||||
ul {
|
||||
&:not(.browser-default) {
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
|
||||
& > li {
|
||||
list-style-type: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $link-color;
|
||||
text-decoration: none;
|
||||
|
||||
// Gets rid of tap active state
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
// Positioning
|
||||
.valign-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
// classic clearfix
|
||||
.clearfix {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
// Z-levels
|
||||
.z-depth-0 {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* 2dp elevation modified*/
|
||||
.z-depth-1 {
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14),
|
||||
0 3px 1px -2px rgba(0,0,0,0.12),
|
||||
0 1px 5px 0 rgba(0,0,0,0.2);
|
||||
}
|
||||
.z-depth-1-half {
|
||||
box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.14), 0 1px 7px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* 6dp elevation modified*/
|
||||
.z-depth-2 {
|
||||
box-shadow: 0 4px 5px 0 rgba(0,0,0,0.14),
|
||||
0 1px 10px 0 rgba(0,0,0,0.12),
|
||||
0 2px 4px -1px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
/* 12dp elevation modified*/
|
||||
.z-depth-3 {
|
||||
box-shadow: 0 8px 17px 2px rgba(0,0,0,0.14),
|
||||
0 3px 14px 2px rgba(0,0,0,0.12),
|
||||
0 5px 5px -3px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* 16dp elevation */
|
||||
.z-depth-4 {
|
||||
box-shadow: 0 16px 24px 2px rgba(0,0,0,0.14),
|
||||
0 6px 30px 5px rgba(0,0,0,0.12),
|
||||
0 8px 10px -7px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
/* 24dp elevation */
|
||||
.z-depth-5 {
|
||||
box-shadow: 0 24px 38px 3px rgba(0,0,0,0.14),
|
||||
0 9px 46px 8px rgba(0,0,0,0.12),
|
||||
0 11px 15px -7px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.hoverable {
|
||||
transition: box-shadow .25s;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
}
|
||||
}
|
||||
|
||||
// Dividers
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
background-color: color("grey", "lighten-2");
|
||||
}
|
||||
|
||||
|
||||
// Blockquote
|
||||
|
||||
blockquote {
|
||||
margin: 20px 0;
|
||||
padding-left: 1.5rem;
|
||||
border-left: 5px solid $primary-color;
|
||||
}
|
||||
|
||||
// Icon Styles
|
||||
|
||||
i {
|
||||
line-height: inherit;
|
||||
|
||||
&.left {
|
||||
float: left;
|
||||
margin-right: 15px;
|
||||
}
|
||||
&.right {
|
||||
float: right;
|
||||
margin-left: 15px;
|
||||
}
|
||||
&.tiny {
|
||||
font-size: 1rem;
|
||||
}
|
||||
&.small {
|
||||
font-size: 2rem;
|
||||
}
|
||||
&.medium {
|
||||
font-size: 4rem;
|
||||
}
|
||||
&.large {
|
||||
font-size: 6rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Images
|
||||
img.responsive-img,
|
||||
video.responsive-video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
||||
// Pagination
|
||||
|
||||
.pagination {
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
border-radius: 2px;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
height: 30px;
|
||||
|
||||
a {
|
||||
color: #444;
|
||||
display: inline-block;
|
||||
font-size: 1.2rem;
|
||||
padding: 0 10px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
&.active a { color: #fff; }
|
||||
|
||||
&.active { background-color: $primary-color; }
|
||||
|
||||
&.disabled a {
|
||||
cursor: default;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
li.pages ul li {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
@media #{$medium-and-down} {
|
||||
.pagination {
|
||||
width: 100%;
|
||||
|
||||
li.prev,
|
||||
li.next {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
li.pages {
|
||||
width: 80%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Breadcrumbs
|
||||
.breadcrumb {
|
||||
font-size: 18px;
|
||||
color: rgba(255,255,255, .7);
|
||||
|
||||
i,
|
||||
[class^="mdi-"], [class*="mdi-"],
|
||||
i.material-icons {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: '\E5CC';
|
||||
color: rgba(255,255,255, .7);
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 25px;
|
||||
margin: 0 10px 0 8px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
&:first-child:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
// Parallax
|
||||
.parallax-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 500px;
|
||||
|
||||
.parallax {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: -1;
|
||||
|
||||
img {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
transform: translate3d(0,0,0);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pushpin
|
||||
.pin-top, .pin-bottom {
|
||||
position: relative;
|
||||
}
|
||||
.pinned {
|
||||
position: fixed !important;
|
||||
}
|
||||
|
||||
/*********************
|
||||
Transition Classes
|
||||
**********************/
|
||||
|
||||
ul.staggered-list li {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
opacity: 0;
|
||||
transform-origin: 0 50%;
|
||||
}
|
||||
|
||||
|
||||
/*********************
|
||||
Media Query Classes
|
||||
**********************/
|
||||
.hide-on-small-only, .hide-on-small-and-down {
|
||||
@media #{$small-and-down} {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.hide-on-med-and-down {
|
||||
@media #{$medium-and-down} {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.hide-on-med-and-up {
|
||||
@media #{$medium-and-up} {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.hide-on-med-only {
|
||||
@media only screen and (min-width: $small-screen) and (max-width: $medium-screen) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.hide-on-large-only {
|
||||
@media #{$large-and-up} {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.hide-on-extra-large-only {
|
||||
@media #{$extra-large-and-up} {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.show-on-extra-large {
|
||||
@media #{$extra-large-and-up} {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
.show-on-large {
|
||||
@media #{$large-and-up} {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
.show-on-medium {
|
||||
@media only screen and (min-width: $small-screen) and (max-width: $medium-screen) {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
.show-on-small {
|
||||
@media #{$small-and-down} {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
.show-on-medium-and-up {
|
||||
@media #{$medium-and-up} {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
.show-on-medium-and-down {
|
||||
@media #{$medium-and-down} {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Center text on mobile
|
||||
.center-on-small-only {
|
||||
@media #{$small-and-down} {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
.page-footer {
|
||||
padding-top: 20px;
|
||||
color: $footer-font-color;
|
||||
background-color: $footer-bg-color;
|
||||
|
||||
.footer-copyright {
|
||||
overflow: hidden;
|
||||
min-height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 0px;
|
||||
color: $footer-copyright-font-color;
|
||||
background-color: $footer-copyright-bg-color;
|
||||
}
|
||||
}
|
||||
|
||||
// Tables
|
||||
table, th, td {
|
||||
border: none;
|
||||
}
|
||||
|
||||
table {
|
||||
width:100%;
|
||||
display: table;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
|
||||
&.striped {
|
||||
tr {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
> tbody {
|
||||
> tr:nth-child(odd) {
|
||||
background-color: $table-striped-color;
|
||||
}
|
||||
|
||||
> tr > td {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.highlight > tbody > tr {
|
||||
transition: background-color .25s ease;
|
||||
&:hover {
|
||||
background-color: $table-striped-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.centered {
|
||||
thead tr th, tbody tr td {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
border-bottom: 1px solid $table-border-color;
|
||||
}
|
||||
|
||||
td, th{
|
||||
padding: 15px 5px;
|
||||
display: table-cell;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
// Responsive Table
|
||||
@media #{$medium-and-down} {
|
||||
|
||||
table.responsive-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
td:empty:before {
|
||||
content: '\00a0';
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
margin: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
th { text-align: left; }
|
||||
thead {
|
||||
display: block;
|
||||
float: left;
|
||||
|
||||
tr {
|
||||
display: block;
|
||||
padding: 0 10px 0 0;
|
||||
|
||||
th::before {
|
||||
content: "\00a0";
|
||||
}
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
display: block;
|
||||
width: auto;
|
||||
position: relative;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
|
||||
tr {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
th {
|
||||
display: block;
|
||||
text-align: right;
|
||||
}
|
||||
td {
|
||||
display: block;
|
||||
min-height: 1.25em;
|
||||
text-align: left;
|
||||
}
|
||||
tr {
|
||||
border-bottom: none;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
/* sort out borders */
|
||||
thead {
|
||||
border: 0;
|
||||
border-right: 1px solid $table-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Collections
|
||||
.collection {
|
||||
margin: $element-top-margin 0 $element-bottom-margin 0;
|
||||
border: 1px solid $collection-border-color;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
.collection-item {
|
||||
background-color: $collection-bg-color;
|
||||
line-height: $collection-line-height;
|
||||
padding: 10px 20px;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid $collection-border-color;
|
||||
|
||||
// Avatar Collection
|
||||
&.avatar {
|
||||
min-height: 84px;
|
||||
padding-left: 72px;
|
||||
position: relative;
|
||||
|
||||
// Don't style circles inside preloader classes.
|
||||
&:not(.circle-clipper) > .circle,
|
||||
:not(.circle-clipper) > .circle {
|
||||
position: absolute;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
overflow: hidden;
|
||||
left: 15px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
i.circle {
|
||||
font-size: 18px;
|
||||
line-height: 42px;
|
||||
color: #fff;
|
||||
background-color: #999;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.secondary-content {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $collection-active-bg-color;
|
||||
color: $collection-active-color;
|
||||
|
||||
.secondary-content {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
a.collection-item{
|
||||
display: block;
|
||||
transition: .25s;
|
||||
color: $collection-link-color;
|
||||
&:not(.active) {
|
||||
&:hover {
|
||||
background-color: $collection-hover-bg-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.with-header {
|
||||
.collection-header {
|
||||
background-color: $collection-bg-color;
|
||||
border-bottom: 1px solid $collection-border-color;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
.collection-item {
|
||||
padding-left: 30px;
|
||||
}
|
||||
.collection-item.avatar {
|
||||
padding-left: 72px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Made less specific to allow easier overriding
|
||||
.secondary-content {
|
||||
float: right;
|
||||
color: $secondary-color;
|
||||
}
|
||||
.collapsible .collection {
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Responsive Videos
|
||||
.video-container {
|
||||
position: relative;
|
||||
padding-bottom: 56.25%;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
iframe, object, embed {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// Progress Bar
|
||||
.progress {
|
||||
position: relative;
|
||||
height: 4px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
background-color: lighten($progress-bar-color, 40%);
|
||||
border-radius: 2px;
|
||||
margin: $element-top-margin 0 $element-bottom-margin 0;
|
||||
overflow: hidden;
|
||||
.determinate {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background-color: $progress-bar-color;
|
||||
transition: width .3s linear;
|
||||
}
|
||||
.indeterminate {
|
||||
background-color: $progress-bar-color;
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: inherit;
|
||||
top: 0;
|
||||
left:0;
|
||||
bottom: 0;
|
||||
will-change: left, right;
|
||||
// Custom bezier
|
||||
animation: indeterminate 2.1s cubic-bezier(0.650, 0.815, 0.735, 0.395) infinite;
|
||||
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: inherit;
|
||||
top: 0;
|
||||
left:0;
|
||||
bottom: 0;
|
||||
will-change: left, right;
|
||||
// Custom bezier
|
||||
animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.840, 0.440, 1.000) infinite;
|
||||
animation-delay: 1.15s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@keyframes indeterminate {
|
||||
0% {
|
||||
left: -35%;
|
||||
right:100%;
|
||||
}
|
||||
60% {
|
||||
left: 100%;
|
||||
right: -90%;
|
||||
}
|
||||
100% {
|
||||
left: 100%;
|
||||
right: -90%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes indeterminate-short {
|
||||
0% {
|
||||
left: -200%;
|
||||
right: 100%;
|
||||
}
|
||||
60% {
|
||||
left: 107%;
|
||||
right: -8%;
|
||||
}
|
||||
100% {
|
||||
left: 107%;
|
||||
right: -8%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************
|
||||
Utility Classes
|
||||
*******************/
|
||||
|
||||
.hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
// Text Align
|
||||
.left-align {
|
||||
text-align: left;
|
||||
}
|
||||
.right-align {
|
||||
text-align: right
|
||||
}
|
||||
.center, .center-align {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.left {
|
||||
float: left !important;
|
||||
}
|
||||
.right {
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
// No Text Select
|
||||
.no-select {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.center-block {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.truncate {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.no-padding {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
.container {
|
||||
margin: 0 auto;
|
||||
max-width: 1280px;
|
||||
width: 90%;
|
||||
}
|
||||
@media #{$medium-and-up} {
|
||||
.container {
|
||||
width: 85%;
|
||||
}
|
||||
}
|
||||
@media #{$large-and-up} {
|
||||
.container {
|
||||
width: 70%;
|
||||
}
|
||||
}
|
||||
.col .row {
|
||||
margin-left: (-1 * $gutter-width / 2);
|
||||
margin-right: (-1 * $gutter-width / 2);
|
||||
}
|
||||
|
||||
.section {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
|
||||
&.no-pad {
|
||||
padding: 0;
|
||||
}
|
||||
&.no-pad-bot {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
&.no-pad-top {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Mixins to eliminate code repitition
|
||||
@mixin reset-offset {
|
||||
margin-left: auto;
|
||||
left: auto;
|
||||
right: auto;
|
||||
}
|
||||
@mixin grid-classes($size, $i, $perc) {
|
||||
&.offset-#{$size}#{$i} {
|
||||
margin-left: $perc;
|
||||
}
|
||||
&.pull-#{$size}#{$i} {
|
||||
right: $perc;
|
||||
}
|
||||
&.push-#{$size}#{$i} {
|
||||
left: $perc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.row {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 20px;
|
||||
|
||||
// Clear floating children
|
||||
&:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.col {
|
||||
float: left;
|
||||
box-sizing: border-box;
|
||||
padding: 0 $gutter-width / 2;
|
||||
min-height: 1px;
|
||||
|
||||
&[class*="push-"],
|
||||
&[class*="pull-"] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
$i: 1;
|
||||
@while $i <= $num-cols {
|
||||
$perc: unquote((100 / ($num-cols / $i)) + "%");
|
||||
&.s#{$i} {
|
||||
width: $perc;
|
||||
@include reset-offset;
|
||||
}
|
||||
$i: $i + 1;
|
||||
}
|
||||
|
||||
$i: 1;
|
||||
@while $i <= $num-cols {
|
||||
$perc: unquote((100 / ($num-cols / $i)) + "%");
|
||||
@include grid-classes("s", $i, $perc);
|
||||
$i: $i + 1;
|
||||
}
|
||||
|
||||
@media #{$medium-and-up} {
|
||||
|
||||
$i: 1;
|
||||
@while $i <= $num-cols {
|
||||
$perc: unquote((100 / ($num-cols / $i)) + "%");
|
||||
&.m#{$i} {
|
||||
width: $perc;
|
||||
@include reset-offset;
|
||||
}
|
||||
$i: $i + 1
|
||||
}
|
||||
|
||||
$i: 1;
|
||||
@while $i <= $num-cols {
|
||||
$perc: unquote((100 / ($num-cols / $i)) + "%");
|
||||
@include grid-classes("m", $i, $perc);
|
||||
$i: $i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media #{$large-and-up} {
|
||||
|
||||
$i: 1;
|
||||
@while $i <= $num-cols {
|
||||
$perc: unquote((100 / ($num-cols / $i)) + "%");
|
||||
&.l#{$i} {
|
||||
width: $perc;
|
||||
@include reset-offset;
|
||||
}
|
||||
$i: $i + 1;
|
||||
}
|
||||
|
||||
$i: 1;
|
||||
@while $i <= $num-cols {
|
||||
$perc: unquote((100 / ($num-cols / $i)) + "%");
|
||||
@include grid-classes("l", $i, $perc);
|
||||
$i: $i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media #{$extra-large-and-up} {
|
||||
|
||||
$i: 1;
|
||||
@while $i <= $num-cols {
|
||||
$perc: unquote((100 / ($num-cols / $i)) + "%");
|
||||
&.xl#{$i} {
|
||||
width: $perc;
|
||||
@include reset-offset;
|
||||
}
|
||||
$i: $i + 1;
|
||||
}
|
||||
|
||||
$i: 1;
|
||||
@while $i <= $num-cols {
|
||||
$perc: unquote((100 / ($num-cols / $i)) + "%");
|
||||
@include grid-classes("xl", $i, $perc);
|
||||
$i: $i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/* This is needed for some mobile phones to display the Google Icon font properly */
|
||||
.material-icons {
|
||||
text-rendering: optimizeLegibility;
|
||||
font-feature-settings: 'liga';
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
.materialboxed {
|
||||
&:hover {
|
||||
&:not(.active) {
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
||||
|
||||
display: block;
|
||||
cursor: zoom-in;
|
||||
position: relative;
|
||||
transition: opacity .4s;
|
||||
-webkit-backface-visibility: hidden;
|
||||
|
||||
&.active {
|
||||
cursor: zoom-out;
|
||||
}
|
||||
}
|
||||
|
||||
#materialbox-overlay {
|
||||
position:fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: #292929;
|
||||
z-index: 1000;
|
||||
will-change: opacity;
|
||||
}
|
||||
|
||||
.materialbox-caption {
|
||||
position: fixed;
|
||||
display: none;
|
||||
color: #fff;
|
||||
line-height: 50px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding: 0% 15%;
|
||||
height: 50px;
|
||||
z-index: 1000;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
.modal {
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@extend .z-depth-5;
|
||||
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #fafafa;
|
||||
padding: 0;
|
||||
max-height: 70%;
|
||||
width: 55%;
|
||||
margin: auto;
|
||||
overflow-y: auto;
|
||||
|
||||
border-radius: 2px;
|
||||
will-change: top, opacity;
|
||||
|
||||
@media #{$medium-and-down} {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
h1,h2,h3,h4 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 24px;
|
||||
}
|
||||
.modal-close {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
border-radius: 0 0 2px 2px;
|
||||
background-color: #fafafa;
|
||||
padding: 4px 6px;
|
||||
height: 56px;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
|
||||
.btn, .btn-flat {
|
||||
margin: 6px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
z-index: 999;
|
||||
top: -25%;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
height: 125%;
|
||||
width: 100%;
|
||||
background: #000;
|
||||
display: none;
|
||||
|
||||
will-change: opacity;
|
||||
}
|
||||
|
||||
// Modal with fixed action footer
|
||||
.modal.modal-fixed-footer {
|
||||
padding: 0;
|
||||
height: 70%;
|
||||
|
||||
.modal-content {
|
||||
position: absolute;
|
||||
height: calc(100% - 56px);
|
||||
max-height: 100%;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
border-top: 1px solid rgba(0,0,0,.1);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Modal Bottom Sheet Style
|
||||
.modal.bottom-sheet {
|
||||
top: auto;
|
||||
bottom: -100%;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
max-height: 45%;
|
||||
border-radius: 0;
|
||||
will-change: bottom, opacity;
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
nav {
|
||||
&.nav-extended {
|
||||
height: auto;
|
||||
|
||||
.nav-wrapper {
|
||||
min-height: $navbar-height-mobile;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.nav-content {
|
||||
position: relative;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
color: $navbar-font-color;
|
||||
@extend .z-depth-1;
|
||||
background-color: $primary-color;
|
||||
width: 100%;
|
||||
height: $navbar-height-mobile;
|
||||
line-height: $navbar-line-height-mobile;
|
||||
|
||||
a { color: $navbar-font-color; }
|
||||
|
||||
i,
|
||||
[class^="mdi-"], [class*="mdi-"],
|
||||
i.material-icons {
|
||||
display: block;
|
||||
font-size: 24px;
|
||||
height: $navbar-height-mobile;
|
||||
line-height: $navbar-line-height-mobile;
|
||||
}
|
||||
|
||||
.nav-wrapper {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media #{$large-and-up} {
|
||||
a.sidenav-trigger { display: none; }
|
||||
}
|
||||
|
||||
|
||||
// Collapse button
|
||||
.sidenav-trigger {
|
||||
float: left;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: $navbar-height-mobile;
|
||||
margin: 0 18px;
|
||||
|
||||
i {
|
||||
height: $navbar-height-mobile;
|
||||
line-height: $navbar-line-height-mobile;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Logo
|
||||
.brand-logo {
|
||||
position: absolute;
|
||||
color: $navbar-font-color;
|
||||
display: inline-block;
|
||||
font-size: $navbar-brand-font-size;
|
||||
padding: 0;
|
||||
|
||||
&.center {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
@media #{$medium-and-down} {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
&.left, &.right {
|
||||
padding: 0;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
&.left { left: 0.5rem; }
|
||||
&.right {
|
||||
right: 0.5rem;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&.right {
|
||||
right: 0.5rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
i,
|
||||
[class^="mdi-"], [class*="mdi-"],
|
||||
i.material-icons {
|
||||
float: left;
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Title
|
||||
.nav-title {
|
||||
display: inline-block;
|
||||
font-size: 32px;
|
||||
padding: 28px 0;
|
||||
}
|
||||
|
||||
|
||||
// Navbar Links
|
||||
ul {
|
||||
margin: 0;
|
||||
|
||||
li {
|
||||
transition: background-color .3s;
|
||||
float: left;
|
||||
padding: 0;
|
||||
|
||||
&.active {
|
||||
background-color: rgba(0,0,0,.1);
|
||||
}
|
||||
}
|
||||
a {
|
||||
transition: background-color .3s;
|
||||
font-size: $navbar-font-size;
|
||||
color: $navbar-font-color;
|
||||
display: block;
|
||||
padding: 0 15px;
|
||||
cursor: pointer;
|
||||
|
||||
&.btn, &.btn-large, &.btn-flat, &.btn-floating {
|
||||
margin-top: -2px;
|
||||
margin-left: 15px;
|
||||
margin-right: 15px;
|
||||
|
||||
& > .material-icons {
|
||||
height: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0,0,0,.1);
|
||||
}
|
||||
}
|
||||
|
||||
&.left {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
// Navbar Search Form
|
||||
form {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
|
||||
input {
|
||||
height: 100%;
|
||||
font-size: 1.2rem;
|
||||
border: none;
|
||||
padding-left: 2rem;
|
||||
|
||||
&:focus, &[type=text]:valid, &[type=password]:valid,
|
||||
&[type=email]:valid, &[type=url]:valid, &[type=date]:valid {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
i {
|
||||
color: rgba(255,255,255,.7);
|
||||
transition: color .3s;
|
||||
}
|
||||
&.active i { color: $navbar-font-color; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fixed Navbar
|
||||
.navbar-fixed {
|
||||
position: relative;
|
||||
height: $navbar-height-mobile;
|
||||
z-index: 997;
|
||||
|
||||
nav {
|
||||
position: fixed;
|
||||
}
|
||||
}
|
||||
@media #{$medium-and-up} {
|
||||
nav.nav-extended .nav-wrapper {
|
||||
min-height: $navbar-height;
|
||||
}
|
||||
nav, nav .nav-wrapper i, nav a.sidenav-trigger, nav a.sidenav-trigger i {
|
||||
height: $navbar-height;
|
||||
line-height: $navbar-line-height;
|
||||
}
|
||||
.navbar-fixed {
|
||||
height: $navbar-height;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,447 @@
|
|||
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in
|
||||
* IE on Windows Phone and in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-ms-text-size-adjust: 100%; /* 2 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
footer,
|
||||
header,
|
||||
nav,
|
||||
section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in IE.
|
||||
*/
|
||||
|
||||
figcaption,
|
||||
figure,
|
||||
main { /* 1 */
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct margin in IE 8.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Remove the gray background on active links in IE 10.
|
||||
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent; /* 1 */
|
||||
-webkit-text-decoration-skip: objects; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57- and Firefox 39-.
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font style in Android 4.3-.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct background and color in IE 9-.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background-color: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
audio,
|
||||
video {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in iOS 4-7.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10-.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the overflow in IE.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers (opinionated).
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: sans-serif; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
html [type="button"], /* 1 */
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct display in IE 9-.
|
||||
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
display: inline-block; /* 1 */
|
||||
vertical-align: baseline; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10-.
|
||||
* 2. Remove the padding in IE 10-.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-cancel-button,
|
||||
[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 {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in Edge, IE, and Firefox.
|
||||
*/
|
||||
|
||||
details, /* 1 */
|
||||
menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Scripting
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
canvas {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hidden
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10-.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
@license
|
||||
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**************************/
|
||||
/* STYLES FOR THE SPINNER */
|
||||
/**************************/
|
||||
|
||||
/*
|
||||
* Constants:
|
||||
* STROKEWIDTH = 3px
|
||||
* ARCSIZE = 270 degrees (amount of circle the arc takes up)
|
||||
* ARCTIME = 1333ms (time it takes to expand and contract arc)
|
||||
* ARCSTARTROT = 216 degrees (how much the start location of the arc
|
||||
* should rotate each time, 216 gives us a
|
||||
* 5 pointed star shape (it's 360/5 * 3).
|
||||
* For a 7 pointed star, we might do
|
||||
* 360/7 * 3 = 154.286)
|
||||
* CONTAINERWIDTH = 28px
|
||||
* SHRINK_TIME = 400ms
|
||||
*/
|
||||
|
||||
|
||||
.preloader-wrapper {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
&.small {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
&.big {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
/* duration: 360 * ARCTIME / (ARCSTARTROT + (360-ARCSIZE)) */
|
||||
-webkit-animation: container-rotate 1568ms linear infinite;
|
||||
animation: container-rotate 1568ms linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes container-rotate {
|
||||
to { -webkit-transform: rotate(360deg) }
|
||||
}
|
||||
|
||||
@keyframes container-rotate {
|
||||
to { transform: rotate(360deg) }
|
||||
}
|
||||
|
||||
.spinner-layer {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
border-color: $spinner-default-color;
|
||||
}
|
||||
|
||||
.spinner-blue,
|
||||
.spinner-blue-only {
|
||||
border-color: #4285f4;
|
||||
}
|
||||
|
||||
.spinner-red,
|
||||
.spinner-red-only {
|
||||
border-color: #db4437;
|
||||
}
|
||||
|
||||
.spinner-yellow,
|
||||
.spinner-yellow-only {
|
||||
border-color: #f4b400;
|
||||
}
|
||||
|
||||
.spinner-green,
|
||||
.spinner-green-only {
|
||||
border-color: #0f9d58;
|
||||
}
|
||||
|
||||
/**
|
||||
* IMPORTANT NOTE ABOUT CSS ANIMATION PROPERTIES (keanulee):
|
||||
*
|
||||
* iOS Safari (tested on iOS 8.1) does not handle animation-delay very well - it doesn't
|
||||
* guarantee that the animation will start _exactly_ after that value. So we avoid using
|
||||
* animation-delay and instead set custom keyframes for each color (as redundant as it
|
||||
* seems).
|
||||
*
|
||||
* We write out each animation in full (instead of separating animation-name,
|
||||
* animation-duration, etc.) because under the polyfill, Safari does not recognize those
|
||||
* specific properties properly, treats them as -webkit-animation, and overrides the
|
||||
* other animation rules. See https://github.com/Polymer/platform/issues/53.
|
||||
*/
|
||||
.active .spinner-layer.spinner-blue {
|
||||
/* durations: 4 * ARCTIME */
|
||||
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, blue-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, blue-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
}
|
||||
|
||||
.active .spinner-layer.spinner-red {
|
||||
/* durations: 4 * ARCTIME */
|
||||
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, red-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, red-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
}
|
||||
|
||||
.active .spinner-layer.spinner-yellow {
|
||||
/* durations: 4 * ARCTIME */
|
||||
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, yellow-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, yellow-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
}
|
||||
|
||||
.active .spinner-layer.spinner-green {
|
||||
/* durations: 4 * ARCTIME */
|
||||
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, green-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, green-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
}
|
||||
|
||||
.active .spinner-layer,
|
||||
.active .spinner-layer.spinner-blue-only,
|
||||
.active .spinner-layer.spinner-red-only,
|
||||
.active .spinner-layer.spinner-yellow-only,
|
||||
.active .spinner-layer.spinner-green-only {
|
||||
/* durations: 4 * ARCTIME */
|
||||
opacity: 1;
|
||||
-webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fill-unfill-rotate {
|
||||
12.5% { -webkit-transform: rotate(135deg); } /* 0.5 * ARCSIZE */
|
||||
25% { -webkit-transform: rotate(270deg); } /* 1 * ARCSIZE */
|
||||
37.5% { -webkit-transform: rotate(405deg); } /* 1.5 * ARCSIZE */
|
||||
50% { -webkit-transform: rotate(540deg); } /* 2 * ARCSIZE */
|
||||
62.5% { -webkit-transform: rotate(675deg); } /* 2.5 * ARCSIZE */
|
||||
75% { -webkit-transform: rotate(810deg); } /* 3 * ARCSIZE */
|
||||
87.5% { -webkit-transform: rotate(945deg); } /* 3.5 * ARCSIZE */
|
||||
to { -webkit-transform: rotate(1080deg); } /* 4 * ARCSIZE */
|
||||
}
|
||||
|
||||
@keyframes fill-unfill-rotate {
|
||||
12.5% { transform: rotate(135deg); } /* 0.5 * ARCSIZE */
|
||||
25% { transform: rotate(270deg); } /* 1 * ARCSIZE */
|
||||
37.5% { transform: rotate(405deg); } /* 1.5 * ARCSIZE */
|
||||
50% { transform: rotate(540deg); } /* 2 * ARCSIZE */
|
||||
62.5% { transform: rotate(675deg); } /* 2.5 * ARCSIZE */
|
||||
75% { transform: rotate(810deg); } /* 3 * ARCSIZE */
|
||||
87.5% { transform: rotate(945deg); } /* 3.5 * ARCSIZE */
|
||||
to { transform: rotate(1080deg); } /* 4 * ARCSIZE */
|
||||
}
|
||||
|
||||
@-webkit-keyframes blue-fade-in-out {
|
||||
from { opacity: 1; }
|
||||
25% { opacity: 1; }
|
||||
26% { opacity: 0; }
|
||||
89% { opacity: 0; }
|
||||
90% { opacity: 1; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes blue-fade-in-out {
|
||||
from { opacity: 1; }
|
||||
25% { opacity: 1; }
|
||||
26% { opacity: 0; }
|
||||
89% { opacity: 0; }
|
||||
90% { opacity: 1; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
@-webkit-keyframes red-fade-in-out {
|
||||
from { opacity: 0; }
|
||||
15% { opacity: 0; }
|
||||
25% { opacity: 1; }
|
||||
50% { opacity: 1; }
|
||||
51% { opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes red-fade-in-out {
|
||||
from { opacity: 0; }
|
||||
15% { opacity: 0; }
|
||||
25% { opacity: 1; }
|
||||
50% { opacity: 1; }
|
||||
51% { opacity: 0; }
|
||||
}
|
||||
|
||||
@-webkit-keyframes yellow-fade-in-out {
|
||||
from { opacity: 0; }
|
||||
40% { opacity: 0; }
|
||||
50% { opacity: 1; }
|
||||
75% { opacity: 1; }
|
||||
76% { opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes yellow-fade-in-out {
|
||||
from { opacity: 0; }
|
||||
40% { opacity: 0; }
|
||||
50% { opacity: 1; }
|
||||
75% { opacity: 1; }
|
||||
76% { opacity: 0; }
|
||||
}
|
||||
|
||||
@-webkit-keyframes green-fade-in-out {
|
||||
from { opacity: 0; }
|
||||
65% { opacity: 0; }
|
||||
75% { opacity: 1; }
|
||||
90% { opacity: 1; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes green-fade-in-out {
|
||||
from { opacity: 0; }
|
||||
65% { opacity: 0; }
|
||||
75% { opacity: 1; }
|
||||
90% { opacity: 1; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch the gap that appear between the two adjacent div.circle-clipper while the
|
||||
* spinner is rotating (appears on Chrome 38, Safari 7.1, and IE 11).
|
||||
*/
|
||||
.gap-patch {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 45%;
|
||||
width: 10%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
border-color: inherit;
|
||||
}
|
||||
|
||||
.gap-patch .circle {
|
||||
width: 1000%;
|
||||
left: -450%;
|
||||
}
|
||||
|
||||
.circle-clipper {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
border-color: inherit;
|
||||
|
||||
.circle {
|
||||
width: 200%;
|
||||
height: 100%;
|
||||
border-width: 3px; /* STROKEWIDTH */
|
||||
border-style: solid;
|
||||
border-color: inherit;
|
||||
border-bottom-color: transparent !important;
|
||||
border-radius: 50%;
|
||||
-webkit-animation: none;
|
||||
animation: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
&.left .circle {
|
||||
left: 0;
|
||||
border-right-color: transparent !important;
|
||||
-webkit-transform: rotate(129deg);
|
||||
transform: rotate(129deg);
|
||||
}
|
||||
&.right .circle {
|
||||
left: -100%;
|
||||
border-left-color: transparent !important;
|
||||
-webkit-transform: rotate(-129deg);
|
||||
transform: rotate(-129deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.active .circle-clipper.left .circle {
|
||||
/* duration: ARCTIME */
|
||||
-webkit-animation: left-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
animation: left-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
}
|
||||
|
||||
.active .circle-clipper.right .circle {
|
||||
/* duration: ARCTIME */
|
||||
-webkit-animation: right-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
animation: right-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
|
||||
}
|
||||
|
||||
@-webkit-keyframes left-spin {
|
||||
from { -webkit-transform: rotate(130deg); }
|
||||
50% { -webkit-transform: rotate(-5deg); }
|
||||
to { -webkit-transform: rotate(130deg); }
|
||||
}
|
||||
|
||||
@keyframes left-spin {
|
||||
from { transform: rotate(130deg); }
|
||||
50% { transform: rotate(-5deg); }
|
||||
to { transform: rotate(130deg); }
|
||||
}
|
||||
|
||||
@-webkit-keyframes right-spin {
|
||||
from { -webkit-transform: rotate(-130deg); }
|
||||
50% { -webkit-transform: rotate(5deg); }
|
||||
to { -webkit-transform: rotate(-130deg); }
|
||||
}
|
||||
|
||||
@keyframes right-spin {
|
||||
from { transform: rotate(-130deg); }
|
||||
50% { transform: rotate(5deg); }
|
||||
to { transform: rotate(-130deg); }
|
||||
}
|
||||
|
||||
#spinnerContainer.cooldown {
|
||||
/* duration: SHRINK_TIME */
|
||||
-webkit-animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0.0, 0.2, 1);
|
||||
animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0.0, 0.2, 1);
|
||||
}
|
||||
|
||||
@-webkit-keyframes fade-out {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes fade-out {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
.pulse {
|
||||
&::before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: inherit;
|
||||
border-radius: inherit;
|
||||
transition: opacity .3s, transform .3s;
|
||||
animation: pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@keyframes pulse-animation {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0;
|
||||
transform: scale(1.5);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: scale(1.5);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
.sidenav {
|
||||
position: fixed;
|
||||
width: $sidenav-width;
|
||||
left: 0;
|
||||
top: 0;
|
||||
margin: 0;
|
||||
transform: translateX(-100%);
|
||||
height: 100%;
|
||||
height: calc(100% + 60px);
|
||||
height: -moz-calc(100%); //Temporary Firefox Fix
|
||||
padding-bottom: 60px;
|
||||
background-color: $sidenav-bg-color;
|
||||
z-index: 999;
|
||||
overflow-y: auto;
|
||||
will-change: transform;
|
||||
backface-visibility: hidden;
|
||||
transform: translateX(-105%);
|
||||
|
||||
@extend .z-depth-1;
|
||||
|
||||
// Right Align
|
||||
&.right-aligned {
|
||||
right: 0;
|
||||
transform: translateX(105%);
|
||||
left: auto;
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.collapsible {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
li {
|
||||
float: none;
|
||||
line-height: $sidenav-line-height;
|
||||
|
||||
&.active { background-color: rgba(0,0,0,.05); }
|
||||
}
|
||||
|
||||
li > a {
|
||||
color: $sidenav-font-color;
|
||||
display: block;
|
||||
font-size: $sidenav-font-size;
|
||||
font-weight: 500;
|
||||
height: $sidenav-item-height;
|
||||
line-height: $sidenav-line-height;
|
||||
padding: 0 ($sidenav-padding * 2);
|
||||
|
||||
&:hover { background-color: rgba(0,0,0,.05);}
|
||||
|
||||
&.btn, &.btn-large, &.btn-flat, &.btn-floating {
|
||||
margin: 10px 15px;
|
||||
}
|
||||
|
||||
&.btn,
|
||||
&.btn-large,
|
||||
&.btn-floating { color: $button-raised-color; }
|
||||
&.btn-flat { color: $button-flat-color; }
|
||||
|
||||
&.btn:hover,
|
||||
&.btn-large:hover { background-color: lighten($button-raised-background, 5%); }
|
||||
&.btn-floating:hover { background-color: $button-raised-background; }
|
||||
|
||||
& > i,
|
||||
& > [class^="mdi-"], li > a > [class*="mdi-"],
|
||||
& > i.material-icons {
|
||||
float: left;
|
||||
height: $sidenav-item-height;
|
||||
line-height: $sidenav-line-height;
|
||||
margin: 0 ($sidenav-padding * 2) 0 0;
|
||||
width: $sidenav-item-height / 2;
|
||||
color: rgba(0,0,0,.54);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.divider {
|
||||
margin: ($sidenav-padding / 2) 0 0 0;
|
||||
}
|
||||
|
||||
.subheader {
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
cursor: initial;
|
||||
pointer-events: none;
|
||||
color: rgba(0,0,0,.54);
|
||||
font-size: $sidenav-font-size;
|
||||
font-weight: 500;
|
||||
line-height: $sidenav-line-height;
|
||||
}
|
||||
|
||||
.user-view {
|
||||
position: relative;
|
||||
padding: ($sidenav-padding * 2) ($sidenav-padding * 2) 0;
|
||||
margin-bottom: $sidenav-padding / 2;
|
||||
|
||||
& > a {
|
||||
&:hover { background-color: transparent; }
|
||||
height: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.background {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.circle, .name, .email {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.circle {
|
||||
height: 64px;
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
.name,
|
||||
.email {
|
||||
font-size: $sidenav-font-size;
|
||||
line-height: $sidenav-line-height / 2;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-top: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.email {
|
||||
padding-bottom: 16px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Touch interaction
|
||||
.drag-target {
|
||||
// Right Align
|
||||
&.right-aligned {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
height: 100%;
|
||||
width: 10px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 998;
|
||||
}
|
||||
|
||||
|
||||
// Fixed Sidenav shown
|
||||
.sidenav.sidenav-fixed {
|
||||
// Right Align
|
||||
&.right-aligned {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
left: 0;
|
||||
transform: translateX(0);
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
// Fixed Sidenav hide on smaller
|
||||
@media #{$medium-and-down} {
|
||||
.sidenav {
|
||||
&.sidenav-fixed {
|
||||
transform: translateX(-105%);
|
||||
|
||||
&.right-aligned {
|
||||
transform: translateX(105%);
|
||||
}
|
||||
}
|
||||
|
||||
> a {
|
||||
padding: 0 $sidenav-padding;
|
||||
}
|
||||
|
||||
.user-view {
|
||||
padding: $sidenav-padding $sidenav-padding 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.sidenav .collapsible-body > ul:not(.collapsible) > li.active,
|
||||
.sidenav.sidenav-fixed .collapsible-body > ul:not(.collapsible) > li.active {
|
||||
background-color: $primary-color;
|
||||
a {
|
||||
color: $sidenav-bg-color;
|
||||
}
|
||||
}
|
||||
.sidenav .collapsible-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
.sidenav-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
height: 120vh;
|
||||
background-color: rgba(0,0,0,.5);
|
||||
z-index: 997;
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
.slider {
|
||||
position: relative;
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
|
||||
// Fullscreen slider
|
||||
&.fullscreen {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
ul.slides {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
ul.indicators {
|
||||
z-index: 2;
|
||||
bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.slides {
|
||||
background-color: $slider-bg-color;
|
||||
margin: 0;
|
||||
height: 400px;
|
||||
|
||||
li {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: inherit;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.caption {
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
top: 15%;
|
||||
left: 15%;
|
||||
width: 70%;
|
||||
opacity: 0;
|
||||
|
||||
p { color: $slider-bg-color-light; }
|
||||
}
|
||||
|
||||
&.active {
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.indicators {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
|
||||
.indicator-item {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin: 0 12px;
|
||||
background-color: $slider-bg-color-light;
|
||||
|
||||
transition: background-color .3s;
|
||||
border-radius: 50%;
|
||||
|
||||
&.active {
|
||||
background-color: $slider-indicator-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/***************
|
||||
Nav List
|
||||
***************/
|
||||
.table-of-contents {
|
||||
&.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 2px 0;
|
||||
}
|
||||
a {
|
||||
display: inline-block;
|
||||
font-weight: 300;
|
||||
color: #757575;
|
||||
padding-left: 16px;
|
||||
height: 1.5rem;
|
||||
line-height: 1.5rem;
|
||||
letter-spacing: .4;
|
||||
display: inline-block;
|
||||
|
||||
&:hover {
|
||||
color: lighten(#757575, 20%);
|
||||
padding-left: 15px;
|
||||
border-left: 1px solid $primary-color;
|
||||
}
|
||||
&.active {
|
||||
font-weight: 500;
|
||||
padding-left: 14px;
|
||||
border-left: 2px solid $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
.tabs {
|
||||
&.tabs-transparent {
|
||||
background-color: transparent;
|
||||
|
||||
.tab a,
|
||||
.tab.disabled a,
|
||||
.tab.disabled a:hover {
|
||||
color: rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
.tab a:hover,
|
||||
.tab a.active {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&.tabs-fixed-width {
|
||||
display: flex;
|
||||
|
||||
.tab {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
position: relative;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
height: 48px;
|
||||
width: 100%;
|
||||
background-color: $tabs-bg-color;
|
||||
margin: 0 auto;
|
||||
white-space: nowrap;
|
||||
|
||||
.tab {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
line-height: 48px;
|
||||
height: 48px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
|
||||
a {
|
||||
&:focus,
|
||||
&:focus.active {
|
||||
background-color: transparentize($tabs-underline-color, .8);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background-color: transparent;
|
||||
color: $tabs-text-color;
|
||||
}
|
||||
|
||||
color: rgba($tabs-text-color, .7);
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 24px;
|
||||
font-size: 14px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
transition: color .28s ease, background-color .28s ease;
|
||||
}
|
||||
|
||||
&.disabled a,
|
||||
&.disabled a:hover {
|
||||
color: rgba($tabs-text-color, .4);
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
.indicator {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: 2px;
|
||||
background-color: $tabs-underline-color;
|
||||
will-change: left, right;
|
||||
}
|
||||
}
|
||||
|
||||
// Fixed Sidenav hide on smaller
|
||||
@media #{$medium-and-down} {
|
||||
.tabs {
|
||||
display: flex;
|
||||
|
||||
.tab {
|
||||
flex-grow: 1;
|
||||
|
||||
a {
|
||||
padding: 0 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
.tap-target-wrapper {
|
||||
width: 800px;
|
||||
height: 800px;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
visibility: hidden;
|
||||
transition: visibility 0s .3s;
|
||||
}
|
||||
|
||||
.tap-target-wrapper.open {
|
||||
visibility: visible;
|
||||
transition: visibility 0s;
|
||||
|
||||
.tap-target {
|
||||
transform: scale(1);
|
||||
opacity: .95;
|
||||
transition:
|
||||
transform .3s cubic-bezier(.42,0,.58,1),
|
||||
opacity .3s cubic-bezier(.42,0,.58,1);
|
||||
}
|
||||
|
||||
.tap-target-wave::before {
|
||||
transform: scale(1);
|
||||
}
|
||||
.tap-target-wave::after {
|
||||
visibility: visible;
|
||||
animation: pulse-animation 1s cubic-bezier(0.24, 0, 0.38, 1) infinite;
|
||||
transition:
|
||||
opacity .3s,
|
||||
transform .3s,
|
||||
visibility 0s 1s;
|
||||
}
|
||||
}
|
||||
|
||||
.tap-target {
|
||||
position: absolute;
|
||||
font-size: 1rem;
|
||||
border-radius: 50%;
|
||||
background-color: $primary-color;
|
||||
box-shadow: 0 20px 20px 0 rgba(0,0,0,0.14), 0 10px 50px 0 rgba(0,0,0,0.12), 0 30px 10px -20px rgba(0,0,0,0.2);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
transition:
|
||||
transform .3s cubic-bezier(.42,0,.58,1),
|
||||
opacity .3s cubic-bezier(.42,0,.58,1);
|
||||
}
|
||||
|
||||
.tap-target-content {
|
||||
position: relative;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.tap-target-wave {
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
&::before {
|
||||
transform: scale(0);
|
||||
transition: transform .3s;
|
||||
}
|
||||
&::after {
|
||||
visibility: hidden;
|
||||
transition:
|
||||
opacity .3s,
|
||||
transform .3s,
|
||||
visibility 0s;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
z-index: 10001;
|
||||
}
|
||||
|
||||
.tap-target-origin {
|
||||
&:not(.btn),
|
||||
&:not(.btn):hover {
|
||||
background: none;
|
||||
}
|
||||
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
|
||||
z-index: 10002;
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
.tap-target, .tap-target-wrapper {
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
/* Timepicker Containers */
|
||||
.timepicker-modal {
|
||||
max-width: 325px;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.timepicker-container.modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
|
||||
/* Clock Digital Display */
|
||||
.timepicker-digital-display {
|
||||
flex: 1 auto;
|
||||
background-color: $secondary-color;
|
||||
padding: 10px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.timepicker-text-container {
|
||||
font-size: 4rem;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-weight: 400;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.timepicker-span-hours,
|
||||
.timepicker-span-minutes,
|
||||
.timepicker-span-am-pm div {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.timepicker-span-hours {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.timepicker-span-minutes {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.timepicker-display-am-pm {
|
||||
font-size: 1.3rem;
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
bottom: 1rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
|
||||
/* Analog Clock Display */
|
||||
.timepicker-analog-display {
|
||||
flex: 2.5 auto;
|
||||
}
|
||||
|
||||
.timepicker-plate {
|
||||
background-color: $timepicker-clock-plate-bg;
|
||||
border-radius: 50%;
|
||||
width: 270px;
|
||||
height: 270px;
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
margin: auto;
|
||||
margin-top: 25px;
|
||||
margin-bottom: 5px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.timepicker-canvas,
|
||||
.timepicker-dial {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.timepicker-minutes {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.timepicker-tick {
|
||||
border-radius: 50%;
|
||||
color: $timepicker-clock-color;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.timepicker-tick.active,
|
||||
.timepicker-tick:hover {
|
||||
background-color: transparentize($secondary-color, .75);
|
||||
}
|
||||
.timepicker-dial {
|
||||
transition: transform 350ms, opacity 350ms;
|
||||
}
|
||||
.timepicker-dial-out {
|
||||
&.timepicker-hours {
|
||||
transform: scale(1.1, 1.1);
|
||||
}
|
||||
|
||||
&.timepicker-minutes {
|
||||
transform: scale(.8, .8);
|
||||
}
|
||||
|
||||
opacity: 0;
|
||||
}
|
||||
.timepicker-canvas {
|
||||
transition: opacity 175ms;
|
||||
|
||||
line {
|
||||
stroke: $secondary-color;
|
||||
stroke-width: 4;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
}
|
||||
.timepicker-canvas-out {
|
||||
opacity: 0.25;
|
||||
}
|
||||
.timepicker-canvas-bearing {
|
||||
stroke: none;
|
||||
fill: $secondary-color;
|
||||
}
|
||||
.timepicker-canvas-bg {
|
||||
stroke: none;
|
||||
fill: $secondary-color;
|
||||
}
|
||||
|
||||
|
||||
/* Footer */
|
||||
.timepicker-footer {
|
||||
margin: 0 auto;
|
||||
padding: 5px 1rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.timepicker-clear {
|
||||
color: $error-color;
|
||||
}
|
||||
|
||||
.timepicker-close {
|
||||
color: $secondary-color;
|
||||
}
|
||||
|
||||
.timepicker-clear,
|
||||
.timepicker-close {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
/* Media Queries */
|
||||
@media #{$medium-and-up} {
|
||||
.timepicker-modal {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.timepicker-container.modal-content {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.timepicker-text-container {
|
||||
top: 32%;
|
||||
}
|
||||
|
||||
.timepicker-display-am-pm {
|
||||
position: relative;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
text-align: center;
|
||||
margin-top: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
#toast-container {
|
||||
display:block;
|
||||
position: fixed;
|
||||
z-index: 10000;
|
||||
|
||||
@media #{$small-and-down} {
|
||||
min-width: 100%;
|
||||
bottom: 0%;
|
||||
}
|
||||
@media #{$medium-only} {
|
||||
left: 5%;
|
||||
bottom: 7%;
|
||||
max-width: 90%;
|
||||
}
|
||||
@media #{$large-and-up} {
|
||||
top: 10%;
|
||||
right: 7%;
|
||||
max-width: 86%;
|
||||
}
|
||||
}
|
||||
|
||||
.toast {
|
||||
@extend .z-depth-1;
|
||||
border-radius: 2px;
|
||||
top: 35px;
|
||||
width: auto;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
max-width:100%;
|
||||
height: auto;
|
||||
min-height: $toast-height;
|
||||
line-height: 1.5em;
|
||||
background-color: $toast-color;
|
||||
padding: 10px 25px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 300;
|
||||
color: $toast-text-color;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: default;
|
||||
|
||||
.toast-action {
|
||||
color: $toast-action-color;
|
||||
font-weight: 500;
|
||||
margin-right: -25px;
|
||||
margin-left: 3rem;
|
||||
}
|
||||
|
||||
&.rounded{
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
@media #{$small-and-down} {
|
||||
width: 100%;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
.material-tooltip {
|
||||
padding: 10px 8px;
|
||||
font-size: 1rem;
|
||||
z-index: 2000;
|
||||
background-color: transparent;
|
||||
border-radius: 2px;
|
||||
color: #fff;
|
||||
min-height: 36px;
|
||||
line-height: 120%;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
max-width: calc(100% - 4px);
|
||||
overflow: hidden;
|
||||
left: 0;
|
||||
top: 0;
|
||||
pointer-events: none;
|
||||
visibility: hidden;
|
||||
background-color: #323232;
|
||||
}
|
||||
|
||||
.backdrop {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
height: 7px;
|
||||
width: 14px;
|
||||
border-radius: 0 0 50% 50%;
|
||||
background-color: #323232;
|
||||
z-index: -1;
|
||||
transform-origin: 50% 0%;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Scale transition
|
||||
.scale-transition {
|
||||
&.scale-out {
|
||||
transform: scale(0);
|
||||
transition: transform .2s !important;
|
||||
}
|
||||
|
||||
&.scale-in {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
transition: transform .3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
html{
|
||||
line-height: 1.5;
|
||||
|
||||
@media only screen and (min-width: 0) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: $medium-screen) {
|
||||
font-size: 14.5px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: $large-screen) {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
font-family: $font-stack;
|
||||
font-weight: normal;
|
||||
color: $off-black;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: 400;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
// Header Styles
|
||||
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { font-weight: inherit; }
|
||||
h1 { font-size: $h1-fontsize; line-height: 110%; margin: ($h1-fontsize / 1.5) 0 ($h1-fontsize / 2.5) 0;}
|
||||
h2 { font-size: $h2-fontsize; line-height: 110%; margin: ($h2-fontsize / 1.5) 0 ($h2-fontsize / 2.5) 0;}
|
||||
h3 { font-size: $h3-fontsize; line-height: 110%; margin: ($h3-fontsize / 1.5) 0 ($h3-fontsize / 2.5) 0;}
|
||||
h4 { font-size: $h4-fontsize; line-height: 110%; margin: ($h4-fontsize / 1.5) 0 ($h4-fontsize / 2.5) 0;}
|
||||
h5 { font-size: $h5-fontsize; line-height: 110%; margin: ($h5-fontsize / 1.5) 0 ($h5-fontsize / 2.5) 0;}
|
||||
h6 { font-size: $h6-fontsize; line-height: 110%; margin: ($h6-fontsize / 1.5) 0 ($h6-fontsize / 2.5) 0;}
|
||||
|
||||
// Text Styles
|
||||
em { font-style: italic; }
|
||||
strong { font-weight: 500; }
|
||||
small { font-size: 75%; }
|
||||
.light { font-weight: 300; }
|
||||
.thin { font-weight: 200; }
|
||||
|
||||
|
||||
.flow-text{
|
||||
$i: 0;
|
||||
@while $i <= $intervals {
|
||||
@media only screen and (min-width : 360 + ($i * $interval-size)) {
|
||||
font-size: 1.2rem * (1 + (.02 * $i));
|
||||
}
|
||||
$i: $i + 1;
|
||||
}
|
||||
|
||||
// Handle below 360px screen
|
||||
@media only screen and (max-width: 360px) {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,349 @@
|
|||
// ==========================================================================
|
||||
// Materialize variables
|
||||
// ==========================================================================
|
||||
//
|
||||
// Table of Contents:
|
||||
//
|
||||
// 1. Colors
|
||||
// 2. Badges
|
||||
// 3. Buttons
|
||||
// 4. Cards
|
||||
// 5. Carousel
|
||||
// 6. Collapsible
|
||||
// 7. Chips
|
||||
// 8. Date + Time Picker
|
||||
// 9. Dropdown
|
||||
// 10. Forms
|
||||
// 11. Global
|
||||
// 12. Grid
|
||||
// 13. Navigation Bar
|
||||
// 14. Side Navigation
|
||||
// 15. Photo Slider
|
||||
// 16. Spinners | Loaders
|
||||
// 17. Tabs
|
||||
// 18. Tables
|
||||
// 19. Toasts
|
||||
// 20. Typography
|
||||
// 21. Footer
|
||||
// 22. Flow Text
|
||||
// 23. Collections
|
||||
// 24. Progress Bar
|
||||
|
||||
|
||||
|
||||
// 1. Colors
|
||||
// ==========================================================================
|
||||
|
||||
$primary-color: color("materialize-red", "lighten-2") !default;
|
||||
$primary-color-light: lighten($primary-color, 15%) !default;
|
||||
$primary-color-dark: darken($primary-color, 15%) !default;
|
||||
|
||||
$secondary-color: color("teal", "lighten-1") !default;
|
||||
$success-color: color("green", "base") !default;
|
||||
$error-color: color("red", "base") !default;
|
||||
$link-color: color("light-blue", "darken-1") !default;
|
||||
|
||||
|
||||
// 2. Badges
|
||||
// ==========================================================================
|
||||
|
||||
$badge-bg-color: $secondary-color !default;
|
||||
$badge-height: 22px !default;
|
||||
|
||||
|
||||
// 3. Buttons
|
||||
// ==========================================================================
|
||||
|
||||
// Shared styles
|
||||
$button-border: none !default;
|
||||
$button-background-focus: lighten($secondary-color, 4%) !default;
|
||||
$button-font-size: 14px !default;
|
||||
$button-icon-font-size: 1.3rem !default;
|
||||
$button-height: 36px !default;
|
||||
$button-padding: 0 16px !default;
|
||||
$button-radius: 2px !default;
|
||||
|
||||
// Disabled styles
|
||||
$button-disabled-background: #DFDFDF !default;
|
||||
$button-disabled-color: #9F9F9F !default;
|
||||
|
||||
// Raised buttons
|
||||
$button-raised-background: $secondary-color !default;
|
||||
$button-raised-background-hover: lighten($button-raised-background, 5%) !default;
|
||||
$button-raised-color: #fff !default;
|
||||
|
||||
// Large buttons
|
||||
$button-large-font-size: 15px !default;
|
||||
$button-large-icon-font-size: 1.6rem !default;
|
||||
$button-large-height: $button-height * 1.5 !default;
|
||||
$button-floating-large-size: 56px !default;
|
||||
|
||||
// Small buttons
|
||||
$button-small-font-size: 13px !default;
|
||||
$button-small-icon-font-size: 1.2rem !default;
|
||||
$button-small-height: $button-height * .9 !default;
|
||||
$button-floating-small-size: $button-height * .9 !default;
|
||||
|
||||
// Flat buttons
|
||||
$button-flat-color: #343434 !default;
|
||||
$button-flat-disabled-color: lighten(#999, 10%) !default;
|
||||
|
||||
// Floating buttons
|
||||
$button-floating-background: $secondary-color !default;
|
||||
$button-floating-background-hover: $button-floating-background !default;
|
||||
$button-floating-color: #fff !default;
|
||||
$button-floating-size: 40px !default;
|
||||
$button-floating-radius: 50% !default;
|
||||
|
||||
|
||||
// 4. Cards
|
||||
// ==========================================================================
|
||||
|
||||
$card-padding: 24px !default;
|
||||
$card-bg-color: #fff !default;
|
||||
$card-link-color: color("orange", "accent-2") !default;
|
||||
$card-link-color-light: lighten($card-link-color, 20%) !default;
|
||||
|
||||
|
||||
// 5. Carousel
|
||||
// ==========================================================================
|
||||
|
||||
$carousel-height: 400px !default;
|
||||
$carousel-item-height: $carousel-height / 2 !default;
|
||||
$carousel-item-width: $carousel-item-height !default;
|
||||
|
||||
|
||||
// 6. Collapsible
|
||||
// ==========================================================================
|
||||
|
||||
$collapsible-height: 3rem !default;
|
||||
$collapsible-line-height: $collapsible-height !default;
|
||||
$collapsible-header-color: #fff !default;
|
||||
$collapsible-border-color: #ddd !default;
|
||||
|
||||
|
||||
// 7. Chips
|
||||
// ==========================================================================
|
||||
|
||||
$chip-bg-color: #e4e4e4 !default;
|
||||
$chip-border-color: #9e9e9e !default;
|
||||
$chip-selected-color: #26a69a !default;
|
||||
$chip-margin: 5px !default;
|
||||
|
||||
|
||||
// 8. Date + Time Picker
|
||||
// ==========================================================================
|
||||
|
||||
$datepicker-display-font-size: 2.8rem;
|
||||
$datepicker-calendar-header-color: #999;
|
||||
$datepicker-weekday-color: rgba(0, 0, 0, .87) !default;
|
||||
$datepicker-weekday-bg: darken($secondary-color, 7%) !default;
|
||||
$datepicker-date-bg: $secondary-color !default;
|
||||
$datepicker-year: rgba(255, 255, 255, .7) !default;
|
||||
$datepicker-focus: rgba(0,0,0, .05) !default;
|
||||
$datepicker-selected: $secondary-color !default;
|
||||
$datepicker-selected-outfocus: desaturate(lighten($secondary-color, 35%), 15%) !default;
|
||||
$datepicker-day-focus: transparentize(desaturate($secondary-color, 5%), .75) !default;
|
||||
$datepicker-disabled-day-color: rgba(0, 0, 0, .3) !default;
|
||||
|
||||
$timepicker-clock-color: rgba(0, 0, 0, .87) !default;
|
||||
$timepicker-clock-plate-bg: #eee !default;
|
||||
|
||||
|
||||
// 9. Dropdown
|
||||
// ==========================================================================
|
||||
|
||||
$dropdown-bg-color: #fff !default;
|
||||
$dropdown-hover-bg-color: #eee !default;
|
||||
$dropdown-color: $secondary-color !default;
|
||||
$dropdown-item-height: 50px !default;
|
||||
|
||||
|
||||
// 10. Forms
|
||||
// ==========================================================================
|
||||
|
||||
// Text Inputs + Textarea
|
||||
$input-height: 3rem !default;
|
||||
$input-border-color: color("grey", "base") !default;
|
||||
$input-border: 1px solid $input-border-color !default;
|
||||
$input-background: #fff !default;
|
||||
$input-error-color: $error-color !default;
|
||||
$input-success-color: $success-color !default;
|
||||
$input-focus-color: $secondary-color !default;
|
||||
$input-font-size: 16px !default;
|
||||
$input-margin-bottom: 8px;
|
||||
$input-margin: 0 0 $input-margin-bottom 0 !default;
|
||||
$input-padding: 0 !default;
|
||||
$label-font-size: .8rem !default;
|
||||
$input-disabled-color: rgba(0,0,0, .42) !default;
|
||||
$input-disabled-solid-color: #949494 !default;
|
||||
$input-disabled-border: 1px dotted $input-disabled-color !default;
|
||||
$input-invalid-border: 1px solid $input-error-color !default;
|
||||
$input-icon-size: 2rem;
|
||||
$placeholder-text-color: lighten($input-border-color, 20%) !default;
|
||||
|
||||
// Radio Buttons
|
||||
$radio-fill-color: $secondary-color !default;
|
||||
$radio-empty-color: #5a5a5a !default;
|
||||
$radio-border: 2px solid $radio-fill-color !default;
|
||||
|
||||
// Range
|
||||
$range-height: 14px !default;
|
||||
$range-width: 14px !default;
|
||||
$track-height: 3px !default;
|
||||
|
||||
// Select
|
||||
$select-border: 1px solid #f2f2f2 !default;
|
||||
$select-background: rgba(255, 255, 255, 0.90) !default;
|
||||
$select-focus: 1px solid lighten($secondary-color, 47%) !default;
|
||||
$select-option-hover: rgba(0,0,0,.08) !default;
|
||||
$select-option-focus: rgba(0,0,0,.08) !default;
|
||||
$select-option-selected: rgba(0,0,0,.03) !default;
|
||||
$select-padding: 5px !default;
|
||||
$select-radius: 2px !default;
|
||||
$select-disabled-color: rgba(0,0,0,.3) !default;
|
||||
|
||||
// Switches
|
||||
$switch-bg-color: $secondary-color !default;
|
||||
$switch-checked-lever-bg: desaturate(lighten($switch-bg-color, 25%), 25%) !default;
|
||||
$switch-unchecked-bg: #F1F1F1 !default;
|
||||
$switch-unchecked-lever-bg: rgba(0,0,0,.38) !default;
|
||||
$switch-radius: 15px !default;
|
||||
|
||||
|
||||
// 11. Global
|
||||
// ==========================================================================
|
||||
|
||||
// Media Query Ranges
|
||||
$small-screen-up: 601px !default;
|
||||
$medium-screen-up: 993px !default;
|
||||
$large-screen-up: 1201px !default;
|
||||
$small-screen: 600px !default;
|
||||
$medium-screen: 992px !default;
|
||||
$large-screen: 1200px !default;
|
||||
|
||||
$medium-and-up: "only screen and (min-width : #{$small-screen-up})" !default;
|
||||
$large-and-up: "only screen and (min-width : #{$medium-screen-up})" !default;
|
||||
$extra-large-and-up: "only screen and (min-width : #{$large-screen-up})" !default;
|
||||
$small-and-down: "only screen and (max-width : #{$small-screen})" !default;
|
||||
$medium-and-down: "only screen and (max-width : #{$medium-screen})" !default;
|
||||
$medium-only: "only screen and (min-width : #{$small-screen-up}) and (max-width : #{$medium-screen})" !default;
|
||||
|
||||
|
||||
// 12. Grid
|
||||
// ==========================================================================
|
||||
|
||||
$num-cols: 12 !default;
|
||||
$gutter-width: 1.5rem !default;
|
||||
$element-top-margin: $gutter-width/3 !default;
|
||||
$element-bottom-margin: ($gutter-width*2)/3 !default;
|
||||
|
||||
|
||||
// 13. Navigation Bar
|
||||
// ==========================================================================
|
||||
|
||||
$navbar-height: 64px !default;
|
||||
$navbar-line-height: $navbar-height !default;
|
||||
$navbar-height-mobile: 56px !default;
|
||||
$navbar-line-height-mobile: $navbar-height-mobile !default;
|
||||
$navbar-font-size: 1rem !default;
|
||||
$navbar-font-color: #fff !default;
|
||||
$navbar-brand-font-size: 2.1rem !default;
|
||||
|
||||
// 14. Side Navigation
|
||||
// ==========================================================================
|
||||
|
||||
$sidenav-width: 300px !default;
|
||||
$sidenav-font-size: 14px !default;
|
||||
$sidenav-font-color: rgba(0,0,0,.87) !default;
|
||||
$sidenav-bg-color: #fff !default;
|
||||
$sidenav-padding: 16px !default;
|
||||
$sidenav-item-height: 48px !default;
|
||||
$sidenav-line-height: $sidenav-item-height !default;
|
||||
|
||||
|
||||
// 15. Photo Slider
|
||||
// ==========================================================================
|
||||
|
||||
$slider-bg-color: color('grey', 'base') !default;
|
||||
$slider-bg-color-light: color('grey', 'lighten-2') !default;
|
||||
$slider-indicator-color: color('green', 'base') !default;
|
||||
|
||||
|
||||
// 16. Spinners | Loaders
|
||||
// ==========================================================================
|
||||
|
||||
$spinner-default-color: $secondary-color !default;
|
||||
|
||||
|
||||
// 17. Tabs
|
||||
// ==========================================================================
|
||||
|
||||
$tabs-underline-color: $primary-color-light !default;
|
||||
$tabs-text-color: $primary-color !default;
|
||||
$tabs-bg-color: #fff !default;
|
||||
|
||||
|
||||
// 18. Tables
|
||||
// ==========================================================================
|
||||
|
||||
$table-border-color: rgba(0,0,0,.12) !default;
|
||||
$table-striped-color: rgba(242, 242, 242, 0.5) !default;
|
||||
|
||||
|
||||
// 19. Toasts
|
||||
// ==========================================================================
|
||||
|
||||
$toast-height: 48px !default;
|
||||
$toast-color: #323232 !default;
|
||||
$toast-text-color: #fff !default;
|
||||
$toast-action-color: #eeff41;
|
||||
|
||||
|
||||
// 20. Typography
|
||||
// ==========================================================================
|
||||
|
||||
$font-stack: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !default;
|
||||
$off-black: rgba(0, 0, 0, 0.87) !default;
|
||||
// Header Styles
|
||||
$h1-fontsize: 4.2rem !default;
|
||||
$h2-fontsize: 3.56rem !default;
|
||||
$h3-fontsize: 2.92rem !default;
|
||||
$h4-fontsize: 2.28rem !default;
|
||||
$h5-fontsize: 1.64rem !default;
|
||||
$h6-fontsize: 1.15rem !default;
|
||||
|
||||
|
||||
// 21. Footer
|
||||
// ==========================================================================
|
||||
|
||||
$footer-font-color: #fff !default;
|
||||
$footer-bg-color: $primary-color !default;
|
||||
$footer-copyright-font-color: rgba(255,255,255,.8) !default;
|
||||
$footer-copyright-bg-color: rgba(51,51,51,.08) !default;
|
||||
|
||||
|
||||
// 22. Flow Text
|
||||
// ==========================================================================
|
||||
|
||||
$range : $large-screen - $small-screen !default;
|
||||
$intervals: 20 !default;
|
||||
$interval-size: $range / $intervals !default;
|
||||
|
||||
|
||||
// 23. Collections
|
||||
// ==========================================================================
|
||||
|
||||
$collection-border-color: #e0e0e0 !default;
|
||||
$collection-bg-color: #fff !default;
|
||||
$collection-active-bg-color: $secondary-color !default;
|
||||
$collection-active-color: lighten($secondary-color, 55%) !default;
|
||||
$collection-hover-bg-color: #ddd !default;
|
||||
$collection-link-color: $secondary-color !default;
|
||||
$collection-line-height: 1.5rem !default;
|
||||
|
||||
|
||||
// 24. Progress Bar
|
||||
// ==========================================================================
|
||||
|
||||
$progress-bar-color: $secondary-color !default;
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
|
||||
/*!
|
||||
* Waves v0.6.0
|
||||
* http://fian.my.id/Waves
|
||||
*
|
||||
* Copyright 2014 Alfiana E. Sibuea and other contributors
|
||||
* Released under the MIT license
|
||||
* https://github.com/fians/Waves/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
|
||||
.waves-effect {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
vertical-align: middle;
|
||||
z-index: 1;
|
||||
transition: .3s ease-out;
|
||||
|
||||
.waves-ripple {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top:-10px;
|
||||
margin-left:-10px;
|
||||
opacity: 0;
|
||||
|
||||
background: rgba(0,0,0,0.2);
|
||||
transition: all 0.7s ease-out;
|
||||
transition-property: transform, opacity;
|
||||
transform: scale(0);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Waves Colors
|
||||
&.waves-light .waves-ripple {
|
||||
background-color: rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
&.waves-red .waves-ripple {
|
||||
background-color: rgba(244, 67, 54, .70);
|
||||
}
|
||||
&.waves-yellow .waves-ripple {
|
||||
background-color: rgba(255, 235, 59, .70);
|
||||
}
|
||||
&.waves-orange .waves-ripple {
|
||||
background-color: rgba(255, 152, 0, .70);
|
||||
}
|
||||
&.waves-purple .waves-ripple {
|
||||
background-color: rgba(156, 39, 176, 0.70);
|
||||
}
|
||||
&.waves-green .waves-ripple {
|
||||
background-color: rgba(76, 175, 80, 0.70);
|
||||
}
|
||||
&.waves-teal .waves-ripple {
|
||||
background-color: rgba(0, 150, 136, 0.70);
|
||||
}
|
||||
|
||||
// Style input button bug.
|
||||
input[type="button"], input[type="reset"], input[type="submit"] {
|
||||
border: 0;
|
||||
font-style: normal;
|
||||
font-size: inherit;
|
||||
text-transform: inherit;
|
||||
background: none;
|
||||
}
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
|
||||
.waves-notransition {
|
||||
transition: none #{"!important"};
|
||||
}
|
||||
|
||||
.waves-circle {
|
||||
transform: translateZ(0);
|
||||
-webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%);
|
||||
}
|
||||
|
||||
.waves-input-wrapper {
|
||||
border-radius: 0.2em;
|
||||
vertical-align: bottom;
|
||||
|
||||
.waves-button-input {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.waves-circle {
|
||||
text-align: center;
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
line-height: 2.5em;
|
||||
border-radius: 50%;
|
||||
-webkit-mask-image: none;
|
||||
}
|
||||
|
||||
.waves-block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Firefox Bug: link not triggered */
|
||||
.waves-effect .waves-ripple {
|
||||
z-index: -1;
|
||||
}
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
/* Checkboxes
|
||||
========================================================================== */
|
||||
|
||||
/* Remove default checkbox */
|
||||
[type="checkbox"]:not(:checked),
|
||||
[type="checkbox"]:checked {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Checkbox Styles
|
||||
[type="checkbox"] {
|
||||
// Text Label Style
|
||||
+ span:not(.lever) {
|
||||
position: relative;
|
||||
padding-left: 35px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
font-size: 1rem;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* checkbox aspect */
|
||||
+ span:not(.lever):before,
|
||||
&:not(.filled-in) + span:not(.lever):after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
z-index: 0;
|
||||
border: 2px solid $radio-empty-color;
|
||||
border-radius: 1px;
|
||||
margin-top: 3px;
|
||||
transition: .2s;
|
||||
}
|
||||
|
||||
&:not(.filled-in) + span:not(.lever):after {
|
||||
border: 0;
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
&:not(:checked):disabled + span:not(.lever):before {
|
||||
border: none;
|
||||
background-color: $input-disabled-color;
|
||||
}
|
||||
|
||||
// Focused styles
|
||||
&.tabbed:focus + span:not(.lever):after {
|
||||
transform: scale(1);
|
||||
border: 0;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 0 10px rgba(0,0,0,.1);
|
||||
background-color: rgba(0,0,0,.1);
|
||||
}
|
||||
}
|
||||
|
||||
[type="checkbox"]:checked {
|
||||
+ span:not(.lever):before {
|
||||
top: -4px;
|
||||
left: -5px;
|
||||
width: 12px;
|
||||
height: 22px;
|
||||
border-top: 2px solid transparent;
|
||||
border-left: 2px solid transparent;
|
||||
border-right: $radio-border;
|
||||
border-bottom: $radio-border;
|
||||
transform: rotate(40deg);
|
||||
backface-visibility: hidden;
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
|
||||
&:disabled + span:before {
|
||||
border-right: 2px solid $input-disabled-color;
|
||||
border-bottom: 2px solid $input-disabled-color;
|
||||
}
|
||||
}
|
||||
|
||||
/* Indeterminate checkbox */
|
||||
[type="checkbox"]:indeterminate {
|
||||
+ span:not(.lever):before {
|
||||
top: -11px;
|
||||
left: -12px;
|
||||
width: 10px;
|
||||
height: 22px;
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
border-right: $radio-border;
|
||||
border-bottom: none;
|
||||
transform: rotate(90deg);
|
||||
backface-visibility: hidden;
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
|
||||
// Disabled indeterminate
|
||||
&:disabled + span:not(.lever):before {
|
||||
border-right: 2px solid $input-disabled-color;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// Filled in Style
|
||||
[type="checkbox"].filled-in {
|
||||
// General
|
||||
+ span:not(.lever):after {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
+ span:not(.lever):before,
|
||||
+ span:not(.lever):after {
|
||||
content: '';
|
||||
left: 0;
|
||||
position: absolute;
|
||||
/* .1s delay is for check animation */
|
||||
transition: border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// Unchecked style
|
||||
&:not(:checked) + span:not(.lever):before {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 3px solid transparent;
|
||||
left: 6px;
|
||||
top: 10px;
|
||||
transform: rotateZ(37deg);
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
|
||||
&:not(:checked) + span:not(.lever):after {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
background-color: transparent;
|
||||
border: 2px solid $radio-empty-color;
|
||||
top: 0px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
// Checked style
|
||||
&:checked {
|
||||
+ span:not(.lever):before {
|
||||
top: 0;
|
||||
left: 1px;
|
||||
width: 8px;
|
||||
height: 13px;
|
||||
border-top: 2px solid transparent;
|
||||
border-left: 2px solid transparent;
|
||||
border-right: 2px solid $input-background;
|
||||
border-bottom: 2px solid $input-background;
|
||||
transform: rotateZ(37deg);
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
|
||||
+ span:not(.lever):after {
|
||||
top: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid $secondary-color;
|
||||
background-color: $secondary-color;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Focused styles
|
||||
&.tabbed:focus + span:not(.lever):after {
|
||||
border-radius: 2px;
|
||||
border-color: $radio-empty-color;
|
||||
background-color: rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
&.tabbed:checked:focus + span:not(.lever):after {
|
||||
border-radius: 2px;
|
||||
background-color: $secondary-color;
|
||||
border-color: $secondary-color;
|
||||
}
|
||||
|
||||
// Disabled style
|
||||
&:disabled:not(:checked) + span:not(.lever):before {
|
||||
background-color: transparent;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
&:disabled:not(:checked) + span:not(.lever):after {
|
||||
border-color: transparent;
|
||||
background-color: $input-disabled-solid-color;
|
||||
}
|
||||
|
||||
&:disabled:checked + span:not(.lever):before {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&:disabled:checked + span:not(.lever):after {
|
||||
background-color: $input-disabled-solid-color;
|
||||
border-color: $input-disabled-solid-color;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/* File Input
|
||||
========================================================================== */
|
||||
|
||||
.file-field {
|
||||
position: relative;
|
||||
|
||||
.file-path-wrapper {
|
||||
overflow: hidden;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
input.file-path { width: 100%; }
|
||||
|
||||
.btn {
|
||||
float: left;
|
||||
height: $input-height;
|
||||
line-height: $input-height;
|
||||
}
|
||||
|
||||
span {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type=file] {
|
||||
|
||||
// Needed to override webkit button
|
||||
&::-webkit-file-upload-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
filter: alpha(opacity=0);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// Remove Focus Boxes
|
||||
select:focus {
|
||||
outline: $select-focus;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: none;
|
||||
background-color: $button-background-focus;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: $label-font-size;
|
||||
color: $input-border-color;
|
||||
}
|
||||
|
||||
@import 'input-fields';
|
||||
@import 'radio-buttons';
|
||||
@import 'checkboxes';
|
||||
@import 'switches';
|
||||
@import 'select';
|
||||
@import 'file-input';
|
||||
@import 'range';
|
||||
|
|
@ -0,0 +1,354 @@
|
|||
/* Text Inputs + Textarea
|
||||
========================================================================== */
|
||||
|
||||
/* Style Placeholders */
|
||||
|
||||
::placeholder {
|
||||
color: $placeholder-text-color;
|
||||
}
|
||||
|
||||
/* Text inputs */
|
||||
|
||||
input:not([type]),
|
||||
input[type=text]:not(.browser-default),
|
||||
input[type=password]:not(.browser-default),
|
||||
input[type=email]:not(.browser-default),
|
||||
input[type=url]:not(.browser-default),
|
||||
input[type=time]:not(.browser-default),
|
||||
input[type=date]:not(.browser-default),
|
||||
input[type=datetime]:not(.browser-default),
|
||||
input[type=datetime-local]:not(.browser-default),
|
||||
input[type=tel]:not(.browser-default),
|
||||
input[type=number]:not(.browser-default),
|
||||
input[type=search]:not(.browser-default),
|
||||
textarea.materialize-textarea {
|
||||
|
||||
// General Styles
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-bottom: $input-border;
|
||||
border-radius: 0;
|
||||
outline: none;
|
||||
height: $input-height;
|
||||
width: 100%;
|
||||
font-size: $input-font-size;
|
||||
margin: $input-margin;
|
||||
padding: $input-padding;
|
||||
box-shadow: none;
|
||||
box-sizing: content-box;
|
||||
transition: box-shadow .3s, border .3s;
|
||||
|
||||
// Disabled input style
|
||||
&:disabled,
|
||||
&[readonly="readonly"] {
|
||||
color: $input-disabled-color;
|
||||
border-bottom: $input-disabled-border;
|
||||
}
|
||||
|
||||
// Disabled label style
|
||||
&:disabled+label,
|
||||
&[readonly="readonly"]+label {
|
||||
color: $input-disabled-color;
|
||||
}
|
||||
|
||||
// Focused input style
|
||||
&:focus:not([readonly]) {
|
||||
border-bottom: 1px solid $input-focus-color;
|
||||
box-shadow: 0 1px 0 0 $input-focus-color;
|
||||
}
|
||||
|
||||
// Focused label style
|
||||
&:focus:not([readonly])+label {
|
||||
color: $input-focus-color;
|
||||
}
|
||||
|
||||
// Hide helper text on data message
|
||||
&.valid ~ .helper-text[data-success],
|
||||
&:focus.valid ~ .helper-text[data-success],
|
||||
&.invalid ~ .helper-text[data-error],
|
||||
&:focus.invalid ~ .helper-text[data-error] {
|
||||
@extend %hidden-text;
|
||||
}
|
||||
|
||||
// Valid Input Style
|
||||
&.valid,
|
||||
&:focus.valid {
|
||||
@extend %valid-input-style;
|
||||
}
|
||||
|
||||
// Custom Success Message
|
||||
&.valid ~ .helper-text:after,
|
||||
&:focus.valid ~ .helper-text:after {
|
||||
@extend %custom-success-message;
|
||||
}
|
||||
&:focus.valid ~ label {
|
||||
color: $input-success-color;
|
||||
}
|
||||
|
||||
// Invalid Input Style
|
||||
&.invalid,
|
||||
&:focus.invalid {
|
||||
@extend %invalid-input-style;
|
||||
}
|
||||
|
||||
// Custom Error message
|
||||
&.invalid ~ .helper-text:after,
|
||||
&:focus.invalid ~ .helper-text:after {
|
||||
@extend %custom-error-message;
|
||||
}
|
||||
&:focus.invalid ~ label {
|
||||
color: $input-error-color;
|
||||
}
|
||||
|
||||
// Full width label when using validate for error messages
|
||||
&.validate + label {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// Form Message Shared Styles
|
||||
& + label:after {
|
||||
@extend %input-after-style;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Validation Sass Placeholders */
|
||||
%valid-input-style {
|
||||
border-bottom: 1px solid $input-success-color;
|
||||
box-shadow: 0 1px 0 0 $input-success-color;
|
||||
}
|
||||
%invalid-input-style {
|
||||
border-bottom: $input-invalid-border;
|
||||
box-shadow: 0 1px 0 0 $input-error-color;
|
||||
}
|
||||
%hidden-text {
|
||||
color: transparent;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
%custom-success-message {
|
||||
content: attr(data-success);
|
||||
color: $input-success-color;
|
||||
}
|
||||
%custom-error-message {
|
||||
content: attr(data-error);
|
||||
color: $input-error-color;
|
||||
}
|
||||
%input-after-style {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
transition: .2s opacity ease-out, .2s color ease-out;
|
||||
}
|
||||
|
||||
|
||||
// Styling for input field wrapper
|
||||
.input-field {
|
||||
// Inline styles
|
||||
&.inline {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-left: 5px;
|
||||
|
||||
input,
|
||||
.select-dropdown {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Gutter spacing
|
||||
&.col {
|
||||
label {
|
||||
left: $gutter-width / 2;
|
||||
}
|
||||
|
||||
.prefix ~ label,
|
||||
.prefix ~ .validate ~ label {
|
||||
width: calc(100% - 3rem - #{$gutter-width});
|
||||
}
|
||||
}
|
||||
|
||||
position: relative;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
& > label {
|
||||
color: $input-border-color;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
font-size: 1rem;
|
||||
cursor: text;
|
||||
transition: transform .2s ease-out, color .2s ease-out;
|
||||
transform-origin: 0% 100%;
|
||||
text-align: initial;
|
||||
transform: translateY(12px);
|
||||
|
||||
&:not(.label-icon).active {
|
||||
transform: translateY(-14px) scale(.8);
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Autofill + date + time inputs
|
||||
& > input[type]:-webkit-autofill:not(.browser-default):not([type="search"]) + label,
|
||||
& > input[type=date]:not(.browser-default) + label,
|
||||
& > input[type=time]:not(.browser-default) + label {
|
||||
transform: translateY(-14px) scale(.8);
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
&::after {
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
position: relative;
|
||||
min-height: 18px;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: rgba(0,0,0,.54);
|
||||
}
|
||||
|
||||
// Prefix Icons
|
||||
.prefix {
|
||||
position: absolute;
|
||||
width: $input-height;
|
||||
font-size: $input-icon-size;
|
||||
transition: color .2s;
|
||||
top: ($input-height - $input-icon-size) / 2;
|
||||
|
||||
&.active { color: $input-focus-color; }
|
||||
}
|
||||
|
||||
.prefix ~ input,
|
||||
.prefix ~ textarea,
|
||||
.prefix ~ label,
|
||||
.prefix ~ .validate ~ label,
|
||||
.prefix ~ .helper-text,
|
||||
.prefix ~ .autocomplete-content {
|
||||
margin-left: 3rem;
|
||||
width: 92%;
|
||||
width: calc(100% - 3rem);
|
||||
}
|
||||
|
||||
.prefix ~ label { margin-left: 3rem; }
|
||||
|
||||
@media #{$medium-and-down} {
|
||||
.prefix ~ input {
|
||||
width: 86%;
|
||||
width: calc(100% - 3rem);
|
||||
}
|
||||
}
|
||||
|
||||
@media #{$small-and-down} {
|
||||
.prefix ~ input {
|
||||
width: 80%;
|
||||
width: calc(100% - 3rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Search Field */
|
||||
|
||||
.input-field input[type=search] {
|
||||
display: block;
|
||||
line-height: inherit;
|
||||
transition: .3s background-color;
|
||||
|
||||
.nav-wrapper & {
|
||||
height: inherit;
|
||||
padding-left: 4rem;
|
||||
width: calc(100% - 4rem);
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:focus:not(.browser-default) {
|
||||
background-color: $input-background;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
color: #444;
|
||||
|
||||
& + label i,
|
||||
& ~ .mdi-navigation-close,
|
||||
& ~ .material-icons {
|
||||
color: #444;
|
||||
}
|
||||
}
|
||||
|
||||
& + .label-icon {
|
||||
transform: none;
|
||||
left: 1rem;
|
||||
}
|
||||
|
||||
& ~ .mdi-navigation-close,
|
||||
& ~ .material-icons {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 1rem;
|
||||
color: transparent;
|
||||
cursor: pointer;
|
||||
font-size: $input-icon-size;
|
||||
transition: .3s color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Textarea */
|
||||
|
||||
// Default textarea
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: $input-height;
|
||||
background-color: transparent;
|
||||
|
||||
&.materialize-textarea {
|
||||
line-height: normal;
|
||||
overflow-y: hidden; /* prevents scroll bar flash */
|
||||
padding: .8rem 0 .8rem 0; /* prevents text jump on Enter keypress */
|
||||
resize: none;
|
||||
min-height: $input-height;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
// For textarea autoresize
|
||||
.hiddendiv {
|
||||
visibility: hidden;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word; /* future version of deprecated 'word-wrap' */
|
||||
padding-top: 1.2rem; /* prevents text jump on Enter keypress */
|
||||
|
||||
// Reduces repaints
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
|
||||
/* Autocomplete */
|
||||
.autocomplete-content {
|
||||
li {
|
||||
.highlight { color: #444; }
|
||||
|
||||
img {
|
||||
height: $dropdown-item-height - 10;
|
||||
width: $dropdown-item-height - 10;
|
||||
margin: 5px 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Character Counter */
|
||||
.character-counter {
|
||||
min-height: 18px;
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/* Radio Buttons
|
||||
========================================================================== */
|
||||
|
||||
// Remove default Radio Buttons
|
||||
[type="radio"]:not(:checked),
|
||||
[type="radio"]:checked {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
[type="radio"]:not(:checked) + span,
|
||||
[type="radio"]:checked + span {
|
||||
position: relative;
|
||||
padding-left: 35px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
font-size: 1rem;
|
||||
transition: .28s ease;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[type="radio"] + span:before,
|
||||
[type="radio"] + span:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
margin: 4px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
z-index: 0;
|
||||
transition: .28s ease;
|
||||
}
|
||||
|
||||
/* Unchecked styles */
|
||||
[type="radio"]:not(:checked) + span:before,
|
||||
[type="radio"]:not(:checked) + span:after,
|
||||
[type="radio"]:checked + span:before,
|
||||
[type="radio"]:checked + span:after,
|
||||
[type="radio"].with-gap:checked + span:before,
|
||||
[type="radio"].with-gap:checked + span:after {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
[type="radio"]:not(:checked) + span:before,
|
||||
[type="radio"]:not(:checked) + span:after {
|
||||
border: 2px solid $radio-empty-color;
|
||||
}
|
||||
|
||||
[type="radio"]:not(:checked) + span:after {
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
/* Checked styles */
|
||||
[type="radio"]:checked + span:before {
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
[type="radio"]:checked + span:after,
|
||||
[type="radio"].with-gap:checked + span:before,
|
||||
[type="radio"].with-gap:checked + span:after {
|
||||
border: $radio-border;
|
||||
}
|
||||
|
||||
[type="radio"]:checked + span:after,
|
||||
[type="radio"].with-gap:checked + span:after {
|
||||
background-color: $radio-fill-color;
|
||||
}
|
||||
|
||||
[type="radio"]:checked + span:after {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
/* Radio With gap */
|
||||
[type="radio"].with-gap:checked + span:after {
|
||||
transform: scale(.5);
|
||||
}
|
||||
|
||||
/* Focused styles */
|
||||
[type="radio"].tabbed:focus + span:before {
|
||||
box-shadow: 0 0 0 10px rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
/* Disabled Radio With gap */
|
||||
[type="radio"].with-gap:disabled:checked + span:before {
|
||||
border: 2px solid $input-disabled-color;
|
||||
}
|
||||
|
||||
[type="radio"].with-gap:disabled:checked + span:after {
|
||||
border: none;
|
||||
background-color: $input-disabled-color;
|
||||
}
|
||||
|
||||
/* Disabled style */
|
||||
[type="radio"]:disabled:not(:checked) + span:before,
|
||||
[type="radio"]:disabled:checked + span:before {
|
||||
background-color: transparent;
|
||||
border-color: $input-disabled-color;
|
||||
}
|
||||
|
||||
[type="radio"]:disabled + span {
|
||||
color: $input-disabled-color;
|
||||
}
|
||||
|
||||
[type="radio"]:disabled:not(:checked) + span:before {
|
||||
border-color: $input-disabled-color;
|
||||
}
|
||||
|
||||
[type="radio"]:disabled:checked + span:after {
|
||||
background-color: $input-disabled-color;
|
||||
border-color: $input-disabled-solid-color;
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/* Range
|
||||
========================================================================== */
|
||||
|
||||
.range-field {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input[type=range],
|
||||
input[type=range] + .thumb {
|
||||
@extend .no-select;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type=range] {
|
||||
position: relative;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
margin: 15px 0;
|
||||
padding: 0;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=range] + .thumb {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0;
|
||||
border: none;
|
||||
height: 0;
|
||||
width: 0;
|
||||
border-radius: 50%;
|
||||
background-color: $radio-fill-color;
|
||||
margin-left: 7px;
|
||||
|
||||
transform-origin: 50% 50%;
|
||||
transform: rotate(-45deg);
|
||||
|
||||
.value {
|
||||
display: block;
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
color: $radio-fill-color;
|
||||
font-size: 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-radius: 50% 50% 50% 0;
|
||||
|
||||
.value {
|
||||
color: $input-background;
|
||||
margin-left: -1px;
|
||||
margin-top: 8px;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shared
|
||||
@mixin range-track {
|
||||
height: $track-height;
|
||||
background: #c2c0c2;
|
||||
border: none;
|
||||
}
|
||||
|
||||
@mixin range-thumb {
|
||||
border: none;
|
||||
height: $range-height;
|
||||
width: $range-width;
|
||||
border-radius: 50%;
|
||||
background: $radio-fill-color;
|
||||
transition: box-shadow .3s;
|
||||
}
|
||||
|
||||
// WebKit
|
||||
input[type=range] {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
input[type=range]::-webkit-slider-runnable-track {
|
||||
@include range-track;
|
||||
}
|
||||
|
||||
input[type=range]::-webkit-slider-thumb {
|
||||
@include range-thumb;
|
||||
-webkit-appearance: none;
|
||||
background-color: $radio-fill-color;
|
||||
transform-origin: 50% 50%;
|
||||
margin: -5px 0 0 0;
|
||||
|
||||
}
|
||||
|
||||
.keyboard-focused input[type=range]:focus:not(.active)::-webkit-slider-thumb {
|
||||
box-shadow: 0 0 0 10px rgba($radio-fill-color, .26);
|
||||
}
|
||||
|
||||
// FireFox
|
||||
input[type=range] {
|
||||
/* fix for FF unable to apply focus style bug */
|
||||
border: 1px solid white;
|
||||
|
||||
/*required for proper track sizing in FF*/
|
||||
}
|
||||
|
||||
input[type=range]::-moz-range-track {
|
||||
@include range-track;
|
||||
}
|
||||
|
||||
input[type=range]::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
input[type=range]::-moz-range-thumb {
|
||||
@include range-thumb;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
// hide the outline behind the border
|
||||
input[type=range]:-moz-focusring {
|
||||
outline: 1px solid #fff;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
.keyboard-focused input[type=range]:focus:not(.active)::-moz-range-thumb {
|
||||
box-shadow: 0 0 0 10px rgba($radio-fill-color, .26);
|
||||
}
|
||||
|
||||
// IE 10+
|
||||
input[type=range]::-ms-track {
|
||||
height: $track-height;
|
||||
|
||||
// remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead
|
||||
background: transparent;
|
||||
|
||||
// leave room for the larger thumb to overflow with a transparent border */
|
||||
border-color: transparent;
|
||||
border-width: 6px 0;
|
||||
|
||||
/*remove default tick marks*/
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
input[type=range]::-ms-fill-lower {
|
||||
background: #777;
|
||||
}
|
||||
|
||||
input[type=range]::-ms-fill-upper {
|
||||
background: #ddd;
|
||||
}
|
||||
|
||||
input[type=range]::-ms-thumb {
|
||||
@include range-thumb;
|
||||
}
|
||||
|
||||
.keyboard-focused input[type=range]:focus:not(.active)::-ms-thumb {
|
||||
box-shadow: 0 0 0 10px rgba($radio-fill-color, .26);
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
/* Select Field
|
||||
========================================================================== */
|
||||
|
||||
select { display: none; }
|
||||
select.browser-default { display: block; }
|
||||
|
||||
select {
|
||||
background-color: $select-background;
|
||||
width: 100%;
|
||||
padding: $select-padding;
|
||||
border: $select-border;
|
||||
border-radius: $select-radius;
|
||||
height: $input-height;
|
||||
}
|
||||
|
||||
.select-label {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
&.valid .helper-text[data-success],
|
||||
&.invalid ~ .helper-text[data-error] {
|
||||
@extend %hidden-text;
|
||||
}
|
||||
|
||||
&.valid {
|
||||
& > input.select-dropdown {
|
||||
@extend %valid-input-style;
|
||||
}
|
||||
|
||||
& ~ .helper-text:after {
|
||||
@extend %custom-success-message;
|
||||
}
|
||||
}
|
||||
|
||||
&.invalid {
|
||||
& > input.select-dropdown,
|
||||
& > input.select-dropdown:focus {
|
||||
@extend %invalid-input-style;
|
||||
}
|
||||
|
||||
& ~ .helper-text:after {
|
||||
@extend %custom-error-message;
|
||||
}
|
||||
}
|
||||
|
||||
&.valid + label,
|
||||
&.invalid + label {
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
& + label:after {
|
||||
@extend %input-after-style;
|
||||
}
|
||||
|
||||
position: relative;
|
||||
|
||||
input.select-dropdown {
|
||||
&:focus {
|
||||
border-bottom: 1px solid $input-focus-color;
|
||||
}
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-bottom: $input-border;
|
||||
outline: none;
|
||||
height: $input-height;
|
||||
line-height: $input-height;
|
||||
width: 100%;
|
||||
font-size: $input-font-size;
|
||||
margin: $input-margin;
|
||||
padding: 0;
|
||||
display: block;
|
||||
user-select:none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.caret {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto 0;
|
||||
z-index: 0;
|
||||
fill: rgba(0,0,0,.87);
|
||||
}
|
||||
|
||||
& + label {
|
||||
position: absolute;
|
||||
top: -26px;
|
||||
font-size: $label-font-size;
|
||||
}
|
||||
}
|
||||
|
||||
// Disabled styles
|
||||
select:disabled {
|
||||
color: $input-disabled-color;
|
||||
}
|
||||
|
||||
.select-wrapper.disabled {
|
||||
+ label {
|
||||
color: $input-disabled-color;
|
||||
}
|
||||
.caret {
|
||||
fill: $input-disabled-color;
|
||||
}
|
||||
}
|
||||
|
||||
.select-wrapper input.select-dropdown:disabled {
|
||||
color: $input-disabled-color;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.select-wrapper i {
|
||||
color: $select-disabled-color;
|
||||
}
|
||||
|
||||
.select-dropdown li.disabled,
|
||||
.select-dropdown li.disabled > span,
|
||||
.select-dropdown li.optgroup {
|
||||
color: $select-disabled-color;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
body.keyboard-focused {
|
||||
.select-dropdown.dropdown-content li:focus {
|
||||
background-color: $select-option-focus;
|
||||
}
|
||||
}
|
||||
|
||||
.select-dropdown.dropdown-content {
|
||||
li {
|
||||
&:hover {
|
||||
background-color: $select-option-hover;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: $select-option-selected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prefix Icons
|
||||
.prefix ~ .select-wrapper {
|
||||
margin-left: 3rem;
|
||||
width: 92%;
|
||||
width: calc(100% - 3rem);
|
||||
}
|
||||
|
||||
.prefix ~ label { margin-left: 3rem; }
|
||||
|
||||
// Icons
|
||||
.select-dropdown li {
|
||||
img {
|
||||
height: $dropdown-item-height - 10;
|
||||
width: $dropdown-item-height - 10;
|
||||
margin: 5px 15px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
// Optgroup styles
|
||||
.select-dropdown li.optgroup {
|
||||
border-top: 1px solid $dropdown-hover-bg-color;
|
||||
|
||||
&.selected > span {
|
||||
color: rgba(0, 0, 0, .7);
|
||||
}
|
||||
|
||||
& > span {
|
||||
color: rgba(0, 0, 0, .4);
|
||||
}
|
||||
|
||||
& ~ li.optgroup-option {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/* Switch
|
||||
========================================================================== */
|
||||
|
||||
.switch,
|
||||
.switch * {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.switch label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.switch label input[type=checkbox] {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
&:checked + .lever {
|
||||
background-color: $switch-checked-lever-bg;
|
||||
|
||||
&:before, &:after {
|
||||
left: 18px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
background-color: $switch-bg-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.switch label .lever {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 36px;
|
||||
height: 14px;
|
||||
background-color: $switch-unchecked-lever-bg;
|
||||
border-radius: $switch-radius;
|
||||
margin-right: 10px;
|
||||
transition: background 0.3s ease;
|
||||
vertical-align: middle;
|
||||
margin: 0 16px;
|
||||
|
||||
&:before, &:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
left: 0;
|
||||
top: -3px;
|
||||
transition: left 0.3s ease, background .3s ease, box-shadow 0.1s ease, transform .1s ease;
|
||||
}
|
||||
|
||||
&:before {
|
||||
background-color: transparentize($switch-bg-color, .85);
|
||||
}
|
||||
|
||||
&:after {
|
||||
background-color: $switch-unchecked-bg;
|
||||
box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
}
|
||||
|
||||
// Switch active style
|
||||
input[type=checkbox]:checked:not(:disabled) ~ .lever:active::before,
|
||||
input[type=checkbox]:checked:not(:disabled).tabbed:focus ~ .lever::before {
|
||||
transform: scale(2.4);
|
||||
background-color: transparentize($switch-bg-color, .85);
|
||||
}
|
||||
|
||||
input[type=checkbox]:not(:disabled) ~ .lever:active:before,
|
||||
input[type=checkbox]:not(:disabled).tabbed:focus ~ .lever::before {
|
||||
transform: scale(2.4);
|
||||
background-color: rgba(0,0,0,.08);
|
||||
}
|
||||
|
||||
// Disabled Styles
|
||||
.switch input[type=checkbox][disabled] + .lever {
|
||||
cursor: default;
|
||||
background-color: rgba(0,0,0,.12);
|
||||
}
|
||||
|
||||
.switch label input[type=checkbox][disabled] + .lever:after,
|
||||
.switch label input[type=checkbox][disabled]:checked + .lever:after {
|
||||
background-color: $input-disabled-solid-color;
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
// Color
|
||||
@import "components/color-variables";
|
||||
@import "components/color-classes";
|
||||
|
||||
// Variables;
|
||||
@import "components/variables";
|
||||
|
||||
// Reset
|
||||
@import "components/normalize";
|
||||
|
||||
// components
|
||||
@import "components/global";
|
||||
@import "components/badges";
|
||||
@import "components/icons-material-design";
|
||||
@import "components/grid";
|
||||
@import "components/navbar";
|
||||
@import "components/typography";
|
||||
@import "components/transitions";
|
||||
@import "components/cards";
|
||||
@import "components/toast";
|
||||
@import "components/tabs";
|
||||
@import "components/tooltip";
|
||||
@import "components/buttons";
|
||||
@import "components/dropdown";
|
||||
@import "components/waves";
|
||||
@import "components/modal";
|
||||
@import "components/collapsible";
|
||||
@import "components/chips";
|
||||
@import "components/materialbox";
|
||||
@import "components/forms/forms";
|
||||
@import "components/table_of_contents";
|
||||
@import "components/sidenav";
|
||||
@import "components/preloader";
|
||||
@import "components/slider";
|
||||
@import "components/carousel";
|
||||
@import "components/tapTarget";
|
||||
@import "components/pulse";
|
||||
@import "components/datepicker";
|
||||
@import "components/timepicker";
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
table {
|
||||
border-collapse: collapse;
|
||||
border: solid;
|
||||
border-color: darkblue !important;
|
||||
}
|
||||
|
||||
th {
|
||||
color: darkblue !important;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background-color: darkred !important;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background-color: red !important;
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
overflow: visible;
|
||||
border-radius: 6px;
|
||||
color: #444;
|
||||
border: 1px solid #CCC;
|
||||
background: #dddddd3b;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
padding: 5px 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.select-wrapper input.select-dropdown {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.select-wrapper:hover {
|
||||
color: #333;
|
||||
background-color: #e7e7e7;
|
||||
}
|
||||
|
||||
/* PROGRESS BAR STEP */
|
||||
|
||||
.stepper-wrapper {
|
||||
margin-top: auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 20px 0px;
|
||||
}
|
||||
|
||||
.stepper-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.stepper-item::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
border-bottom: 2px solid #ccc;
|
||||
width: 100%;
|
||||
top: 15px;
|
||||
left: -50%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.stepper-item::after {
|
||||
position: absolute;
|
||||
content: "";
|
||||
border-bottom: 2px solid #ccc;
|
||||
width: 100%;
|
||||
top: 13px;
|
||||
left: 50%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.stepper-item .step-counter {
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
background: #ccc;
|
||||
margin-bottom: 6px;
|
||||
color: white
|
||||
}
|
||||
|
||||
.stepper-item.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.stepper-item.active .step-counter {
|
||||
background-color: rgb(38, 166, 154);
|
||||
}
|
||||
|
||||
.stepper-item.completed .step-counter {
|
||||
background-color: darkblue;
|
||||
}
|
||||
|
||||
.stepper-item.empty .step-counter {
|
||||
background-color: #f46536;
|
||||
}
|
||||
|
||||
.stepper-item.line::after {
|
||||
position: absolute;
|
||||
content: "";
|
||||
border-bottom: 4px solid darkblue;
|
||||
width: 100%;
|
||||
top: 13px;
|
||||
left: 50%;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.stepper-item:first-child::before {
|
||||
content: none !important;
|
||||
}
|
||||
|
||||
.stepper-item:last-child::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.mrgl0 {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.card {
|
||||
height: 95%;
|
||||
}
|
||||
|
||||
.card .card-action:last-child {
|
||||
margin-bottom: 20px;
|
||||
border-radius: 0 0 20px 20px;
|
||||
}
|
||||
|
||||
.txt-color {
|
||||
color: darkblue !important;
|
||||
font-weight: bold;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
|
||||
/* Style pour la franchise sélectionnée */
|
||||
.franchise-card {
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.franchise-card.selected {
|
||||
border: 3px solid darkblue !important;
|
||||
box-shadow: 0 4px 10px rgba(76, 175, 80, 0.3);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.franchise-card.selected .card-tarif-head {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.franchise-card.selected .card-tarif-head::after {
|
||||
content: "✓ SÉLECTIONNÉ";
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 10px;
|
||||
background: darkblue !important;
|
||||
color: white;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 0.7em;
|
||||
font-weight: bold;
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue