├── .bowerrc ├── .gitignore ├── .jshintrc ├── Gruntfile.js ├── README.md ├── bower.json ├── demo └── demo.html ├── dist ├── bs.sm.css ├── bs.sm.js ├── bs.sm.min.css └── bs.sm.min.js ├── package.json └── src ├── bs.sm.css └── bs.sm.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "demo/components", 3 | "json": "bower.json" 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | components 3 | .DS_Store 4 | *.log 5 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "evil": true, 3 | "curly": true, 4 | "eqnull": false, 5 | "browser": true, 6 | "loopfunc": true, 7 | "globals": {} 8 | } 9 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | module.exports = function(grunt) { 4 | 5 | var pkg = grunt.file.readJSON('package.json'), 6 | js_path = 'src/*.js', 7 | css_path = 'src/*.css', 8 | 9 | banner = "/*! Submodal v<%= pkg.version %> */"; 10 | 11 | grunt.initConfig({ 12 | 13 | pkg: pkg, 14 | 15 | cssmin: { 16 | 17 | // App 18 | submodal: { 19 | options: { 20 | banner: banner 21 | }, 22 | files: { 23 | 'dist/bs.sm.min.css': [css_path] 24 | } 25 | } 26 | }, 27 | 28 | uglify: { 29 | app: { 30 | options: { 31 | banner: banner + "\n", 32 | preserveComments: 'some' 33 | }, 34 | files: { 35 | 'dist/bs.sm.min.js': ['dist/bs.sm.js'] 36 | } 37 | } 38 | }, 39 | 40 | jshint: { 41 | all: [js_path], 42 | options: { 43 | jshintrc: true 44 | } 45 | }, 46 | 47 | watch: { 48 | all: { 49 | files: 'src/*.*', 50 | tasks: ['jshint', 'copy', 'cssmin', 'uglify'] 51 | } 52 | }, 53 | 54 | copy: { 55 | misc: { 56 | files: [ 57 | {expand: true, cwd: 'src', src: ['*.css'], dest: 'dist/'}, 58 | {expand: true, cwd: 'src', src: ['*.js'], dest: 'dist/'}, 59 | ] 60 | } 61 | } 62 | }); 63 | 64 | grunt.loadNpmTasks('grunt-contrib-copy'); 65 | grunt.loadNpmTasks('grunt-contrib-cssmin'); 66 | grunt.loadNpmTasks('grunt-contrib-jshint'); 67 | grunt.loadNpmTasks('grunt-contrib-uglify'); 68 | grunt.loadNpmTasks('grunt-contrib-watch'); 69 | 70 | // Register Tasks 71 | grunt.registerTask('default', ['jshint', 'copy', 'cssmin', 'uglify']); 72 | grunt.registerTask('develop', ['jshint', 'copy', 'cssmin', 'uglify', 'watch']); 73 | }; 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Submodal 2 | ================== 3 | 4 | Add submodals to Bootstrap 3.x modals 5 | 6 | 7 | 8 | ## Installing 9 | You can install Submodal via bower 10 | ```bash 11 | $ bower install submodal 12 | ``` 13 | Then include the JS and CSS in your page. 14 | ```html 15 | 16 | 17 | ``` 18 | 19 | ## Building 20 | ```bash 21 | $ npm install 22 | $ npm run-script bower 23 | $ npm run-script build 24 | 25 | # For watching 26 | $ npm run-script build-watch 27 | ``` 28 | 29 | 30 | ## HTML 31 | 32 | Submodals are added to the `.modal-body` element of a modal. It's HTML structure is identical to regular modals excluding the following **two differences** 33 | 34 | * Sub Modals do not have a `.modal-header` element 35 | * Sub Modals must have a class of `.submodal` (in addition to `.modal`) 36 | 37 | ```html 38 | 53 | ``` 54 |
55 | ## Data Attributes 56 | 57 | This resembles Twitter Bootstrap's data API: 58 | 59 | **Opening** 60 | ```html 61 | Open Submodal 62 | ``` 63 | 64 | **Closing** 65 | ```html 66 | Close Submodal 67 | ``` 68 |
69 | ## JS API 70 | 71 | ```javascript 72 | 73 | // Show 74 | $('#my-submodal').submodal('show'); 75 | 76 | // Hide 77 | $('#my-submodal').submodal('hide'); 78 | 79 | // Toggle 80 | $('#my-submodal').submodal('toggle'); 81 | ``` 82 |
83 | ## Events 84 | 85 | ```javascript 86 | 87 | // Before submodal is shown 88 | $('#my-submodal').on('beforeShow', fn); 89 | 90 | // After submodal is shown 91 | $('#my-submodal').on('show', fn); 92 | 93 | // Before submodal is hidden 94 | $('#my-submodal').on('beforeHide', fn); 95 | 96 | // After submodal is hidden 97 | $('#my-submodal').on('hide', fn); 98 | ``` 99 | 100 | ### Upgrading 101 | There are some things to note when upgrading to the v2.0 version 102 | * Only supports Bootstrap 3.x 103 | * The `subModal` namespace has been renamed to `submodal` 104 | 105 | ### Known Issues 106 | Currently, there is a nasty overflow issue that may take a while to fix. Please reference the issues list. 107 | 108 | ### License 109 | 110 | MIT, dawg. 111 | 112 | 113 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "submodal", 3 | "version": "2.0.0", 4 | "main": [ 5 | "dist/bs.sm.css", 6 | "dist/bs.sm.js" 7 | ], 8 | "dependencies": { 9 | "bootstrap": "3.2.0", 10 | "jquery": "2.1.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demo/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Submodal Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 26 | 27 | 28 | 29 | 30 | Show Modal 31 | 32 | 96 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /dist/bs.sm.css: -------------------------------------------------------------------------------- 1 | .submodal{ 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | right: 0; 6 | overflow: hidden; 7 | } 8 | .submodal.transition { 9 | -webkit-transition: all 0.3s ease; 10 | transition: all 0.3s ease; 11 | } 12 | .submodal.in{ top: 0 !important; } 13 | .submodal .modal-dialog { 14 | width: 80%; 15 | margin: 0 auto; 16 | z-index: 9; 17 | } 18 | .submodal .modal-content { 19 | border-top: none; 20 | -webkit-border-top-left-radius: 0; 21 | -moz-border-top-left-radius: 0; 22 | border-top-left-radius: 0; 23 | -webkit-border-top-right-radius: 0; 24 | -moz-border-top-right-radius: 0; 25 | border-top-right-radius: 0; 26 | } 27 | .submodal .modal-header{ display: none; } 28 | .modal.parent .modal-body { overflow: hidden; } 29 | 30 | @media (max-width:767px){ 31 | .submodal .modal-dialog{ 32 | width: 90%; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /dist/bs.sm.js: -------------------------------------------------------------------------------- 1 | ;(function($){ 2 | 3 | var TRANSITION_DURATION = 300; // ms 4 | 5 | var modal = { 6 | open: function(){ 7 | var $submodal = $(this); 8 | var $parentModal = $submodal.closest('.modal:not(.submodal)'); 9 | 10 | // Hide our parents overflow 11 | $parentModal.addClass('parent'); 12 | 13 | // set visibility 14 | $submodal.css({ 15 | display: 'block', 16 | top: $submodal.height() * -1 17 | }); 18 | 19 | // Show 20 | setTimeout(function(){ 21 | $submodal.addClass('transition in').trigger('beforeShow'); 22 | setTimeout(function() { 23 | $submodal.addClass('transition in').trigger('show'); 24 | }, TRANSITION_DURATION); 25 | }, 1); 26 | }, 27 | close: function(){ 28 | 29 | var $submodal = $(this); 30 | var $parentModal = $submodal.closest('.modal:not(.submodal) .modal-body'); 31 | 32 | // Hide 33 | $submodal.removeClass('in').trigger('beforeHide'); 34 | 35 | // Reset styles 36 | setTimeout(function(){ 37 | $submodal.css({ 38 | display: "none", 39 | top: "" 40 | }).removeClass('transition').trigger('hide'); 41 | 42 | // Reset parents overflow 43 | $parentModal.removeClass('parent'); 44 | }, TRANSITION_DURATION); 45 | } 46 | }, 47 | listen = { 48 | open: function(e){ 49 | e.preventDefault(); 50 | 51 | var $submodal = $($(this).attr('href')); 52 | if(!$submodal.length) { 53 | return; 54 | } 55 | 56 | modal.open.call( $submodal ); 57 | }, 58 | close: function(e){ 59 | e.preventDefault(); 60 | modal.close.call( $('.submodal:visible') ); 61 | }, 62 | init: function(){ 63 | $(document) 64 | .on('click', '[data-toggle="submodal"]', listen.open) 65 | .on('click', '[data-dismiss="submodal"]', listen.close); 66 | } 67 | }; 68 | 69 | listen.init(); 70 | 71 | $.fn.extend({ 72 | submodal: function(fn) { 73 | 74 | var methods = { 75 | show: function(){ 76 | modal.open.call($(this)); 77 | }, 78 | hide: function(){ 79 | modal.close.call($(this)); 80 | }, 81 | toggle: function(){ 82 | var $submodal = $(this); 83 | var fn = $subModal.is(':visible') ? 'close' : 'open'; 84 | modal[fn].call($submodal); 85 | } 86 | }; 87 | if( typeof methods[fn]=="function" ){ 88 | return this.each(function() { 89 | methods[fn].call(this); 90 | }); 91 | } 92 | } 93 | }); 94 | })(jQuery); 95 | -------------------------------------------------------------------------------- /dist/bs.sm.min.css: -------------------------------------------------------------------------------- 1 | /*! Submodal v2.0.0 */ 2 | .submodal{position:absolute;top:0;left:0;right:0;overflow:hidden}.submodal.transition{-webkit-transition:all .3s ease;transition:all .3s ease}.submodal.in{top:0!important}.submodal .modal-dialog{width:80%;margin:0 auto;z-index:9}.submodal .modal-content{border-top:none;-webkit-border-top-left-radius:0;-moz-border-top-left-radius:0;border-top-left-radius:0;-webkit-border-top-right-radius:0;-moz-border-top-right-radius:0;border-top-right-radius:0}.submodal .modal-header{display:none}.modal.parent .modal-body{overflow:hidden}@media (max-width:767px){.submodal .modal-dialog{width:90%}} -------------------------------------------------------------------------------- /dist/bs.sm.min.js: -------------------------------------------------------------------------------- 1 | /*! Submodal v2.0.0 */ 2 | !function(a){var b=300,c={open:function(){var c=a(this),d=c.closest(".modal:not(.submodal)");d.addClass("parent"),c.css({display:"block",top:-1*c.height()}),setTimeout(function(){c.addClass("transition in").trigger("beforeShow"),setTimeout(function(){c.addClass("transition in").trigger("show")},b)},1)},close:function(){var c=a(this),d=c.closest(".modal:not(.submodal) .modal-body");c.removeClass("in").trigger("beforeHide"),setTimeout(function(){c.css({display:"none",top:""}).removeClass("transition").trigger("hide"),d.removeClass("parent")},b)}},d={open:function(b){b.preventDefault();var d=a(a(this).attr("href"));d.length&&c.open.call(d)},close:function(b){b.preventDefault(),c.close.call(a(".submodal:visible"))},init:function(){a(document).on("click",'[data-toggle="submodal"]',d.open).on("click",'[data-dismiss="submodal"]',d.close)}};d.init(),a.fn.extend({submodal:function(b){var d={show:function(){c.open.call(a(this))},hide:function(){c.close.call(a(this))},toggle:function(){var b=a(this),d=$subModal.is(":visible")?"close":"open";c[d].call(b)}};return"function"==typeof d[b]?this.each(function(){d[b].call(this)}):void 0}})}(jQuery); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "submodal", 3 | "version": "2.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "./node_modules/grunt-cli/bin/grunt", 7 | "build-watch": "./node_modules/grunt-cli/bin/grunt develop", 8 | "bower": "./node_modules/bower/bin/bower install" 9 | }, 10 | "repository": "git@github.com:jakiestfu/Bootstrap-SubModal.git", 11 | "dependencies": { 12 | "bower": "1.3.9", 13 | "grunt": "0.4.1", 14 | "grunt-cli": "0.1.9", 15 | "grunt-contrib-copy": "0.4.1", 16 | "grunt-contrib-cssmin": "0.9.0", 17 | "grunt-contrib-jshint": "0.8.0", 18 | "grunt-contrib-uglify": "0.2.5", 19 | "grunt-contrib-watch": "0.5.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/bs.sm.css: -------------------------------------------------------------------------------- 1 | .submodal{ 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | right: 0; 6 | overflow: hidden; 7 | } 8 | .submodal.transition { 9 | -webkit-transition: all 0.3s ease; 10 | transition: all 0.3s ease; 11 | } 12 | .submodal.in{ top: 0 !important; } 13 | .submodal .modal-dialog { 14 | width: 80%; 15 | margin: 0 auto; 16 | z-index: 9; 17 | } 18 | .submodal .modal-content { 19 | border-top: none; 20 | -webkit-border-top-left-radius: 0; 21 | -moz-border-top-left-radius: 0; 22 | border-top-left-radius: 0; 23 | -webkit-border-top-right-radius: 0; 24 | -moz-border-top-right-radius: 0; 25 | border-top-right-radius: 0; 26 | } 27 | .submodal .modal-header{ display: none; } 28 | .modal.parent .modal-body { overflow: hidden; } 29 | 30 | @media (max-width:767px){ 31 | .submodal .modal-dialog{ 32 | width: 90%; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/bs.sm.js: -------------------------------------------------------------------------------- 1 | ;(function($){ 2 | 3 | var TRANSITION_DURATION = 300; // ms 4 | 5 | var modal = { 6 | open: function(){ 7 | var $submodal = $(this); 8 | var $parentModal = $submodal.closest('.modal:not(.submodal)'); 9 | 10 | // Hide our parents overflow 11 | $parentModal.addClass('parent'); 12 | 13 | // set visibility 14 | $submodal.css({ 15 | display: 'block', 16 | top: $submodal.height() * -1 17 | }); 18 | 19 | // Show 20 | setTimeout(function(){ 21 | $submodal.addClass('transition in').trigger('beforeShow'); 22 | setTimeout(function() { 23 | $submodal.addClass('transition in').trigger('show'); 24 | }, TRANSITION_DURATION); 25 | }, 1); 26 | }, 27 | close: function(){ 28 | 29 | var $submodal = $(this); 30 | var $parentModal = $submodal.closest('.modal:not(.submodal) .modal-body'); 31 | 32 | // Hide 33 | $submodal.removeClass('in').trigger('beforeHide'); 34 | 35 | // Reset styles 36 | setTimeout(function(){ 37 | $submodal.css({ 38 | display: "none", 39 | top: "" 40 | }).removeClass('transition').trigger('hide'); 41 | 42 | // Reset parents overflow 43 | $parentModal.removeClass('parent'); 44 | }, TRANSITION_DURATION); 45 | } 46 | }, 47 | listen = { 48 | open: function(e){ 49 | e.preventDefault(); 50 | 51 | var $submodal = $($(this).attr('href')); 52 | if(!$submodal.length) { 53 | return; 54 | } 55 | 56 | modal.open.call( $submodal ); 57 | }, 58 | close: function(e){ 59 | e.preventDefault(); 60 | modal.close.call( $('.submodal:visible') ); 61 | }, 62 | init: function(){ 63 | $(document) 64 | .on('click', '[data-toggle="submodal"]', listen.open) 65 | .on('click', '[data-dismiss="submodal"]', listen.close); 66 | } 67 | }; 68 | 69 | listen.init(); 70 | 71 | $.fn.extend({ 72 | submodal: function(fn) { 73 | 74 | var methods = { 75 | show: function(){ 76 | modal.open.call($(this)); 77 | }, 78 | hide: function(){ 79 | modal.close.call($(this)); 80 | }, 81 | toggle: function(){ 82 | var $submodal = $(this); 83 | var fn = $subModal.is(':visible') ? 'close' : 'open'; 84 | modal[fn].call($submodal); 85 | } 86 | }; 87 | if( typeof methods[fn]=="function" ){ 88 | return this.each(function() { 89 | methods[fn].call(this); 90 | }); 91 | } 92 | } 93 | }); 94 | })(jQuery); 95 | --------------------------------------------------------------------------------