easy_menu 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e4f6990701058e22283c180fef8fe41a1ec80951
4
+ data.tar.gz: 28e9fc33e9e1e32424885884bf9967ba8cc48129
5
+ SHA512:
6
+ metadata.gz: 6a2f590cdc0d64bbeb095272a4008cccb46925382564126dfe0d9b102becbd82a5600c13ae2b35495ebea4c6abfbdcd2723d03389f80a5a9ad4cbb4c7cc41b15
7
+ data.tar.gz: a6c806734115f44e2ceea6a437956c75b8948b1ec3c6b8e3b193b7273a78eef5db66db9dce63fe0d91f7cbc8293ed57bb2715920fe99c6bd1b95a37d423a40fa
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gem "rails", "~> 3.1.0"
data/MIT-LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2011 by Nicholas Jakobsen
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,4 @@
1
+ # Easy Menu
2
+ ## Make menus the easy way
3
+
4
+ Supports Prototype and Jquery
Binary file
@@ -0,0 +1,59 @@
1
+ $(document).ready(function() {
2
+ var menuBarRootSelector = '.menu_bar:not(.no_js) ';
3
+
4
+ // Because some browsers don't support submit buttons outside of forms triggering form submits,
5
+ // do it in javascript just to make sure it happens
6
+ $(document).on('click', 'input[type=submit][form]', function(){
7
+ if (form = document.getElementById(this.getAttribute('form'))) {
8
+ // If the form is already submitting, we want don't want to submit it again.
9
+ if ($(form).hasClass('submitting')) {
10
+ return false;
11
+ }
12
+
13
+ var tempCommit = $("<input type='submit' name='commit' style='display:none'>");
14
+ tempCommit.attr('value', $(this).attr('value'));
15
+ form.appendChild(tempCommit[0]);
16
+ tempCommit.click();
17
+ return false;
18
+ }
19
+ });
20
+ // Used by the above click handler to determine if the browser already submitted the form.
21
+ $('form').submit(function() {
22
+ $(this).addClass('submitting');
23
+ });
24
+
25
+ // Allow users to open an close menus by clicking
26
+ $(menuBarRootSelector + '.menu_bar_content.with_menu').removeClass('no_js');
27
+ $(menuBarRootSelector + '.menu_bar_content.with_menu .menu_bar_item').click(function(){
28
+ var mbc = $(this).closest('.menu_bar_content');
29
+ $(menuBarRootSelector + '.menu_bar_content.with_menu').not(mbc).removeClass('open');
30
+ mbc.toggleClass('open');
31
+ });
32
+
33
+ $('body').click(function(event){
34
+ if ($(event.target).closest('.menu_bar_content.with_menu .menu_bar_item').length > 0) { return } // Don't close the menus if the click came from a menu
35
+ $(menuBarRootSelector + '.menu_bar_content.with_menu.open').removeClass('open');
36
+ });
37
+
38
+ // Disable Elements with a disable condition when that condition is met
39
+ $(menuBarRootSelector + '.menu_bar_item[data-disable-event-element]').each(function(){
40
+ var mbi = $(this);
41
+ var observableElement = eval(mbi.getAttribute('data-disable-event-element'));
42
+ var event = mbi.getAttribute('data-disable-event');
43
+ var condition = mbi.getAttribute('data-disable-condition') || true;
44
+
45
+ var setState = function(){
46
+ if (eval(condition)){
47
+ mbi.addClass('disabled');
48
+ } else {
49
+ mbi.removeClass('disabled');
50
+ }
51
+ }
52
+
53
+ // Init the current state and bind the observer
54
+ if (observableElement && event){
55
+ setState();
56
+ observableElement.bind(event, setState);
57
+ }
58
+ });
59
+ });
@@ -0,0 +1,56 @@
1
+ $(document).observe('dom:loaded', function() {
2
+ var menuBarRootSelector = '.menu_bar:not(.no_js) ';
3
+
4
+ // Because some browsers don't support submit buttons outside of forms triggering form submits,
5
+ // do it in javascript just to make sure it happens
6
+ Element.on(document.body, 'click', 'input[type=submit][form]', function(event){
7
+ var element = event.element();
8
+ if (form = document.getElementById(element.getAttribute('form'))) {
9
+ event.stop();
10
+ // If the form is already submitting, we want don't want to submit it again.
11
+ if ($(form).hasClassName('submitting')) {
12
+ return;
13
+ }
14
+
15
+ var tempCommit = new Element('input', {type:'submit', name:'commit', style:'display: none', 'value':element.getAttribute('value')});
16
+ form.appendChild(tempCommit[0]);
17
+ tempCommit.click();
18
+ }
19
+ });
20
+ // Used by the above click handler to determine if the browser already submitted the form.
21
+ $$('form').invoke('observe', 'submit', function(event) { event.element().addClassName('submitting') });
22
+
23
+ // Allow users to open an close menus by clicking
24
+ $$(menuBarRootSelector + '.menu_bar_content.with_menu').invoke('removeClassName', 'no_js');
25
+ $$(menuBarRootSelector + '.menu_bar_content.with_menu .menu_bar_item').invoke('observe', 'click', function(event){
26
+ var mbc = $(event.element()).up('.menu_bar_content');
27
+ $$('.menu_bar_content.with_menu').without(mbc).invoke('removeClassName', 'open');
28
+ mbc.toggleClassName('open');
29
+ });
30
+ Element.observe(document.body, 'click', function(event){
31
+ if (event.findElement('.menu_bar_content.with_menu .menu_bar_item')){ return } // Don't close the menus if the click came from a menu trigger
32
+ $$(menuBarRootSelector + '.menu_bar_content.with_menu.open').invoke('removeClassName', 'open');
33
+ });
34
+
35
+ // Disable Elements with a disable condition when that condition is met
36
+ $$(menuBarRootSelector + '.menu_bar_item[data-disable-event-element]').each(function(element){
37
+ var mbi = $(element);
38
+ var observableElement = eval(mbi.getAttribute('data-disable-event-element'));
39
+ var event = mbi.getAttribute('data-disable-event');
40
+ var condition = mbi.getAttribute('data-disable-condition') || true;
41
+
42
+ function setState(){
43
+ if (eval(condition)){
44
+ mbi.addClassName('disabled');
45
+ } else {
46
+ mbi.removeClassName('disabled');
47
+ }
48
+ }
49
+
50
+ // Init the current state and bind the observer
51
+ if (observableElement && event){
52
+ setState();
53
+ $(observableElement).observe(event, setState);
54
+ }
55
+ });
56
+ });
@@ -0,0 +1,252 @@
1
+ // VARIABLES
2
+ $menu-bar-item-horizontal-padding: 7px;
3
+ $menu-bar-item-height: 26px;
4
+ $menu-bar-item-min-width: $menu-bar-item-height;
5
+ $menu-bar-item-border-radius: 2px;
6
+ $menu-bar-item-border-darken: .1;
7
+ $menu-bar-item-border-shadow-darken: .3;
8
+ $menu-bar-item-border-width: 1px;
9
+ $menu-bar-item-border-colour: rgba(0,0,0, $menu-bar-item-border-darken);
10
+ $menu-bar-item-border-shadow-colour: rgba(0,0,0, $menu-bar-item-border-shadow-darken);
11
+ $menu-bar-item-light-colour: #FEFEFE;
12
+ $menu-bar-item-dark-colour: #DDDEDE;
13
+ $menu-bar-item-text-color: #333;
14
+ $menu-bar-item-disabled-text-color: #999;
15
+ $menu-bar-item-spacing: 10px;
16
+ $menu-bar-height: 2*$menu-bar-item-border-width + $menu-bar-item-height;
17
+ $menu-bar-font: bold 12px 'Arial', 'sans-serif';
18
+ $menu-bar-separator-height: 26px;
19
+ $menu-border-width: 1px;
20
+ $menu-vertical-padding: 0.2em;
21
+ $menu-item-vertical-padding: 0.4em;
22
+ $menu-item-horizontal-padding: 2em;
23
+ $menu-item-disabled-text-color: #AAA;
24
+ // Make the button border appear even when in a group by popping the button up above its siblings
25
+ $menu-bar-item-zindex: 1;
26
+ $menu-bar-item-hover-zindex: 2;
27
+ $menu-bar-item-pressed-zindex: 3;
28
+
29
+ // HELPERS
30
+
31
+ @mixin no-select{
32
+ -moz-user-select: none;
33
+ -webkit-user-select: none;
34
+ user-select: none;
35
+ }
36
+ @mixin no-drag{
37
+ -moz-user-drag: none;
38
+ -webkit-user-drag: none;
39
+ user-drag: none;
40
+ }
41
+ @mixin menu-bar-item-border-shadow-with-fallback($background-colour: $menu-bar-item-dark-colour){
42
+ border-bottom-color: darken($background-colour, percentage($menu-bar-item-border-shadow-darken)); // Fallback
43
+ border-bottom-color: $menu-bar-item-border-shadow-colour;
44
+ }
45
+ @mixin menu-bar-item-border-colour-with-fallback($background-colour: $menu-bar-item-dark-colour){
46
+ border-color: darken($background-colour, percentage($menu-bar-item-border-darken)); // Fallback
47
+ border-color: $menu-bar-item-border-colour;
48
+ @include menu-bar-item-border-shadow-with-fallback($background-colour);
49
+ }
50
+ @mixin menu-bar-item-border($background-colour: $menu-bar-item-dark-colour){
51
+ border: $menu-bar-item-border-width solid;
52
+ @include menu-bar-item-border-colour-with-fallback($background-colour);
53
+ border-radius: $menu-bar-item-border-radius;
54
+ -moz-transition: border-color 0.2s linear;
55
+ -webkit-transition: border-color 0.2s linear;
56
+
57
+ }
58
+ @mixin gradient($start, $end){
59
+ // IE 9+
60
+ background-image: data_url('image/svg+xml', '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">
61
+ <linearGradient id="grad-ucgg-generated" gradientUnits="userSpaceOnUse" x1="0%" y1="0%" x2="0%" y2="100%">
62
+ <stop offset="0%" stop-color="#{$start}" stop-opacity="1"/>
63
+ <stop offset="100%" stop-color="#{$end}" stop-opacity="1"/>
64
+ </linearGradient>
65
+ <rect x="0" y="0" width="1" height="1" fill="url(#grad-ucgg-generated)" />
66
+ </svg>');
67
+
68
+ background-image: -moz-linear-gradient(top, $start 0%, $end 100%);
69
+ background-image: -webkit-linear-gradient(top, $start 0%, $end 100%);
70
+ background-image: linear-gradient(top, $start 0%, $end 100%);
71
+ background-repeat: no-repeat;
72
+ }
73
+ @mixin button_colour($light, $dark, $text-colour: white, $text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25), $shift: 15px){
74
+ color: $text-colour !important; // Make this important because host app may override hover colours, but we don't want to be affected
75
+ text-shadow: $text-shadow;
76
+ @include menu-bar-item-border($dark);
77
+
78
+ background-color: mix($light, $dark);
79
+
80
+ // Don't override the background if the browser doesn't support gradients otherwise the buttons will be too dark
81
+ &:not(oldbrowser){
82
+ @include gradient($light, $dark);
83
+ background-color: $dark;
84
+ }
85
+
86
+ &:hover:not(:active):not(.disabled):not(.selected){
87
+ -moz-transition: background-position 0.1s linear;
88
+ -webkit-transition: background-position 0.1s linear;
89
+ background-position: 0 (-$shift);
90
+ }
91
+ }
92
+
93
+ // STYLES
94
+ @mixin menu_bar_item{
95
+ @include menu_bar_item_base;
96
+ @include menu_bar_item_hover;
97
+ @include menu_bar_item_pressed;
98
+
99
+ @include menu_bar_item_default_colour;
100
+ &.danger{ @include menu_bar_item_danger_colour }
101
+ &.primary{ @include menu_bar_item_primary_colour }
102
+ &.info{ @include menu_bar_item_info_colour }
103
+ &.success{ @include menu_bar_item_success_colour }
104
+ &.warning{ @include menu_bar_item_warning_colour }
105
+ &.inverse{ @include menu_bar_item_inverse_colour }
106
+
107
+ @include menu_bar_item_disabled;
108
+ }
109
+
110
+ @mixin menu_bar_item_base{
111
+ @include menu-bar-item-border;
112
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); // Bevelled top edge
113
+ min-width: $menu-bar-item-min-width - 2*$menu-bar-item-horizontal-padding;
114
+ padding: 0 $menu-bar-item-horizontal-padding;
115
+ display: inline-block;
116
+ font: $menu-bar-font;
117
+ line-height: $menu-bar-item-height;
118
+ white-space: nowrap;
119
+ text-align: center;
120
+ cursor: pointer;
121
+
122
+ // Place the button above any nested menu
123
+ position: relative;
124
+ z-index: $menu-bar-item-zindex;
125
+
126
+ img{
127
+ vertical-align: middle;
128
+ position: relative;
129
+ bottom: 1px;
130
+ }
131
+
132
+ > a, > label{
133
+ cursor: pointer; // Override browser default on labels
134
+ text-decoration: none;
135
+ // Make the a fill the hoverable area of the button so clicking anywhere triggers the link
136
+ margin: -$menu-bar-item-horizontal-padding (-$menu-bar-item-horizontal-padding - $menu-bar-item-border-width);
137
+ padding: $menu-bar-item-horizontal-padding ($menu-bar-item-horizontal-padding + $menu-bar-item-border-width);
138
+ }
139
+ a:link, a:visited{
140
+ color: inherit;
141
+ }
142
+
143
+ // Hide the inputs without making them display none. IE doesn't click them if they are display none.
144
+ input[type='submit']{
145
+ position: absolute;
146
+ z-index: -1;
147
+ left: -9999px;
148
+ }
149
+ }
150
+ @mixin menu_bar_item_hover{
151
+ &:hover{
152
+ border-color: #888; // Fallback
153
+ border-color: rgba(0,0,0, $menu-bar-item-border-darken * 2);
154
+ border-bottom-color: rgba(0,0,0, $menu-bar-item-border-shadow-darken * 2);
155
+ z-index: $menu-bar-item-hover-zindex;
156
+ position: relative;
157
+ }
158
+ }
159
+ @mixin menu_bar_item_pressed{
160
+ &:active, &.selected{
161
+ border-color: #555; // Fallback
162
+ border-color: rgba(0,0,0, $menu-bar-item-border-darken * 2);
163
+ box-shadow: 0 -1px 1px rgba(255, 255, 255, 0.4) inset, 0 2px 2px rgba(0, 0, 0, .3) inset;
164
+ z-index: $menu-bar-item-pressed-zindex;
165
+ position: relative;
166
+ }
167
+ }
168
+
169
+ @mixin menu_bar_item_disabled{
170
+ &.disabled{
171
+ @include button_colour(#fefefe, #dddede, $menu-bar-item-disabled-text-color, 0 1px 1px rgba(255, 255, 255, 0.75));
172
+ cursor: default;
173
+ }
174
+ }
175
+
176
+ @mixin menu_bar_item_default_colour{
177
+ @include button_colour($menu-bar-item-light-colour, $menu-bar-item-dark-colour, $menu-bar-item-text-color, 0 1px 1px rgba(255, 255, 255, 0.75), 5px);
178
+ }
179
+ @mixin menu_bar_item_danger_colour{
180
+ @include button_colour(#EE5F5B, #BD362F);
181
+ }
182
+ @mixin menu_bar_item_primary_colour{
183
+ @include button_colour(#0088CC, #0055CC);
184
+ }
185
+ @mixin menu_bar_item_info_colour{
186
+ @include button_colour(#5BC0DE, #2F96B4);
187
+ }
188
+ @mixin menu_bar_item_success_colour{
189
+ @include button_colour(#62C462, #51A351);
190
+ }
191
+ @mixin menu_bar_item_warning_colour{
192
+ @include button_colour(#FBB450, #F89406);
193
+ }
194
+ @mixin menu_bar_item_inverse_colour{
195
+ @include button_colour(#555555, #222222);
196
+ }
197
+
198
+ @mixin text-field{
199
+ border-radius: $menu-bar-item-border-radius;
200
+ border: 1px solid #AAA;
201
+ height: $menu-bar-height;
202
+ -moz-box-sizing: border-box;
203
+ -webkit-box-sizing: border-box;
204
+ box-sizing: border-box;
205
+ margin: 0;
206
+ padding-left: $menu-bar-item-horizontal-padding; // Define individually so we don't override top padding. IE8 issue
207
+ padding-right: $menu-bar-item-horizontal-padding; // Define individually so we don't override top padding. IE8 issue
208
+ }
209
+
210
+ @mixin menu_item{
211
+ @include menu_item_base;
212
+ @include menu_item_hover;
213
+ @include menu_item_disabled;
214
+ }
215
+
216
+ @mixin menu_item_base{
217
+ white-space: nowrap;
218
+ font: $menu-bar-font;
219
+ padding: $menu-item-vertical-padding $menu-item-horizontal-padding;
220
+ cursor: pointer;
221
+
222
+ a {
223
+ color: $menu-bar-item-text-color;
224
+ padding: $menu-item-vertical-padding $menu-item-horizontal-padding;
225
+ margin: (-$menu-item-vertical-padding) (-$menu-item-horizontal-padding);
226
+ display: block;
227
+ text-decoration: none;
228
+ }
229
+ }
230
+ @mixin menu_item_hover {
231
+ &.selected a {
232
+ background: url("/assets/checkbox_selected.png") no-repeat 5px center;
233
+
234
+ &:hover {
235
+ background: url("/assets/checkbox_selected_hovered.png") no-repeat 5px center;
236
+ }
237
+ }
238
+ &:hover {
239
+ background: #08C;
240
+ color: white;
241
+
242
+ a {
243
+ color: inherit;
244
+ }
245
+ }
246
+ }
247
+ @mixin menu_item_disabled{
248
+ &.disabled, &.disabled a{
249
+ cursor: default;
250
+ color: $menu-item-disabled-text-color;
251
+ }
252
+ }
@@ -0,0 +1,216 @@
1
+ /*
2
+ * This is a manifest file that'll automatically include all the stylesheets available in this directory
3
+ * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
4
+ * the top of the compiled file, but it's generally better to create a new file per style scope.
5
+ *= require_self
6
+ */
7
+
8
+ @import 'easy_menu_mixins';
9
+
10
+ // STYLES
11
+ .menu_bar.default_theme{
12
+ display: block;
13
+ margin: 0;
14
+ padding: 0;
15
+ list-style: none;
16
+ height: $menu-bar-height;
17
+ position: relative;
18
+ z-index: 0; // Scope all positioned elements z-index within the menu so they don't move above or below other page elements, only each other
19
+
20
+ // MENU BAR_GROUP
21
+ .menu_bar_group{
22
+ padding: 0;
23
+
24
+ .menu_bar_content{
25
+ margin-left: -$menu-bar-item-spacing - $menu-bar-item-border-width;
26
+
27
+ .menu_bar_item{
28
+ border-radius: 0;
29
+ }
30
+ }
31
+
32
+ .menu_bar_content:first-child{
33
+ margin-left: 0;
34
+
35
+ .menu_bar_item{
36
+ border-top-left-radius: $menu-bar-item-border-radius;
37
+ border-bottom-left-radius: $menu-bar-item-border-radius;
38
+ }
39
+ }
40
+ .menu_bar_content:last-child{
41
+ margin-right: 0;
42
+
43
+ .menu_bar_item{
44
+ border-top-right-radius: $menu-bar-item-border-radius;
45
+ border-bottom-right-radius: $menu-bar-item-border-radius;
46
+ }
47
+ }
48
+
49
+ }
50
+
51
+ // MENU BAR CONTENT
52
+ .menu_bar_content{
53
+ display: inline-block;
54
+ vertical-align: middle;
55
+ margin: 0 $menu-bar-item-spacing 0 0;
56
+
57
+ // MENU BAR ITEMS
58
+ .menu_bar_item{
59
+ @include menu_bar_item;
60
+ @include no-select;
61
+ @include no-drag;
62
+ }
63
+
64
+ // MENU BAR SEPARATOR
65
+ .menu_bar_separator{
66
+ background-color: #AAA;
67
+ border-right: 1px solid #F7F7F7;
68
+ opacity: 0.5;
69
+ display: inline-block;
70
+ height: $menu-bar-separator-height;
71
+ padding-right: 1px;
72
+ vertical-align: middle;
73
+ }
74
+
75
+ // MENUS
76
+ .menu{
77
+ background: none repeat scroll 0 0 white;
78
+ @include menu-bar-item-border;
79
+ border-radius: 0 0 2px 2px;
80
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
81
+ list-style: none outside none;
82
+ margin-top: -$menu-border-width - $menu-bar-item-border-width; // Make the button appear to be part of the clicked trigger button
83
+ padding: $menu-vertical-padding 0;
84
+ position: absolute;
85
+ display: none;
86
+
87
+ .menu_content{
88
+ display: block;
89
+ padding: 0;
90
+ margin: 0;
91
+ position: relative;
92
+
93
+ .menu_item{
94
+ @include menu_item;
95
+ }
96
+ .menu_group_title{
97
+ @include menu_item_base;
98
+ background: none repeat scroll 0 0 #EEEEEE;
99
+ color: #888888;
100
+ font-weight: bold;
101
+ margin: $menu-item-vertical-padding 0;
102
+ white-space: nowrap;
103
+ }
104
+ .menu_group{
105
+ padding: 0;
106
+ margin: 0;
107
+ }
108
+
109
+ .menu_separator{
110
+ border-top: 1px solid #E0E0E0;
111
+ margin: $menu-item-vertical-padding 0;
112
+ }
113
+
114
+ }
115
+
116
+ // Subsequent menu groups
117
+ .menu_content + .menu_content .menu_group_title{
118
+ border-top: 1px solid #E0E0E0;
119
+ margin-top: $menu-item-vertical-padding;
120
+ }
121
+
122
+ }
123
+ &.with_menu {
124
+ .menu_bar_item .arrow{
125
+ background: url('/assets/arrows.png') center 87px;
126
+ display: inline-block;
127
+ margin-left: 3px;
128
+ height: 10px;
129
+ width: 10px;
130
+ vertical-align: middle;
131
+ }
132
+ }
133
+ &.with_menu.no_js:hover, &.with_menu.open{
134
+ .menu_bar_item {
135
+ background: white;
136
+ border-radius: 2px 2px 0 0;
137
+ @include menu-bar-item-border-colour-with-fallback;
138
+ border-bottom: none;
139
+ margin-bottom: $menu-bar-item-border-width; // Make up for the loss of border so the button doesn't shift
140
+ box-shadow: none;
141
+ }
142
+
143
+ z-index: $menu-bar-item-pressed-zindex + 2; // Position all the contents of this menu_content above any other menu bar item (the pressed item being the highest)
144
+ .menu {
145
+ display: block;
146
+ }
147
+ }
148
+
149
+ // Allow right alignment of buttons
150
+ &.right{
151
+ float: right;
152
+ margin: 0 0 0 $menu-bar-item-spacing;
153
+
154
+ .menu {
155
+ right: 0;
156
+ }
157
+ }
158
+
159
+ // CLICK BLOCKER (only appears if the element is disabled)
160
+ position: relative; // So click blocker can fill the button area
161
+ .disabled + .click_blocker{
162
+ height: 100%;
163
+ left: 0;
164
+ position: absolute;
165
+ top: 0;
166
+ width: 100%;
167
+ z-index: $menu-bar-item-zindex + 3;
168
+ }
169
+
170
+ // INPUTS IN THE MENU
171
+ form{
172
+ display: inline-block;
173
+ }
174
+ input[type=search]{
175
+ @include text-field;
176
+ -webkit-appearance: textfield;
177
+ -webkit-padding-start: 0;
178
+ -webkit-padding-end: 0;
179
+ }
180
+ input[type=text]{
181
+ @include text-field;
182
+ }
183
+
184
+ .menu_bar_item{
185
+ input[type='checkbox']{
186
+ // Make input align properly when used in the menu bar, especially if used in a menu trigger in IE.
187
+ margin: 0;
188
+ display: inline-block;
189
+ vertical-align: top;
190
+ height: $menu-bar-item-height;
191
+ }
192
+ select{
193
+ @include menu_bar_item_base;
194
+ // @include menu_bar_item_hover;
195
+ height: $menu-bar-height;
196
+ font: $menu-bar-font;
197
+ padding: 0.4em;
198
+
199
+ option{
200
+ text-shadow: none;
201
+ padding: 0.4em;
202
+ }
203
+ }
204
+ }
205
+ }
206
+ }
207
+ .menu_bar.toggle_menu{
208
+ .menu_bar_content:first-child .menu_bar_item{
209
+ border-top-left-radius: 14px;
210
+ border-bottom-left-radius: 14px;
211
+ }
212
+ .menu_bar_content:last-child .menu_bar_item{
213
+ border-top-right-radius: 14px;
214
+ border-bottom-right-radius: 14px;
215
+ }
216
+ }
@@ -0,0 +1,310 @@
1
+ # TODO make menu bar group an actual group instead of a state toggle
2
+ class MenuBar
3
+ include EasyMenu::Helpers
4
+ include EasyMenu::Configuration
5
+
6
+ attr_reader :content
7
+
8
+ def initialize(template, options = {})
9
+ @template = template
10
+ @options = options.reverse_merge(:theme => config[:default_theme_class], :remove_dangling_separators => true)
11
+ config.merge! options[:config] if options[:config] # Allow per menu overriding of configuration
12
+ config[:template] = @template
13
+
14
+ @content = []
15
+
16
+ yield self if block_given?
17
+ end
18
+
19
+ def empty?
20
+ @content.blank?
21
+ end
22
+
23
+ def group(options = {})
24
+ initialize_options(options)
25
+
26
+ mbg = MenuBarGroup.new(@template, options.merge(@options.slice(:config)))
27
+ mbc = MenuBarContent.new(config, mbg, options[:menu_bar_content])
28
+
29
+ yield mbg if block_given?
30
+
31
+ @content << mbc
32
+
33
+ return mbg
34
+ end
35
+
36
+ def menu_bar_content(content = nil, options = {}, &block)
37
+ initialize_options(options)
38
+
39
+ if block_given?
40
+ options = content || options
41
+ content = block.call
42
+ end
43
+
44
+ mbc = MenuBarContent.new(config, content, options)
45
+ @content << mbc
46
+
47
+ return mbc
48
+ end
49
+
50
+ def menu_bar_item(content, options = {})
51
+ initialize_options(options)
52
+
53
+ raise if config[:template].is_a?(Hash) || config[:template].nil?
54
+
55
+ mbi = MenuBarItem.new config, content, options
56
+ @content << MenuBarContent.new(config, mbi, options[:menu_bar_content])
57
+
58
+ return mbi
59
+ end
60
+
61
+ def menu_bar_input(content, options = {})
62
+ initialize_options(options)
63
+
64
+ mbin = MenuBarInput.new config, content, options
65
+ mbi = MenuBarItem.new config, mbin, options[:menu_bar_item]
66
+ @content << MenuBarContent.new(config, mbi, options[:menu_bar_content])
67
+
68
+ return mbi
69
+ end
70
+
71
+ def menu(button_text, options = {})
72
+ initialize_options(options)
73
+
74
+ arrow = @template.content_tag(:span, '', :class => config[:menu_bar_item_arrow_class])
75
+
76
+ m = Menu.new(config, options)
77
+ mbt = MenuBarTrigger.new(config, button_text.html_safe + arrow, m, options[:menu_bar_item])
78
+
79
+ yield m if block_given?
80
+
81
+ # We give the menu bar content a special class so we can treat its contents differently than one without a menu inside
82
+ @content << MenuBarContent.new(config, mbt, merge_class(options[:menu_bar_content], config[:menu_bar_content_with_menu_class]))
83
+
84
+ return m
85
+ end
86
+
87
+ def separator(options = {})
88
+ s = @template.content_tag :div, '', :class => config[:menu_bar_separator_class]
89
+ @content << MenuBarContent.new(config, s, options.reverse_merge(:remove_if_dangling => @options[:remove_dangling_separators]))
90
+
91
+ return s
92
+ end
93
+
94
+ def to_s
95
+ @content.pop if @content.last && @content.last.options[:remove_if_dangling]
96
+ wrap_content(@content.join.html_safe)
97
+ end
98
+
99
+ private
100
+
101
+ def initialize_options(options)
102
+ options[:menu_bar_item] ||= {}
103
+ options[:menu_bar_content] ||= {}
104
+
105
+ # Alignment always lies with the content wrapper
106
+ options[:menu_bar_content][:align] = options.delete(:align)
107
+
108
+ return options
109
+ end
110
+
111
+ def html_options
112
+ html_opts = @options.slice(*html_option_keys)
113
+
114
+ # Set up the css class
115
+ merge_class(html_opts, css_class, @options[:theme])
116
+ merge_class(html_opts, 'no_js') if @options[:js] == false
117
+
118
+ return html_opts
119
+ end
120
+
121
+ # ABSTRACT CLASSES
122
+
123
+ class AbstractContent
124
+ include EasyMenu::Helpers
125
+
126
+ attr_reader :content, :config, :options
127
+ def initialize(config, content, options = {})
128
+ raise if config[:template].is_a?(Hash) || config[:template].nil?
129
+ @config = config
130
+ @template = config[:template]
131
+ @content = Array(content)
132
+ @options = options
133
+ end
134
+
135
+ def empty?
136
+ @content.join.blank?
137
+ end
138
+
139
+ def to_s
140
+ empty? ? '' : wrap_content(@content.join.html_safe) # Don't render anything if empty
141
+ end
142
+
143
+ private
144
+
145
+ def html_options
146
+ html_opts = @options.slice(*html_option_keys)
147
+ merge_class(html_opts, css_class, @options[:align])
148
+
149
+ return html_opts
150
+ end
151
+ end
152
+
153
+ class AbstractItem < AbstractContent
154
+ def selected(condition = :unset)
155
+ @options[:selected] = (condition == :unset ? true : condition)
156
+
157
+ return self
158
+ end
159
+
160
+ def disabled(*args)
161
+ @click_blocker_html_options = args.extract_options!
162
+ @options[:disabled] = args.present? ? args.first : true
163
+
164
+ return self
165
+ end
166
+
167
+ # Set the button up to disable when a particular DOM Event occurs
168
+ def disable_when(observable_dom_element, dom_event, js_condition, click_blocker_html_options = {})
169
+ @options[:disable_when] = {:element => observable_dom_element, :event => dom_event, :condition => js_condition}
170
+ @click_blocker_html_options = click_blocker_html_options
171
+ return self
172
+ end
173
+
174
+ private
175
+
176
+ def wrap_content(content)
177
+ output = super
178
+ output << @template.content_tag(:div, '', click_blocker_html_options) if @options[:disabled] || @options[:disable_when]
179
+
180
+ return output
181
+ end
182
+
183
+ def html_options
184
+ html_opts = @options.slice(*html_option_keys)
185
+
186
+ if @options[:disable_when]
187
+ html_opts[:'data-disable-event-element'] = @options[:disable_when][:element]
188
+ html_opts[:'data-disable-event'] = @options[:disable_when][:event]
189
+ html_opts[:'data-disable-condition'] = @options[:disable_when][:condition]
190
+ end
191
+
192
+ merge_class(html_opts, css_class)
193
+ merge_class(html_opts, config[:selected_class]) if @options[:selected]
194
+ merge_class(html_opts, config[:disabled_class]) if @options[:disabled]
195
+
196
+ return html_opts
197
+ end
198
+
199
+ def click_blocker_html_options
200
+ html_opts = @click_blocker_html_options
201
+ html_opts.reverse_merge! :title => @options[:title] # Default the title text to be the same as the unblocked title text
202
+
203
+ merge_class(html_opts, config[:click_blocker_class])
204
+
205
+ return html_opts
206
+ end
207
+ end
208
+
209
+ # CLASSES
210
+
211
+ class MenuBarGroup < MenuBar
212
+ end
213
+
214
+ class MenuBarContent < AbstractContent
215
+ end
216
+
217
+ class MenuBarItem < AbstractItem
218
+ end
219
+
220
+ class MenuBarTrigger < MenuBarItem
221
+ def initialize(config, content, menu, options)
222
+ @menu = menu
223
+ super(config, content, options)
224
+ end
225
+
226
+ # If the menu has no content, don't show the menu bar trigger
227
+ def empty?
228
+ @menu.empty?
229
+ end
230
+
231
+ def to_s
232
+ super + @menu.to_s
233
+ end
234
+ end
235
+
236
+ class MenuBarInput < AbstractContent
237
+ end
238
+
239
+ class Menu < AbstractContent
240
+ def initialize(config, options = {})
241
+ raise if config[:template].is_a?(Hash) || config[:template].nil?
242
+
243
+ @config = config
244
+ @template = config[:template]
245
+ @options = options
246
+ @content = []
247
+
248
+ yield self if block_given?
249
+ end
250
+
251
+ def group(title, options = {})
252
+ initialize_options(options)
253
+
254
+ mgt = @template.content_tag(config[:menu_group_title_element], title, merge_class(options[:menu_group_title], config[:menu_group_title_class]))
255
+ mg = MenuGroup.new(config, options)
256
+
257
+ yield mg if block_given?
258
+
259
+ @content << MenuContent.new(config, [mgt, mg], options[:menu_content])
260
+
261
+ return mg
262
+ end
263
+
264
+ def menu_content(content = nil, options = {}, &block)
265
+ initialize_options(options)
266
+
267
+ if block_given?
268
+ options = content || options
269
+ content = block.call
270
+ end
271
+
272
+ @content << MenuContent.new(config, content, options)
273
+ end
274
+
275
+ def menu_item(content, options = {})
276
+ initialize_options(options)
277
+
278
+ mi = MenuItem.new(config, content, options)
279
+ @content << MenuContent.new(config, mi, options[:menu_content])
280
+
281
+ return mi
282
+ end
283
+
284
+ def separator
285
+ s = @template.content_tag :div, '', :class => config[:menu_separator_class]
286
+ @content << MenuContent.new(config, s)
287
+
288
+ return s
289
+ end
290
+
291
+ private
292
+
293
+ def initialize_options(options)
294
+ options[:menu_item] ||= {}
295
+ options[:menu_content] ||= {}
296
+ options[:menu_group_title] ||= {}
297
+
298
+ return options
299
+ end
300
+ end
301
+
302
+ class MenuGroup < Menu
303
+ end
304
+
305
+ class MenuContent < AbstractContent
306
+ end
307
+
308
+ class MenuItem < AbstractItem
309
+ end
310
+ end
@@ -0,0 +1,5 @@
1
+ module MenuBarHelper
2
+ def menu_bar(*args, &block)
3
+ MenuBar.new(self, *args, &block)
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ require 'base64'
2
+
3
+ # tools.ietf.org/html/rfc2397
4
+ # developer.mozilla.org/en/data_URIs
5
+
6
+ if defined?(Sass)
7
+ puts "Loading Easy Menu SCSS extensions"
8
+ module Sass::Script::Functions
9
+ def data_url(content_type, content)
10
+ outuri = "data:#{unquote(content_type)};base64,#{Base64.encode64(unquote(content).to_s.gsub(/\s*$\s*/,''))}"
11
+
12
+ # IE8 has a 32KiB limit on data uri
13
+ # en.wikipedia.org/wiki/Data_URI_scheme
14
+ if outuri.length > 32768
15
+ raise ArgumentError.new("Data URI is greater than 32KiB in size, that is the max size of data urls in IE8.")
16
+ end
17
+
18
+ Sass::Script::String.new("url('#{outuri}')")
19
+ end
20
+ end
21
+ end
data/lib/easy_menu.rb ADDED
@@ -0,0 +1,7 @@
1
+ module EasyMenu
2
+ require 'easy_menu_helpers'
3
+ require 'easy_menu_configuration'
4
+
5
+ class Engine < Rails::Engine
6
+ end
7
+ end
@@ -0,0 +1,60 @@
1
+ module EasyMenu
2
+ module Configuration
3
+ def self.included(base)
4
+ base.class_eval do
5
+ class_attribute :config
6
+ self.config = Hash.new{|hash, key| raise "#{key} has not been set in Easy Menu Configuration"}.merge(Default)
7
+ end
8
+ end
9
+
10
+ Default = {
11
+ # CLASSES
12
+
13
+ :default_theme_class => 'default_theme',
14
+ :menu_bar_class => 'menu_bar',
15
+ :menu_bar_content_class => 'menu_bar_content',
16
+ :menu_bar_group_class => 'menu_bar_group',
17
+ :menu_bar_content_with_menu_class => 'with_menu no_js', # no_js class will be removed by browser if it has js, disabling the hover behaviour and enabling a click behaviour
18
+ :menu_bar_item_class => 'menu_bar_item',
19
+ :menu_bar_item_arrow_class => 'arrow',
20
+ :menu_bar_input_class => 'menu_bar_input',
21
+ :menu_bar_separator_class => 'menu_bar_separator',
22
+ :menu_bar_trigger_class => :menu_bar_item, # This element is a menu item but behaves differently
23
+ :menu_class => 'menu',
24
+ :menu_content_class => 'menu_content',
25
+ :menu_group_class => 'menu_group',
26
+ :menu_group_title_class => 'menu_group_title',
27
+ :menu_item_class => 'menu_item',
28
+ :menu_separator_class => 'menu_separator',
29
+ :click_blocker_class => 'click_blocker',
30
+ :selected_class => 'selected',
31
+ :disabled_class => 'disabled',
32
+ :grouped_class => 'grouped',
33
+ :first_group_item_class => 'first_group_item',
34
+ :last_group_item_class => 'last_group_item',
35
+
36
+ # ELEMENTS
37
+ :menu_bar_element => :ul,
38
+ :menu_bar_group_element => :ul,
39
+ :menu_bar_content_element => :li,
40
+ :menu_bar_item_element => :div,
41
+ :menu_bar_trigger_element => :div,
42
+ :menu_bar_input_element => :label,
43
+
44
+ :menu_element => :ul,
45
+ :menu_content_element => :li,
46
+ :menu_group_title_element => :div,
47
+ :menu_group_element => :ul,
48
+ :menu_item_element => :div
49
+ }
50
+
51
+ Bootstrap = {
52
+ :menu_bar_content_with_menu_class => 'dropdown',
53
+ :menu_bar_item_arrow_class => 'caret',
54
+ :menu_class => 'dropdown-menu',
55
+ :menu_item_element => nil,
56
+ :menu_bar_trigger_element => :a,
57
+ :menu_bar_trigger_class => 'dropdown-toggle'
58
+ }
59
+ end
60
+ end
@@ -0,0 +1,36 @@
1
+ module EasyMenu
2
+ module Helpers
3
+ HTML_OPTIONS = [:id, :class, :title, :style, :data]
4
+
5
+ # Determines the config name from the class name.
6
+ # e.g. MenuBar => 'menu_bar'
7
+ def config_name
8
+ self.class.name.demodulize.underscore
9
+ end
10
+
11
+ def css_class
12
+ config[:"#{config_name}_class"]
13
+ end
14
+
15
+ def wrapper_element
16
+ config[:"#{config_name}_element"]
17
+ end
18
+
19
+ def wrap_content(content)
20
+ if (wrapper_element)
21
+ @template.content_tag wrapper_element, content, html_options
22
+ else
23
+ content
24
+ end
25
+ end
26
+
27
+ def merge_class(hash, *classes)
28
+ hash[:class] = ([hash[:class]] + classes.flatten).select(&:present?).join(' ')
29
+ return hash
30
+ end
31
+
32
+ def html_option_keys
33
+ HTML_OPTIONS + @options.keys.select{|key| key.to_s.starts_with? 'data-'}
34
+ end
35
+ end
36
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: easy_menu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.2
5
+ platform: ruby
6
+ authors:
7
+ - Nicholas Jakobsen
8
+ - Ryan Wallace
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-01-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '3.1'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '3.1'
28
+ description:
29
+ email:
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - Gemfile
35
+ - MIT-LICENSE
36
+ - README
37
+ - app/assets/images/arrows.png
38
+ - app/assets/images/checkbox_selected.png
39
+ - app/assets/images/checkbox_selected_hovered.png
40
+ - app/assets/javascripts/easy_menu_jquery.js
41
+ - app/assets/javascripts/easy_menu_prototype.js
42
+ - app/assets/stylesheets/_easy_menu_mixins.scss
43
+ - app/assets/stylesheets/easy_menu.css.scss
44
+ - app/helpers/menu_bar.rb
45
+ - app/helpers/menu_bar_helper.rb
46
+ - config/initializers/easy_menu_scss_extensions.rb
47
+ - lib/easy_menu.rb
48
+ - lib/easy_menu_configuration.rb
49
+ - lib/easy_menu_helpers.rb
50
+ homepage:
51
+ licenses: []
52
+ metadata: {}
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 2.2.1
70
+ signing_key:
71
+ specification_version: 4
72
+ summary: Simple menu bar dsl for Rails views
73
+ test_files: []