├── .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 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
[...submodal content...]
46 |
47 |
[parent modal content]
48 |
49 |
50 |
51 |
52 |
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 |
33 |
34 |
35 |
36 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
Are you sure you want to close your account?
You won't be able to undo this.
53 |
54 |
62 |
63 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
90 |
91 |
92 |
93 |
94 |
95 |
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 |
--------------------------------------------------------------------------------