easy_menu 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []