govuk_elements_rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENCE +20 -0
- data/README.md +54 -0
- data/lib/govuk_elements_rails.rb +4 -0
- data/lib/govuk_elements_rails/engine.rb +4 -0
- data/lib/govuk_elements_rails/version.rb +5 -0
- data/vendor/assets/javascripts/bind.js +40 -0
- data/vendor/assets/javascripts/details.polyfill.js +158 -0
- data/vendor/assets/javascripts/selection-buttons.js +137 -0
- data/vendor/assets/stylesheets/elements-page-ie6.scss +5 -0
- data/vendor/assets/stylesheets/elements-page-ie7.scss +4 -0
- data/vendor/assets/stylesheets/elements-page-ie8.scss +4 -0
- data/vendor/assets/stylesheets/elements-page.scss +333 -0
- data/vendor/assets/stylesheets/elements/_buttons.scss +44 -0
- data/vendor/assets/stylesheets/elements/_details.scss +39 -0
- data/vendor/assets/stylesheets/elements/_elements-typography.scss +179 -0
- data/vendor/assets/stylesheets/elements/_forms.scss +166 -0
- data/vendor/assets/stylesheets/elements/_helpers.scss +46 -0
- data/vendor/assets/stylesheets/elements/_icons.scss +224 -0
- data/vendor/assets/stylesheets/elements/_layout.scss +67 -0
- data/vendor/assets/stylesheets/elements/_lists.scss +32 -0
- data/vendor/assets/stylesheets/elements/_panels.scss +29 -0
- data/vendor/assets/stylesheets/elements/_reset.scss +33 -0
- data/vendor/assets/stylesheets/elements/_tables.scss +26 -0
- data/vendor/assets/stylesheets/elements/forms/_form-block-labels.scss +67 -0
- data/vendor/assets/stylesheets/elements/forms/_form-date.scss +46 -0
- data/vendor/assets/stylesheets/elements/forms/_form-validation.scss +68 -0
- data/vendor/assets/stylesheets/main-ie6.scss +5 -0
- data/vendor/assets/stylesheets/main-ie7.scss +4 -0
- data/vendor/assets/stylesheets/main-ie8.scss +4 -0
- data/vendor/assets/stylesheets/main.scss +36 -0
- data/vendor/assets/stylesheets/prism.scss +144 -0
- data/vendor/assets/stylesheets/service-design-manual/helpers/_breadcrumbs.scss +81 -0
- data/vendor/assets/stylesheets/service-design-manual/helpers/_page-header.scss +28 -0
- data/vendor/assets/stylesheets/service-design-manual/styleguide/_colours.scss +3 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0e4507b35a48b80125447d05206cad3cfe6f39f0
|
4
|
+
data.tar.gz: 98b68bd115ef63a93316504d741f4130c486f493
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 10ecd7e53790c3d74e3853f4d40b0f83d786a8d6af4e222d7c0ebd4c27c95ffad1780d0a4615a138aa2b2f58a649c847f15dca8aa95014c30e234c2574e27429
|
7
|
+
data.tar.gz: ff14ffac1525b39272214c70e6e6c408377de2365fbff171f6386bb3a221169dd3d561fe88f41aae4f7fd51324133776e199a5805a9ab3928052c687f916b27a
|
data/LICENCE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 HM Government
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# GOV.UK Elements Rails gem
|
2
|
+
|
3
|
+
A gem wrapper around [govuk_elements](http://github.com/alphagov/govuk_elements)
|
4
|
+
that pulls stylesheet and javascript files into a Rails app.
|
5
|
+
|
6
|
+
## Installing
|
7
|
+
|
8
|
+
Just include `govuk_elements_rails` in your `Gemfile`. It
|
9
|
+
automatically attaches itself to your asset path so the static/SCSS
|
10
|
+
files will be available to the asset pipeline.
|
11
|
+
|
12
|
+
### Development
|
13
|
+
|
14
|
+
If you are installing from git, ensure you enable submodules like so:
|
15
|
+
|
16
|
+
gem 'govuk_elements_rails', :git => "https://github.com/ministryofjustice/govuk_elements_rails.git", :submodules => true
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
At the top of a Sass file in your Rails project you should use an `@import` rule
|
21
|
+
to include the file for the mixins you require. For example here are all the
|
22
|
+
imports possible:
|
23
|
+
|
24
|
+
@import 'elements/helpers';
|
25
|
+
@import 'elements/reset';
|
26
|
+
@import 'elements/elements-typography';
|
27
|
+
@import 'elements/layout';
|
28
|
+
|
29
|
+
@import 'elements/forms';
|
30
|
+
@import 'elements/tables';
|
31
|
+
@import 'elements/buttons';
|
32
|
+
@import 'elements/details';
|
33
|
+
@import 'elements/lists';
|
34
|
+
@import 'elements/panels';
|
35
|
+
@import "elements/icons";
|
36
|
+
|
37
|
+
In the `app/assets/javascripts/application.js` file in your Rails project use
|
38
|
+
`require` rule to include the files for the javascript enhancements you require.
|
39
|
+
For example here are all the requires possible at present:
|
40
|
+
|
41
|
+
// from govuk_elements gem
|
42
|
+
//= require details.polyfill
|
43
|
+
//= require bind
|
44
|
+
//= require selection-buttons
|
45
|
+
|
46
|
+
## Alternate ways to reuse GOV.UK Elements
|
47
|
+
|
48
|
+
There are other alternate ways to include GOV.UK Elements files in a Rails
|
49
|
+
project, for example via `Bower`. Feel free to use an alternate approach if it
|
50
|
+
is more appropriate for your team.
|
51
|
+
|
52
|
+
## Feedback
|
53
|
+
|
54
|
+
Please provide feedback via [GitHub issues](https://github.com/ministryofjustice/govuk_elements_rails/issues).
|
@@ -0,0 +1,40 @@
|
|
1
|
+
// Function.prototype.bind
|
2
|
+
//
|
3
|
+
// A polyfill for Function.prototype.bind. Which lets you bind a defined
|
4
|
+
// value to the `this` keyword in a function call.
|
5
|
+
//
|
6
|
+
// Bind is natively supported in:
|
7
|
+
// IE9+
|
8
|
+
// Chrome 7+
|
9
|
+
// Firefox 4+
|
10
|
+
// Safari 5.1.4+
|
11
|
+
// iOS 6+
|
12
|
+
// Android Browser 4+
|
13
|
+
// Chrome for Android 0.16+
|
14
|
+
//
|
15
|
+
// Originally from:
|
16
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
|
17
|
+
if (!Function.prototype.bind) {
|
18
|
+
Function.prototype.bind = function (oThis) {
|
19
|
+
if (typeof this !== "function") {
|
20
|
+
// closest thing possible to the ECMAScript 5
|
21
|
+
// internal IsCallable function
|
22
|
+
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
|
23
|
+
}
|
24
|
+
|
25
|
+
var aArgs = Array.prototype.slice.call(arguments, 1),
|
26
|
+
fToBind = this,
|
27
|
+
fNOP = function () {},
|
28
|
+
fBound = function () {
|
29
|
+
return fToBind.apply(this instanceof fNOP && oThis
|
30
|
+
? this
|
31
|
+
: oThis,
|
32
|
+
aArgs.concat(Array.prototype.slice.call(arguments)));
|
33
|
+
};
|
34
|
+
|
35
|
+
fNOP.prototype = this.prototype;
|
36
|
+
fBound.prototype = new fNOP();
|
37
|
+
|
38
|
+
return fBound;
|
39
|
+
};
|
40
|
+
}
|
@@ -0,0 +1,158 @@
|
|
1
|
+
// <details> polyfill
|
2
|
+
// http://caniuse.com/#feat=details
|
3
|
+
|
4
|
+
// FF Support for HTML5's <details> and <summary>
|
5
|
+
// https://bugzilla.mozilla.org/show_bug.cgi?id=591737
|
6
|
+
|
7
|
+
// http://www.sitepoint.com/fixing-the-details-element/
|
8
|
+
|
9
|
+
(function () {
|
10
|
+
|
11
|
+
// Add event construct for modern browsers or IE
|
12
|
+
// which fires the callback with a pre-converted target reference
|
13
|
+
function addEvent(node, type, callback) {
|
14
|
+
if (node.addEventListener) {
|
15
|
+
node.addEventListener(type, function (e) {
|
16
|
+
callback(e, e.target);
|
17
|
+
}, false);
|
18
|
+
} else if (node.attachEvent) {
|
19
|
+
node.attachEvent('on' + type, function (e) {
|
20
|
+
callback(e, e.srcElement);
|
21
|
+
});
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
// Handle cross-modal click events
|
26
|
+
function addClickEvent(node, callback) {
|
27
|
+
var keydown = false;
|
28
|
+
addEvent(node, 'keydown', function () {
|
29
|
+
keydown = true;
|
30
|
+
});
|
31
|
+
addEvent(node, 'keyup', function (e, target) {
|
32
|
+
keydown = false;
|
33
|
+
if (e.keyCode === 13) { callback(e, target); }
|
34
|
+
});
|
35
|
+
addEvent(node, 'click', function (e, target) {
|
36
|
+
if (!keydown) { callback(e, target); }
|
37
|
+
});
|
38
|
+
}
|
39
|
+
|
40
|
+
// Get the nearest ancestor element of a node that matches a given tag name
|
41
|
+
function getAncestor(node, match) {
|
42
|
+
do {
|
43
|
+
if (!node || node.nodeName.toLowerCase() === match) {
|
44
|
+
break;
|
45
|
+
}
|
46
|
+
} while (node = node.parentNode);
|
47
|
+
|
48
|
+
return node;
|
49
|
+
}
|
50
|
+
|
51
|
+
// Create a started flag so we can prevent the initialisation
|
52
|
+
// function firing from both DOMContentLoaded and window.onload
|
53
|
+
var started = false;
|
54
|
+
|
55
|
+
// Initialisation function
|
56
|
+
function addDetailsPolyfill(list) {
|
57
|
+
|
58
|
+
// If this has already happened, just return
|
59
|
+
// else set the flag so it doesn't happen again
|
60
|
+
if (started) {
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
started = true;
|
64
|
+
|
65
|
+
// Get the collection of details elements, but if that's empty
|
66
|
+
// then we don't need to bother with the rest of the scripting
|
67
|
+
if ((list = document.getElementsByTagName('details')).length === 0) {
|
68
|
+
return;
|
69
|
+
}
|
70
|
+
|
71
|
+
// else iterate through them to apply their initial state
|
72
|
+
var n = list.length, i = 0;
|
73
|
+
for (n; i < n; i++) {
|
74
|
+
var details = list[i];
|
75
|
+
|
76
|
+
// Detect native implementations
|
77
|
+
details.__native = typeof(details.open) == 'boolean';
|
78
|
+
|
79
|
+
// Save shortcuts to the inner summary and content elements
|
80
|
+
details.__summary = details.getElementsByTagName('summary').item(0);
|
81
|
+
details.__content = details.getElementsByTagName('div').item(0);
|
82
|
+
|
83
|
+
// If the content doesn't have an ID, assign it one now
|
84
|
+
// which we'll need for the summary's aria-controls assignment
|
85
|
+
if (!details.__content.id) {
|
86
|
+
details.__content.id = 'details-content-' + i;
|
87
|
+
}
|
88
|
+
|
89
|
+
// Add role=button to summary
|
90
|
+
details.__summary.setAttribute('role', 'button');
|
91
|
+
|
92
|
+
// Add aria-controls
|
93
|
+
details.__summary.setAttribute('aria-controls', details.__content.id);
|
94
|
+
|
95
|
+
// Set tabindex so the summary is keyboard accessible
|
96
|
+
details.__summary.setAttribute('tabindex', '0');
|
97
|
+
|
98
|
+
// Detect initial open/closed state
|
99
|
+
var detailsAttr = details.hasAttribute('open');
|
100
|
+
if (typeof detailsAttr !== 'undefined' && detailsAttr !== false) {
|
101
|
+
details.__summary.setAttribute('aria-expanded', 'true');
|
102
|
+
details.__content.setAttribute('aria-hidden', 'false');
|
103
|
+
} else {
|
104
|
+
details.__summary.setAttribute('aria-expanded', 'false');
|
105
|
+
details.__content.setAttribute('aria-hidden', 'true');
|
106
|
+
details.__content.style.display = 'none';
|
107
|
+
}
|
108
|
+
|
109
|
+
// Create a circular reference from the summary back to its
|
110
|
+
// parent details element, for convenience in the click handler
|
111
|
+
details.__summary.__details = details;
|
112
|
+
|
113
|
+
// If this is not a native implementation, create an arrow
|
114
|
+
// inside the summary, saving its reference as a summary property
|
115
|
+
if (!details.__native) {
|
116
|
+
var twisty = document.createElement('i');
|
117
|
+
twisty.className = 'arrow arrow-closed';
|
118
|
+
twisty.appendChild(document.createTextNode('\u25ba'));
|
119
|
+
details.__summary.__twisty = details.__summary.insertBefore(twisty, details.__summary.firstChild);
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
// Define a statechange function that updates aria-expanded and style.display
|
124
|
+
// Also update the arrow position
|
125
|
+
function statechange(summary) {
|
126
|
+
|
127
|
+
// Update aria-expanded attribute on click
|
128
|
+
var expanded = summary.__details.__summary.getAttribute('aria-expanded') == 'true';
|
129
|
+
var hidden = summary.__details.__content.getAttribute('aria-hidden') == 'true';
|
130
|
+
|
131
|
+
summary.__details.__summary.setAttribute('aria-expanded', (expanded ? 'false' : 'true'));
|
132
|
+
summary.__details.__content.setAttribute('aria-hidden', (hidden ? 'false' : 'true'));
|
133
|
+
summary.__details.__content.style.display = (expanded ? 'none' : 'block');
|
134
|
+
|
135
|
+
if (summary.__twisty) {
|
136
|
+
summary.__twisty.firstChild.nodeValue = (expanded ? '\u25ba' : '\u25bc');
|
137
|
+
summary.__twisty.setAttribute('class', (expanded ? 'arrow arrow-closed' : 'arrow arrow-open'));
|
138
|
+
}
|
139
|
+
|
140
|
+
return true;
|
141
|
+
}
|
142
|
+
|
143
|
+
// Bind a click event to handle summary elements
|
144
|
+
addClickEvent(document, function(e, summary) {
|
145
|
+
if (!(summary = getAncestor(summary, 'summary'))) {
|
146
|
+
return true;
|
147
|
+
}
|
148
|
+
return statechange(summary);
|
149
|
+
});
|
150
|
+
}
|
151
|
+
|
152
|
+
// Bind two load events for modern and older browsers
|
153
|
+
// If the first one fires it will set a flag to block the second one
|
154
|
+
// but if it's not supported then the second one will fire
|
155
|
+
addEvent(document, 'DOMContentLoaded', addDetailsPolyfill);
|
156
|
+
addEvent(window, 'load', addDetailsPolyfill);
|
157
|
+
|
158
|
+
})();
|
@@ -0,0 +1,137 @@
|
|
1
|
+
(function () {
|
2
|
+
"use strict"
|
3
|
+
var root = this,
|
4
|
+
$ = root.jQuery;
|
5
|
+
|
6
|
+
if (typeof GOVUK === 'undefined') { root.GOVUK = {}; }
|
7
|
+
|
8
|
+
var BaseButtons = function ($elms, opts) {
|
9
|
+
this.$elms = $elms;
|
10
|
+
this.selectedClass = 'selected';
|
11
|
+
this.focusedClass = 'focused';
|
12
|
+
if (opts !== undefined) {
|
13
|
+
$.each(opts, function (optionName, optionObj) {
|
14
|
+
this[optionName] = optionObj;
|
15
|
+
}.bind(this));
|
16
|
+
}
|
17
|
+
this.setEventNames();
|
18
|
+
this.getSelections();
|
19
|
+
this.bindEvents();
|
20
|
+
};
|
21
|
+
BaseButtons.prototype.setEventNames = function () {
|
22
|
+
this.selectionEvents = 'click';
|
23
|
+
this.focusEvents = 'focus blur';
|
24
|
+
};
|
25
|
+
BaseButtons.prototype.markFocused = function ($elm, state) {
|
26
|
+
var elmId = $elm.attr('id');
|
27
|
+
|
28
|
+
if (state === 'focused') {
|
29
|
+
$elm.parent('label').addClass(this.focusedClass);
|
30
|
+
} else {
|
31
|
+
$elm.parent('label').removeClass(this.focusedClass);
|
32
|
+
}
|
33
|
+
};
|
34
|
+
BaseButtons.prototype.bindEvents = function () {
|
35
|
+
var selectionEventHandler = this.markSelected.bind(this),
|
36
|
+
focusEventHandler = this.markFocused.bind(this);
|
37
|
+
|
38
|
+
this.$elms
|
39
|
+
.on(this.selectionEvents, function (e) {
|
40
|
+
selectionEventHandler($(e.target));
|
41
|
+
})
|
42
|
+
.on(this.focusEvents, function (e) {
|
43
|
+
var state = (e.type === 'focus') ? 'focused' : 'blurred';
|
44
|
+
|
45
|
+
focusEventHandler($(e.target), state);
|
46
|
+
});
|
47
|
+
};
|
48
|
+
|
49
|
+
var RadioButtons = function ($elms, opts) {
|
50
|
+
BaseButtons.apply(this, arguments);
|
51
|
+
};
|
52
|
+
RadioButtons.prototype.setEventNames = function () {
|
53
|
+
// some browsers fire the 'click' when the selected radio changes by keyboard
|
54
|
+
this.selectionEvents = 'click change';
|
55
|
+
this.focusEvents = 'focus blur';
|
56
|
+
};
|
57
|
+
RadioButtons.prototype.getSelections = function () {
|
58
|
+
var selectionEventHandler = this.markSelected.bind(this);
|
59
|
+
|
60
|
+
this.selections = {};
|
61
|
+
$.each(this.$elms, function (index, elm) {
|
62
|
+
var $elm = $(elm),
|
63
|
+
radioName = $elm.attr('name');
|
64
|
+
|
65
|
+
if (typeof this.selections[radioName] === 'undefined') {
|
66
|
+
this.selections[radioName] = false;
|
67
|
+
}
|
68
|
+
if ($elm.is(':checked')) {
|
69
|
+
selectionEventHandler($elm);
|
70
|
+
}
|
71
|
+
}.bind(this));
|
72
|
+
};
|
73
|
+
RadioButtons.prototype.bindEvents = function () {
|
74
|
+
BaseButtons.prototype.bindEvents.call(this);
|
75
|
+
};
|
76
|
+
RadioButtons.prototype.markSelected = function ($elm) {
|
77
|
+
var radioName = $elm.attr('name'),
|
78
|
+
$previousSelection = this.selections[radioName];
|
79
|
+
|
80
|
+
if ($previousSelection) {
|
81
|
+
$previousSelection.parent('label').removeClass(this.selectedClass);
|
82
|
+
}
|
83
|
+
$elm.parent('label').addClass(this.selectedClass);
|
84
|
+
this.selections[radioName] = $elm;
|
85
|
+
};
|
86
|
+
RadioButtons.prototype.markFocused = function ($elm) {
|
87
|
+
BaseButtons.prototype.markFocused.apply(this, arguments);
|
88
|
+
};
|
89
|
+
|
90
|
+
var CheckboxButtons = function ($elms, opts) {
|
91
|
+
BaseButtons.apply(this, arguments);
|
92
|
+
};
|
93
|
+
CheckboxButtons.prototype.setEventNames = function () {
|
94
|
+
BaseButtons.prototype.setEventNames.call(this);
|
95
|
+
};
|
96
|
+
CheckboxButtons.prototype.getSelections = function () {
|
97
|
+
var selectionEventHandler = this.markSelected.bind(this);
|
98
|
+
|
99
|
+
this.$elms.each(function (idx, elm) {
|
100
|
+
var $elm = $(elm);
|
101
|
+
|
102
|
+
if ($elm.is(':checked')) {
|
103
|
+
selectionEventHandler($elm);
|
104
|
+
}
|
105
|
+
});
|
106
|
+
};
|
107
|
+
CheckboxButtons.prototype.bindEvents = function () {
|
108
|
+
BaseButtons.prototype.bindEvents.call(this);
|
109
|
+
};
|
110
|
+
CheckboxButtons.prototype.markSelected = function ($elm) {
|
111
|
+
if ($elm.is(':checked')) {
|
112
|
+
$elm.parent('label').addClass(this.selectedClass);
|
113
|
+
} else {
|
114
|
+
$elm.parent('label').removeClass(this.selectedClass);
|
115
|
+
}
|
116
|
+
};
|
117
|
+
CheckboxButtons.prototype.markFocused = function ($elm) {
|
118
|
+
BaseButtons.prototype.markFocused.apply(this, arguments);
|
119
|
+
};
|
120
|
+
|
121
|
+
root.GOVUK.RadioButtons = RadioButtons;
|
122
|
+
root.GOVUK.CheckboxButtons = CheckboxButtons;
|
123
|
+
|
124
|
+
var selectionButtons = function ($elms, opts) {
|
125
|
+
var $radios = $elms.filter('[type=radio]'),
|
126
|
+
$checkboxes = $elms.filter('[type=checkbox]');
|
127
|
+
|
128
|
+
if ($radios) {
|
129
|
+
new GOVUK.RadioButtons($radios, opts);
|
130
|
+
}
|
131
|
+
if ($checkboxes) {
|
132
|
+
new GOVUK.CheckboxButtons($checkboxes, opts);
|
133
|
+
}
|
134
|
+
};
|
135
|
+
|
136
|
+
root.GOVUK.selectionButtons = selectionButtons;
|
137
|
+
}).call(this);
|