reorganized structure

This commit is contained in:
FezVrasta 2014-10-31 09:20:24 +01:00
parent 931d61f248
commit 9e65d2577b
13 changed files with 11985 additions and 18 deletions

View File

@ -51,14 +51,6 @@ module.exports = function(grunt) {
}
},
clean: {
css: [
"dist/css/material.css",
"dist/css/material-wfont.css",
"dist/css/ripples.css"
]
},
uglify: {
minifyjs: {
files: {
@ -72,7 +64,7 @@ module.exports = function(grunt) {
distjs: {
expand: true,
cwd: "scripts/",
src: "**.min.js",
src: "**",
dest: "dist/js/",
flatten: true,
filter: "isFile"
@ -172,9 +164,9 @@ module.exports = function(grunt) {
});
grunt.registerTask("default", ["less", "autoprefixer", "cssmin", "uglify", "clean", "copy"]);
grunt.registerTask("default", ["less", "autoprefixer", "cssmin", "uglify", "copy"]);
grunt.registerTask("scss", ["sass", "autoprefixer", "cssmin", "uglify", "clean", "copy"]);
grunt.registerTask("scss", ["sass", "autoprefixer", "cssmin", "uglify", "copy"]);
grunt.registerTask("build", function(target) {
var buildType = "default";

View File

@ -1,4 +1,4 @@
[![banner](demo/imgs/banner.jpg)](#)
[![banner](demo/imgss/banner.jpg)](#)
This Bootstrap theme is an easy way to use the new Material Design guidelines by Google in your Bootstrap 3 based application.
Just include the theme right after the Bootstrap CSS and include the javascript at the end of your document, everything will be converted to Material Design (paper) style.
@ -41,8 +41,8 @@ More "todo" things can be found in the ISSUES of this repository.
If you like this project you may support me by donating something on Gittip, starring this repository or reporting bugs and ideas in the issue section.
[![gittip](screenshots/gittip-button.jpg)](https://www.gratipay.com/FezVrasta/)
[![issues](screenshots/issues-button.jpg)](https://github.com/FezVrasta/bootstrap-material-design/issues)
[![gittip](demo/imgs/gittip-button.jpg)](https://www.gratipay.com/FezVrasta/)
[![issues](demo/imgs/issues-button.jpg)](https://github.com/FezVrasta/bootstrap-material-design/issues)
# Contribute
@ -64,7 +64,7 @@ Example:
These colors are taken from the Material Design color palette and are reported below:
![palette](screenshots/palette.jpg)
![palette](demo/imgs/palette.jpg)
### Buttons:

View File

@ -1,6 +1,6 @@
{
"name": "Material Design for Bootstrap",
"version": "0.1.4",
"version": "0.1.5",
"homepage": "http://fezvrasta.github.io/bootstrap-material-design",
"authors": [
"Federico Zivolo <info@mywebexpression.com>"

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

5802
dist/css/material-wfont.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

5795
dist/css/material.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

40
dist/css/ripples.css vendored Normal file
View File

@ -0,0 +1,40 @@
.withripple {
position: relative;
}
.ripple-wrapper {
position: absolute;
top: 0;
left: 0;
z-index: 1;
width: 100%;
height: 100%;
overflow: hidden;
border-radius: 2px;
}
.ripple {
position: absolute;
width: 20px;
height: 20px;
margin-left: -10px;
margin-top: -10px;
border-radius: 100%;
background-color: rgba(0, 0, 0, 0.05);
-webkit-transform: scale(1);
-ms-transform: scale(1);
transform: scale(1);
-webkit-transform-origin: 50%;
-ms-transform-origin: 50%;
transform-origin: 50%;
opacity: 0;
pointer-events: none;
}
.ripple.ripple-on {
-webkit-transition: opacity 0.15s ease-in 0s, -webkit-transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s;
transition: opacity 0.15s ease-in 0s, transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s;
opacity: 1;
}
.ripple.ripple-out {
-webkit-transition: opacity 0.1s linear 0s !important;
transition: opacity 0.1s linear 0s !important;
opacity: 0;
}

159
dist/js/material.js vendored Normal file
View File

@ -0,0 +1,159 @@
/* globals jQuery, ripples */
(function($) {
// Selector to select only not already processed elements
$.expr[":"].notmdproc = function(obj){
if ($(obj).data("mdproc")) {
return false;
} else {
return true;
}
};
function _isChar(evt) {
if (typeof evt.which == "undefined") {
return true;
} else if (typeof evt.which == "number" && evt.which > 0) {
return !evt.ctrlKey && !evt.metaKey && !evt.altKey && evt.which != 8;
}
return false;
}
$.material = {
"options": {
"withRipples": [
".btn:not(.btn-link)",
".card-image",
".navbar a:not(.withoutripple)",
".dropdown-menu a",
".nav-tabs a:not(.withoutripple)",
".withripple"
].join(","),
"inputElements": "input.form-control, textarea.form-control, select.form-control",
"checkboxElements": ".checkbox > label > input[type=checkbox]",
"radioElements": ".radio > label > input[type=radio]"
},
"checkbox": function(selector) {
// Add fake-checkbox to material checkboxes
$((selector) ? selector : this.options.checkboxElements)
.filter(":notmdproc")
.data("mdproc", true)
.after("<span class=ripple></span><span class=check></span>");
},
"radio": function(selector) {
// Add fake-radio to material radios
$((selector) ? selector : this.options.radioElements)
.filter(":notmdproc")
.data("mdproc", true)
.after("<span class=circle></span><span class=check></span>");
},
"input": function(selector) {
$((selector) ? selector : this.options.inputElements)
.filter(":notmdproc")
.data("mdproc", true)
.each( function() {
var $this = $(this);
$this.wrap("<div class=form-control-wrapper></div>");
$this.after("<span class=material-input></span>");
if ($this.hasClass("floating-label")) {
var placeholder = $this.attr("placeholder");
$this.attr("placeholder", null).removeClass("floating-label");
$this.after("<div class=floating-label>" + placeholder + "</div>");
}
if ($this.val() === null || $this.val() == "undefined" || $this.val() === "") {
$this.addClass("empty");
}
if ($this.parent().next().is("[type=file]")) {
$this.parent().addClass("fileinput");
var $input = $this.parent().next().detach();
$this.after($input);
}
});
$(document)
.on("change", ".checkbox input", function() { $(this).blur(); })
.on("keydown paste", ".form-control", function(e) {
if(_isChar(e)) {
$(this).removeClass("empty");
}
})
.on("keyup change", ".form-control", function() {
var $this = $(this);
if($this.val() === "") {
$this.addClass("empty");
} else {
$this.removeClass("empty");
}
})
.on("focus", ".form-control-wrapper.fileinput", function() {
$(this).find("input").addClass("focus");
})
.on("blur", ".form-control-wrapper.fileinput", function() {
$(this).find("input").removeClass("focus");
})
.on("change", ".form-control-wrapper.fileinput [type=file]", function() {
var value = "";
$.each($(this)[0].files, function(i, file) {
console.log(file);
value += file.name + ", ";
});
value = value.substring(0, value.length - 2);
if (value) {
$(this).prev().removeClass("empty");
} else {
$(this).prev().addClass("empty");
}
$(this).prev().val(value);
});
},
"ripples": function(selector) {
ripples.init((selector) ? selector : this.options.withRipples);
},
"init": function() {
this.ripples();
this.input();
this.checkbox();
this.radio();
if (document.arrive) {
document.arrive("input, textarea, select", function() {
$.material.init();
});
}
// Detect autofill
(function() {
// This part of code will detect autofill when the page is loading (username and password inputs for example)
var loading = setInterval(function() {
$("input").each(function() {
if ($(this).val() !== $(this).attr("value")) {
$(this).trigger("change");
}
});
}, 100);
// After 10 seconds we are quite sure all the needed inputs are autofilled then we can stop checking them
setTimeout(function() {
clearInterval(loading);
}, 10000);
// Now we just listen on inputs of the focused form (because user can select from the autofill dropdown only when the input has focus)
var focused;
$(document)
.on("focus", "input", function() {
var $inputs = $(this).parents("form").find("input");
focused = setInterval(function() {
$inputs.each(function() {
if ($(this).val() !== $(this).attr("value")) {
$(this).trigger("change");
}
});
}, 100);
})
.on("blur", "input", function() {
clearInterval(focused);
});
})();
}
};
})(jQuery);

179
dist/js/ripples.js vendored Normal file
View File

@ -0,0 +1,179 @@
/* Copyright 2014+, Federico Zivolo, LICENSE at https://github.com/FezVrasta/bootstrap-material-design/blob/master/LICENSE.md */
/* globals CustomEvent */
window.ripples = {
init : function(withRipple) {
"use strict";
// Cross browser matches function
function matchesSelector(domElement, selector) {
var matches = domElement.matches ||
domElement.matchesSelector ||
domElement.webkitMatchesSelector ||
domElement.mozMatchesSelector ||
domElement.msMatchesSelector ||
domElement.oMatchesSelector;
return matches.call(domElement, selector);
}
// animations time
var rippleOutTime = 100,
rippleStartTime = 500;
// Helper to bind events on dynamically created elements
var bind = function(events, selector, callback) {
if (typeof events === "string") {
events = [events];
}
events.forEach(function(event) {
document.addEventListener(event, function(e) {
var target = (typeof e.detail !== "number") ? e.detail : e.target;
if (matchesSelector(target, selector)) {
callback(e, target);
}
});
});
};
var rippleStart = function(e, target, callback) {
// Init variables
var $rippleWrapper = target,
$el = $rippleWrapper.parentNode,
$ripple = document.createElement("div"),
elPos = $el.getBoundingClientRect(),
// Mouse pos in most cases
mousePos = {x: e.clientX - elPos.left, y: ((window.ontouchstart) ? e.clientY - window.scrollY: e.clientY) - elPos.top},
scale = "scale(" + Math.round($rippleWrapper.offsetWidth / 5) + ")",
rippleEnd = new CustomEvent("rippleEnd", {detail: $ripple}),
_rippleOpacity = 0.3,
refreshElementStyle;
// If multitouch is detected or some other black magic suff is happening...
if (e.touches) {
mousePos = {x: e.touches[0].clientX - elPos.left, y: e.touches[0].clientY - elPos.top};
}
$ripplecache = $ripple;
// Set ripple class
$ripple.className = "ripple";
// Move ripple to the mouse position
$ripple.setAttribute("style", "left:" + mousePos.x + "px; top:" + mousePos.y + "px;");
// Get the clicked target's text color, this will be applied to the ripple as background-color.
var targetColor = window.getComputedStyle($el).color;
// Convert the rgb color to an rgba color with opacity set to __rippleOpacity__
targetColor = targetColor.replace("rgb", "rgba").replace(")", ", " + _rippleOpacity + ")");
// Insert new ripple into ripple wrapper
$rippleWrapper.appendChild($ripple);
// Make sure the ripple has the class applied (ugly hack but it works)
refreshElementStyle = window.getComputedStyle($ripple).opacity;
// Let other funtions know that this element is animating
$ripple.dataset.animating = 1;
// Set scale value, background-color and opacity to ripple and animate it
$ripple.className = "ripple ripple-on";
// Prepare the style of the ripple
var rippleStyle = [
$ripple.getAttribute("style"),
"background-color: " + targetColor,
"-ms-transform: " + scale,
"-moz-transform" + scale,
"-webkit-transform" + scale,
"transform: " + scale
];
// Apply the style
$ripple.setAttribute("style", rippleStyle.join(";"));
// This function is called when the animation is finished
setTimeout(function() {
// Let know to other functions that this element has finished the animation
$ripple.dataset.animating = 0;
document.dispatchEvent(rippleEnd);
if (callback) {
callback();
}
}, rippleStartTime);
};
var rippleOut = function($ripple) {
// Clear previous animation
$ripple.className = "ripple ripple-on ripple-out";
// Let ripple fade out (with CSS)
setTimeout(function() {
$ripple.remove();
}, rippleOutTime);
};
// Helper, need to know if mouse is up or down
var mouseDown = false;
bind(["mousedown", "touchstart"], "*", function() {
mouseDown = true;
});
bind(["mouseup", "touchend", "mouseout"], "*", function() {
mouseDown = false;
});
// Append ripple wrapper if not exists already
var rippleInit = function(e, target) {
if (target.getElementsByClassName("ripple-wrapper").length === 0) {
target.className += " withripple";
var $rippleWrapper = document.createElement("div");
$rippleWrapper.className = "ripple-wrapper";
target.appendChild($rippleWrapper);
if (window.ontouchstart === null) {
rippleStart(e, $rippleWrapper, function() {
// FIXME: ugly fix for first touchstart event on mobile devices...
$rippleWrapper.getElementsByClassName("ripple")[0].remove();
});
}
}
};
var $ripplecache;
// Events handler
// init RippleJS and start ripple effect on mousedown
bind(["mouseover", "touchstart"], withRipple, rippleInit);
// start ripple effect on mousedown
bind(["mousedown", "touchstart"], ".ripple-wrapper", function(e, $ripple) {
// Start ripple only on left or middle mouse click and touch click
if (e.which === 0 || e.which === 1 || e.which === 2) {
rippleStart(e, $ripple);
}
});
// if animation ends and user is not holding mouse then destroy the ripple
bind("rippleEnd", ".ripple-wrapper .ripple", function(e, $ripple) {
var $ripples = $ripple.parentNode.getElementsByClassName("ripple");
if (!mouseDown || ( $ripples[0] == $ripple && $ripples.length > 1)) {
rippleOut($ripple);
}
});
// Destroy ripple when mouse is not holded anymore if the ripple still exists
bind(["mouseup", "touchend", "mouseout"], ".ripple-wrapper", function() {
var $ripple = $ripplecache;
if ($ripple && $ripple.dataset.animating != 1) {
rippleOut($ripple);
}
});
}
};