govuk_admin_template 0.0.1 → 0.0.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.
- data/README.md +4 -1
- data/app/assets/javascripts/govuk-admin-template.js +9 -0
- data/app/assets/javascripts/govuk-admin.js +103 -0
- data/app/assets/javascripts/modules/auto_show_modal.js +13 -0
- data/app/assets/javascripts/modules/auto_track_event.js +15 -0
- data/app/assets/javascripts/modules/filterable_table.js +29 -0
- data/app/assets/javascripts/modules/fixed_table_header.js +38 -0
- data/app/assets/javascripts/modules/selectable_table.js +154 -0
- data/app/assets/javascripts/modules/toggle.js +25 -0
- data/lib/govuk_admin_template/version.rb +1 -1
- metadata +10 -3
data/README.md
CHANGED
@@ -6,6 +6,7 @@ This gem provides (via a Rails engine):
|
|
6
6
|
* jQuery
|
7
7
|
* Bootstrap 3 standard styles and javascript — including HTML5 and respond.js shims necessary for IE <= IE8
|
8
8
|
* An admin layout with header and footer
|
9
|
+
* A [lightweight javascript framework](JAVASCRIPT.md)
|
9
10
|
* Admin design patterns available from __/style-guide__
|
10
11
|
* SASS variables for the admin theme
|
11
12
|
|
@@ -34,6 +35,8 @@ You will also need to include your styles within the `<head>` of your HTML, do t
|
|
34
35
|
|
35
36
|
The gem source includes a [dummy app](spec/dummy) configured to behave like an app using the gem. If you have the gem checked out it can be run from the `spec\dummy` directory using `rails s`.
|
36
37
|
|
38
|
+
For Javascript usage, available modules and writing modules, see the [Javascript guide](JAVASCRIPT.md).
|
39
|
+
|
37
40
|
### Content blocks
|
38
41
|
|
39
42
|
The gem [uses nested layouts](http://guides.rubyonrails.org/layouts_and_rendering.html#using-nested-layouts) for customisation.
|
@@ -90,4 +93,4 @@ bundle exec rake dummy_app:jasmine:ci
|
|
90
93
|
|
91
94
|
## Publishing
|
92
95
|
|
93
|
-
|
96
|
+
Version bumps will automatically update RubyGems.org.
|
@@ -1,3 +1,12 @@
|
|
1
1
|
//= require jquery
|
2
2
|
//= require jquery_ujs
|
3
3
|
//= require bootstrap
|
4
|
+
//= require govuk-admin
|
5
|
+
//= require_tree ./modules
|
6
|
+
|
7
|
+
// Find and auto-start modules specified using the data-module="" pattern in markup
|
8
|
+
(function($, GOVUKAdmin) {
|
9
|
+
$(function(){
|
10
|
+
GOVUKAdmin.startAll();
|
11
|
+
});
|
12
|
+
})(jQuery, window.GOVUKAdmin);
|
@@ -0,0 +1,103 @@
|
|
1
|
+
(function($, root) {
|
2
|
+
"use strict";
|
3
|
+
|
4
|
+
var GOVUKAdmin = root.GOVUKAdmin = {
|
5
|
+
Modules: {}
|
6
|
+
};
|
7
|
+
|
8
|
+
GOVUKAdmin.find = function(container) {
|
9
|
+
|
10
|
+
var modules,
|
11
|
+
moduleSelector = '[data-module]',
|
12
|
+
container = container || $('body');
|
13
|
+
|
14
|
+
modules = container.find(moduleSelector);
|
15
|
+
|
16
|
+
// Include container if it matches pattern, as that could
|
17
|
+
// be a module too
|
18
|
+
if (container.is(moduleSelector)) {
|
19
|
+
modules.push(container);
|
20
|
+
}
|
21
|
+
|
22
|
+
return modules;
|
23
|
+
}
|
24
|
+
|
25
|
+
GOVUKAdmin.start = function(container) {
|
26
|
+
|
27
|
+
var modules = this.find(container);
|
28
|
+
|
29
|
+
for (var i = 0, l = modules.length; i < l; i++) {
|
30
|
+
|
31
|
+
var module,
|
32
|
+
element = $(modules[i]),
|
33
|
+
type = camelCaseAndCapitalise(element.data('module'));
|
34
|
+
|
35
|
+
if (typeof GOVUKAdmin.Modules[type] === "function") {
|
36
|
+
module = new GOVUKAdmin.Modules[type]();
|
37
|
+
module.start(element);
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
// eg selectable-table to SelectableTable
|
42
|
+
function camelCaseAndCapitalise(string) {
|
43
|
+
return capitaliseFirstLetter(camelCase(string));
|
44
|
+
}
|
45
|
+
|
46
|
+
// http://stackoverflow.com/questions/6660977/convert-hyphens-to-camel-case-camelcase
|
47
|
+
function camelCase(string) {
|
48
|
+
return string.replace(/-([a-z])/g, function (g) {
|
49
|
+
return g[1].toUpperCase();
|
50
|
+
});
|
51
|
+
}
|
52
|
+
|
53
|
+
// http://stackoverflow.com/questions/1026069/capitalize-the-first-letter-of-string-in-javascript
|
54
|
+
function capitaliseFirstLetter(string) {
|
55
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
56
|
+
}
|
57
|
+
|
58
|
+
}
|
59
|
+
|
60
|
+
GOVUKAdmin.startAll = function() {
|
61
|
+
GOVUKAdmin.start();
|
62
|
+
GOVUKAdmin.startBootstrapComponents();
|
63
|
+
}
|
64
|
+
|
65
|
+
GOVUKAdmin.startBootstrapComponents = function() {
|
66
|
+
$('[data-toggle="tooltip"]').tooltip();
|
67
|
+
}
|
68
|
+
|
69
|
+
// Google Analytics event tracking
|
70
|
+
// https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide
|
71
|
+
// Label and value are optional
|
72
|
+
GOVUKAdmin.track = function(action, label, value) {
|
73
|
+
|
74
|
+
// Default category to the page an event occurs on
|
75
|
+
var category = root.location.pathname,
|
76
|
+
event;
|
77
|
+
|
78
|
+
// _gaq is the Google Analytics tracking object we
|
79
|
+
// push events to, GA asynchronously sends them on
|
80
|
+
root._gaq = root._gaq || [];
|
81
|
+
|
82
|
+
event = ["_trackEvent", category, action];
|
83
|
+
|
84
|
+
// Label is optional
|
85
|
+
if (typeof label === "string") {
|
86
|
+
event.push(label);
|
87
|
+
}
|
88
|
+
|
89
|
+
// Value is optional, but when used must be an
|
90
|
+
// integer, otherwise the event will be invalid
|
91
|
+
// and not logged
|
92
|
+
if (value) {
|
93
|
+
value = parseInt(value, 10);
|
94
|
+
if (typeof value === "number" && !isNaN(value)) {
|
95
|
+
event.push(value);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
// Useful for debugging: console.log(event);
|
100
|
+
_gaq.push(event);
|
101
|
+
}
|
102
|
+
|
103
|
+
})(jQuery, window);
|
@@ -0,0 +1,13 @@
|
|
1
|
+
(function(Modules) {
|
2
|
+
"use strict";
|
3
|
+
|
4
|
+
Modules.AutoShowModal = function() {
|
5
|
+
var that = this;
|
6
|
+
that.start = function(element) {
|
7
|
+
element.modal('show').on('hidden.bs.modal', function () {
|
8
|
+
$(this).remove();
|
9
|
+
});
|
10
|
+
}
|
11
|
+
};
|
12
|
+
|
13
|
+
})(window.GOVUKAdmin.Modules);
|
@@ -0,0 +1,15 @@
|
|
1
|
+
(function(Modules) {
|
2
|
+
"use strict";
|
3
|
+
|
4
|
+
Modules.AutoTrackEvent = function() {
|
5
|
+
var that = this;
|
6
|
+
that.start = function(element) {
|
7
|
+
var action = element.data('track-action'),
|
8
|
+
label = element.data('track-label'),
|
9
|
+
value = element.data('track-value');
|
10
|
+
|
11
|
+
GOVUKAdmin.track(action, label, value);
|
12
|
+
}
|
13
|
+
};
|
14
|
+
|
15
|
+
})(window.GOVUKAdmin.Modules);
|
@@ -0,0 +1,29 @@
|
|
1
|
+
(function(Modules) {
|
2
|
+
"use strict";
|
3
|
+
|
4
|
+
Modules.FilterableTable = function() {
|
5
|
+
var that = this;
|
6
|
+
that.start = function(element) {
|
7
|
+
|
8
|
+
var rows = element.find('tbody tr'),
|
9
|
+
tableInput = element.find('.js-filter-table-input');
|
10
|
+
|
11
|
+
element.on('keyup change', '.js-filter-table-input', filterTableBasedOnInput);
|
12
|
+
|
13
|
+
function filterTableBasedOnInput(event) {
|
14
|
+
var searchString = $.trim(tableInput.val()),
|
15
|
+
regExp = new RegExp(searchString, 'i');
|
16
|
+
|
17
|
+
rows.each(function() {
|
18
|
+
var row = $(this);
|
19
|
+
if (row.text().search(regExp) > -1) {
|
20
|
+
row.show();
|
21
|
+
} else {
|
22
|
+
row.hide();
|
23
|
+
}
|
24
|
+
});
|
25
|
+
}
|
26
|
+
}
|
27
|
+
};
|
28
|
+
|
29
|
+
})(window.GOVUKAdmin.Modules);
|
@@ -0,0 +1,38 @@
|
|
1
|
+
(function(Modules) {
|
2
|
+
"use strict";
|
3
|
+
|
4
|
+
Modules.FixedTableHeader = function() {
|
5
|
+
var that = this;
|
6
|
+
that.start = function(element) {
|
7
|
+
|
8
|
+
// Clone the current table header into a fixed container
|
9
|
+
// Use .container class for correct width and responsiveness
|
10
|
+
// Setup a dummy table for correct rendering of cloned <thead>
|
11
|
+
// Basics derived from http://stackoverflow.com/questions/4709390/
|
12
|
+
|
13
|
+
var header = element.find('thead'),
|
14
|
+
headerOffset = header.offset().top,
|
15
|
+
fixedHeader = header.clone(),
|
16
|
+
fixedHeaderContainer = $('\
|
17
|
+
<div class="fixed-table-header-container">\
|
18
|
+
<div class="container">\
|
19
|
+
<table class="table table-bordered">\
|
20
|
+
</table>\
|
21
|
+
</div>\
|
22
|
+
</div>');
|
23
|
+
|
24
|
+
fixedHeaderContainer.hide().find('table').append(fixedHeader);
|
25
|
+
element.prepend(fixedHeaderContainer);
|
26
|
+
$(window).bind("scroll", checkOffsetAndToggleFixedHeader);
|
27
|
+
|
28
|
+
function checkOffsetAndToggleFixedHeader() {
|
29
|
+
var offset = $(window).scrollTop();
|
30
|
+
if (offset >= headerOffset && fixedHeaderContainer.is(":hidden")) {
|
31
|
+
fixedHeaderContainer.show();
|
32
|
+
} else if (offset < headerOffset) {
|
33
|
+
fixedHeaderContainer.hide();
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
};
|
38
|
+
})(window.GOVUKAdmin.Modules);
|
@@ -0,0 +1,154 @@
|
|
1
|
+
(function(Modules) {
|
2
|
+
"use strict";
|
3
|
+
|
4
|
+
Modules.SelectableTable = function() {
|
5
|
+
|
6
|
+
var that = this;
|
7
|
+
|
8
|
+
that.start = function(element) {
|
9
|
+
|
10
|
+
var tableRows = element.find('tbody tr'),
|
11
|
+
SELECTED_ROW_CLASS = 'selected-row',
|
12
|
+
RECENTLY_CHANGED_CLASS = 'js-most-recently-changed';
|
13
|
+
|
14
|
+
element.on('click', '.js-toggle-row', toggleRow);
|
15
|
+
element.on('click', '.js-toggle-all', toggleAllRows);
|
16
|
+
element.on('click', '.js-submit-form', submitForm);
|
17
|
+
|
18
|
+
element.on('ajax:success', 'form', createModal);
|
19
|
+
element.on('ajax:error', 'form', handleModalError);
|
20
|
+
element.on('ajax:complete','form', resetSubmitButtons);
|
21
|
+
|
22
|
+
onLoadMarkSelectedRowsWithClass();
|
23
|
+
updateHeaderToggleState();
|
24
|
+
|
25
|
+
function onLoadMarkSelectedRowsWithClass() {
|
26
|
+
|
27
|
+
var selectedRows = tableRows.find('.js-toggle-row:checked').parents('tr');
|
28
|
+
selectedRows.addClass(SELECTED_ROW_CLASS);
|
29
|
+
|
30
|
+
}
|
31
|
+
|
32
|
+
function toggleRow(event) {
|
33
|
+
|
34
|
+
var row = $(event.target).parents('tr');
|
35
|
+
|
36
|
+
event.shiftKey ? shiftToggleRow(row) : row.toggleClass(SELECTED_ROW_CLASS);
|
37
|
+
|
38
|
+
markRowAsRecentlyChanged(row);
|
39
|
+
updateHeaderToggleState();
|
40
|
+
}
|
41
|
+
|
42
|
+
function updateHeaderToggleState() {
|
43
|
+
|
44
|
+
var selectedRowsCount = tableRows.filter('.' + SELECTED_ROW_CLASS).length,
|
45
|
+
inputHeader = element.find('.js-toggle-all');
|
46
|
+
|
47
|
+
if (selectedRowsCount > 0 && selectedRowsCount < tableRows.length) {
|
48
|
+
inputHeader.prop('indeterminate', true);
|
49
|
+
inputHeader.prop('checked', false);
|
50
|
+
resetSubmitButtons();
|
51
|
+
} else if (selectedRowsCount === 0) {
|
52
|
+
inputHeader.prop('checked', false);
|
53
|
+
inputHeader.prop('indeterminate', false);
|
54
|
+
disableSubmitButtons();
|
55
|
+
} else {
|
56
|
+
inputHeader.prop('checked', true);
|
57
|
+
inputHeader.prop('indeterminate', false);
|
58
|
+
resetSubmitButtons();
|
59
|
+
}
|
60
|
+
|
61
|
+
}
|
62
|
+
|
63
|
+
function markRowAsRecentlyChanged(row) {
|
64
|
+
tableRows.removeClass(RECENTLY_CHANGED_CLASS);
|
65
|
+
row.addClass(RECENTLY_CHANGED_CLASS);
|
66
|
+
}
|
67
|
+
|
68
|
+
function shiftToggleRow(targetRow) {
|
69
|
+
|
70
|
+
var targetIndex = tableRows.index(targetRow),
|
71
|
+
targetState = targetRow.is('.' + SELECTED_ROW_CLASS),
|
72
|
+
mostRecentlyChanged = tableRows.filter('.' + RECENTLY_CHANGED_CLASS),
|
73
|
+
mostRecentlyChangedIndex = tableRows.index(mostRecentlyChanged),
|
74
|
+
rows, range;
|
75
|
+
|
76
|
+
// If we don't have a most recently changed, only toggle the current row
|
77
|
+
if (mostRecentlyChangedIndex < 0) {
|
78
|
+
mostRecentlyChangedIndex = targetIndex;
|
79
|
+
}
|
80
|
+
|
81
|
+
range = mostRecentlyChangedIndex < targetIndex ? [mostRecentlyChangedIndex, targetIndex + 1] : [targetIndex, mostRecentlyChangedIndex + 1];
|
82
|
+
rows = tableRows.slice.apply(tableRows, range);
|
83
|
+
toggleRows(rows, ! targetState);
|
84
|
+
|
85
|
+
}
|
86
|
+
|
87
|
+
function toggleAllRows(event) {
|
88
|
+
|
89
|
+
var rows = element.find('tbody tr');
|
90
|
+
|
91
|
+
// If everything selected
|
92
|
+
if (tableRows.length == element.find('.' + SELECTED_ROW_CLASS).length) {
|
93
|
+
toggleRows(rows, false);
|
94
|
+
} else {
|
95
|
+
toggleRows(rows, true);
|
96
|
+
}
|
97
|
+
|
98
|
+
updateHeaderToggleState();
|
99
|
+
}
|
100
|
+
|
101
|
+
function toggleRows(rows, select) {
|
102
|
+
if (select) {
|
103
|
+
rows.addClass(SELECTED_ROW_CLASS)
|
104
|
+
} else {
|
105
|
+
rows.removeClass(SELECTED_ROW_CLASS)
|
106
|
+
}
|
107
|
+
rows.find('input').prop('checked', select);
|
108
|
+
}
|
109
|
+
|
110
|
+
function submitForm(event) {
|
111
|
+
var target = $(event.target),
|
112
|
+
type = target.data('type');
|
113
|
+
|
114
|
+
if (target.is('.disabled')) {
|
115
|
+
event.preventDefault();
|
116
|
+
return;
|
117
|
+
}
|
118
|
+
|
119
|
+
disableSubmitButtons();
|
120
|
+
target.button('loading');
|
121
|
+
|
122
|
+
element.find('input[type="radio"][value="' + type + '"]').prop('checked', true);
|
123
|
+
element.find('form').submit();
|
124
|
+
event.preventDefault();
|
125
|
+
}
|
126
|
+
|
127
|
+
function createModal(event, html, status) {
|
128
|
+
var modal = $(html);
|
129
|
+
|
130
|
+
$('body').append(modal);
|
131
|
+
|
132
|
+
modal.modal('show').on('hidden.bs.modal', function () {
|
133
|
+
modal.remove();
|
134
|
+
});
|
135
|
+
|
136
|
+
GOVUKAdmin.start(modal);
|
137
|
+
}
|
138
|
+
|
139
|
+
function handleModalError(xhr, status, error) {
|
140
|
+
alert('There was a problem loading this. Please try again.');
|
141
|
+
}
|
142
|
+
|
143
|
+
function resetSubmitButtons() {
|
144
|
+
element.find('.js-submit-form').removeClass('disabled').button('reset');
|
145
|
+
element.find('.js-submit-container').addClass('buttons-enabled');
|
146
|
+
}
|
147
|
+
|
148
|
+
function disableSubmitButtons() {
|
149
|
+
element.find('.js-submit-form').addClass('disabled');
|
150
|
+
element.find('.js-submit-container').removeClass('buttons-enabled');
|
151
|
+
}
|
152
|
+
}
|
153
|
+
};
|
154
|
+
})(window.GOVUKAdmin.Modules);
|
@@ -0,0 +1,25 @@
|
|
1
|
+
(function(Modules) {
|
2
|
+
"use strict";
|
3
|
+
|
4
|
+
Modules.Toggle = function() {
|
5
|
+
|
6
|
+
var that = this;
|
7
|
+
|
8
|
+
that.start = function(element) {
|
9
|
+
element.on('click', '.js-toggle', toggle);
|
10
|
+
element.on('click', '.js-cancel', cancel);
|
11
|
+
|
12
|
+
function toggle(event) {
|
13
|
+
element.find('.js-toggle-target').toggleClass('if-js-hide');
|
14
|
+
element.find('input').first().focus();
|
15
|
+
event.preventDefault();
|
16
|
+
}
|
17
|
+
|
18
|
+
function cancel(event) {
|
19
|
+
toggle(event);
|
20
|
+
element.find('input').first().val('');
|
21
|
+
}
|
22
|
+
};
|
23
|
+
};
|
24
|
+
|
25
|
+
})(window.GOVUKAdmin.Modules);
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: govuk_admin_template
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -159,6 +159,13 @@ files:
|
|
159
159
|
- app/assets/images/header-crown.png
|
160
160
|
- app/assets/javascripts/vendor/html5.js
|
161
161
|
- app/assets/javascripts/vendor/respond.min.js
|
162
|
+
- app/assets/javascripts/govuk-admin.js
|
163
|
+
- app/assets/javascripts/modules/selectable_table.js
|
164
|
+
- app/assets/javascripts/modules/filterable_table.js
|
165
|
+
- app/assets/javascripts/modules/fixed_table_header.js
|
166
|
+
- app/assets/javascripts/modules/auto_track_event.js
|
167
|
+
- app/assets/javascripts/modules/toggle.js
|
168
|
+
- app/assets/javascripts/modules/auto_show_modal.js
|
162
169
|
- app/assets/javascripts/lte-ie8.js
|
163
170
|
- app/assets/javascripts/govuk-admin-template.js
|
164
171
|
- app/controllers/govuk_admin_template/style_guide_controller.rb
|
@@ -181,7 +188,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
181
188
|
version: '0'
|
182
189
|
segments:
|
183
190
|
- 0
|
184
|
-
hash:
|
191
|
+
hash: -1211471381049981484
|
185
192
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
186
193
|
none: false
|
187
194
|
requirements:
|
@@ -190,7 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
190
197
|
version: '0'
|
191
198
|
segments:
|
192
199
|
- 0
|
193
|
-
hash:
|
200
|
+
hash: -1211471381049981484
|
194
201
|
requirements: []
|
195
202
|
rubyforge_project:
|
196
203
|
rubygems_version: 1.8.23
|