fustrate-rails 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/lib/fustrate-rails.rb +4 -0
  3. data/lib/fustrate/rails/engine.rb +14 -0
  4. data/lib/fustrate/rails/version.rb +6 -0
  5. data/vendor/assets/javascripts/awesomplete.js +402 -0
  6. data/vendor/assets/javascripts/fustrate.coffee +6 -0
  7. data/vendor/assets/javascripts/fustrate/_module.coffee +134 -0
  8. data/vendor/assets/javascripts/fustrate/components/_module.coffee +3 -0
  9. data/vendor/assets/javascripts/fustrate/components/alert_box.coffee +10 -0
  10. data/vendor/assets/javascripts/fustrate/components/autocomplete.coffee +161 -0
  11. data/vendor/assets/javascripts/fustrate/components/disclosure.coffee +12 -0
  12. data/vendor/assets/javascripts/fustrate/components/drop_zone.coffee +9 -0
  13. data/vendor/assets/javascripts/fustrate/components/dropdown.coffee +48 -0
  14. data/vendor/assets/javascripts/fustrate/components/file_picker.coffee +10 -0
  15. data/vendor/assets/javascripts/fustrate/components/flash.coffee +31 -0
  16. data/vendor/assets/javascripts/fustrate/components/modal.coffee +213 -0
  17. data/vendor/assets/javascripts/fustrate/components/pagination.coffee +84 -0
  18. data/vendor/assets/javascripts/fustrate/components/tabs.coffee +28 -0
  19. data/vendor/assets/javascripts/fustrate/components/tooltip.coffee +72 -0
  20. data/vendor/assets/javascripts/fustrate/generic_form.coffee +31 -0
  21. data/vendor/assets/javascripts/fustrate/generic_page.coffee +40 -0
  22. data/vendor/assets/javascripts/fustrate/generic_table.coffee +57 -0
  23. data/vendor/assets/javascripts/fustrate/listenable.coffee +25 -0
  24. data/vendor/assets/javascripts/fustrate/object.coffee +21 -0
  25. data/vendor/assets/javascripts/fustrate/record.coffee +23 -0
  26. data/vendor/assets/stylesheets/_fustrate.sass +7 -0
  27. data/vendor/assets/stylesheets/awesomplete.sass +75 -0
  28. data/vendor/assets/stylesheets/fustrate/_colors.sass +9 -0
  29. data/vendor/assets/stylesheets/fustrate/_settings.sass +20 -0
  30. data/vendor/assets/stylesheets/fustrate/components/_components.sass +36 -0
  31. data/vendor/assets/stylesheets/fustrate/components/_functions.sass +40 -0
  32. data/vendor/assets/stylesheets/fustrate/components/alerts.sass +78 -0
  33. data/vendor/assets/stylesheets/fustrate/components/buttons.sass +103 -0
  34. data/vendor/assets/stylesheets/fustrate/components/disclosures.sass +23 -0
  35. data/vendor/assets/stylesheets/fustrate/components/dropdowns.sass +31 -0
  36. data/vendor/assets/stylesheets/fustrate/components/flash.sass +33 -0
  37. data/vendor/assets/stylesheets/fustrate/components/forms.sass +188 -0
  38. data/vendor/assets/stylesheets/fustrate/components/grid.sass +204 -0
  39. data/vendor/assets/stylesheets/fustrate/components/labels.sass +63 -0
  40. data/vendor/assets/stylesheets/fustrate/components/modals.sass +119 -0
  41. data/vendor/assets/stylesheets/fustrate/components/pagination.sass +57 -0
  42. data/vendor/assets/stylesheets/fustrate/components/panels.sass +49 -0
  43. data/vendor/assets/stylesheets/fustrate/components/popovers.sass +15 -0
  44. data/vendor/assets/stylesheets/fustrate/components/tables.sass +58 -0
  45. data/vendor/assets/stylesheets/fustrate/components/tabs.sass +44 -0
  46. data/vendor/assets/stylesheets/fustrate/components/tooltips.sass +28 -0
  47. data/vendor/assets/stylesheets/fustrate/components/typography.sass +355 -0
  48. metadata +211 -0
@@ -0,0 +1,84 @@
1
+ class Fustrate.Components.Pagination extends Fustrate.Components.Base
2
+ constructor: ({@current_page, @total_pages, @total_entries, @per_page}) ->
3
+ @base = @constructor._getPreppedPaginationURL()
4
+
5
+ link: (text, page, options = {}) =>
6
+ Fustrate.linkTo(text, "#{@base}page=#{page}", options)
7
+
8
+ previousLink: =>
9
+ if @current_page > 1
10
+ return "
11
+ <li class=\"previous_page\">
12
+ #{@link('← Previous', @current_page - 1, rel: 'prev')}
13
+ </li>"
14
+
15
+ '<li class="previous_page unavailable"><a href="#">← Previous</a></li>'
16
+
17
+ nextLink: =>
18
+ if @current_page < @total_pages
19
+ return "
20
+ <li class=\"next_page\">
21
+ #{@link('Next →', @current_page + 1, rel: 'next')}
22
+ </li>"
23
+
24
+ '<li class="next_page unavailable"><a href="#">Next →</a></li>'
25
+
26
+ generate: =>
27
+ pages = []
28
+
29
+ if @total_pages > 1
30
+ pages = for i in @windowedPageNumbers()
31
+ if i == @current_page
32
+ "<li class=\"current\">#{Fustrate.linkTo(i, '#')}</li>"
33
+ else if i == 'gap'
34
+ '<li class="unavailable"><span class="gap">…</span></li>'
35
+ else
36
+ "<li>#{@link i, i}</li>"
37
+
38
+ pages.unshift @previousLink()
39
+ pages.push @nextLink()
40
+
41
+ $('<ul class="pagination">').html pages.join(' ')
42
+
43
+ windowedPageNumbers: =>
44
+ window_from = @current_page - 4
45
+ window_to = @current_page + 4
46
+
47
+ if window_to > @total_pages
48
+ window_from -= window_to - @total_pages
49
+ window_to = @total_pages
50
+
51
+ if window_from < 1
52
+ window_to += 1 - window_from
53
+ window_from = 1
54
+ window_to = @total_pages if window_to > @total_pages
55
+
56
+ middle = [window_from..window_to]
57
+
58
+ left = if 4 < middle[0] then [1, 2, 'gap'] else [1...middle[0]]
59
+
60
+ if @total_pages - 3 > middle.last()
61
+ right = [(@total_pages - 1)..@total_pages]
62
+ right.unshift 'gap'
63
+ else if middle.last() + 1 <= @total_pages
64
+ right = [(middle.last() + 1)..@total_pages]
65
+ else
66
+ right = []
67
+
68
+ left.concat middle, right
69
+
70
+ @getCurrentPage: ->
71
+ window.location.search.match(/[?&]page=(\d+)/)?[1] ? 1
72
+
73
+ # Just add 'page='
74
+ @_getPreppedPaginationURL: ->
75
+ search = window.location.search.replace(/[?&]page=\d+/, '')
76
+
77
+ search = if search[0] == '?'
78
+ "#{search}&"
79
+ else if search[0] == '&'
80
+ "?#{search[1...search.length]}&"
81
+ else
82
+ '?'
83
+
84
+ "#{window.location.pathname}#{search}"
@@ -0,0 +1,28 @@
1
+ class Fustrate.Components.Tabs extends Fustrate.Components.Base
2
+ constructor: (@tabs) ->
3
+ @tabs.on 'click', 'li > a', (e) =>
4
+ @activateTab $(e.currentTarget)
5
+
6
+ false
7
+
8
+ if window.location.hash
9
+ @activateTab $("li > a[href='#{window.location.hash}']", @tabs).first()
10
+ else if $('li > a.active', @tabs).length > 0
11
+ @activateTab $('li > a.active', @tabs).first()
12
+ else
13
+ @activateTab $('li > a', @tabs).first()
14
+
15
+ activateTab: (tab) =>
16
+ return unless tab
17
+
18
+ $('.active', @tabs).removeClass 'active'
19
+ tab.addClass 'active'
20
+
21
+ $("##{tab.attr('href').split('#')[1]}")
22
+ .addClass 'active'
23
+ .siblings()
24
+ .removeClass 'active'
25
+
26
+ @initialize: =>
27
+ $('ul.tabs').each (index, elem) =>
28
+ new @($ elem)
@@ -0,0 +1,72 @@
1
+ class Fustrate.Components.Tooltip extends Fustrate.Components.Base
2
+ @fadeSpeed: 100
3
+
4
+ constructor: (element, title) ->
5
+ @element = $ element
6
+ @active = false
7
+
8
+ @addEventListeners()
9
+
10
+ @element.attr('title', title) if title
11
+
12
+ addEventListeners: =>
13
+ @element
14
+ .off '.tooltip'
15
+ .on 'mouseenter.tooltip', @_show
16
+ .on 'mousemove.tooltip', @_move
17
+ .on 'mouseleave.tooltip', @_hide
18
+
19
+ setTitle: (title) ->
20
+ if @active
21
+ @tooltip.text title
22
+ else
23
+ @element.prop('title', title)
24
+
25
+ _move: (e) =>
26
+ @tooltip.css @_tooltipPosition(e) if @active
27
+
28
+ false
29
+
30
+ _show: (e) =>
31
+ return false if @active
32
+
33
+ title = @element.prop('title') ? ''
34
+
35
+ return false unless title.length > 0
36
+
37
+ @tooltip ?= $('<span class="tooltip">').hide()
38
+
39
+ @element.attr('title', '').removeAttr('title')
40
+
41
+ @active = true
42
+
43
+ @tooltip
44
+ .text title
45
+ .appendTo $('body')
46
+ .css @_tooltipPosition(e)
47
+ .fadeIn @constructor.fadeSpeed
48
+
49
+ false
50
+
51
+ _hide: (e) =>
52
+ # No use hiding something that doesn't exist.
53
+ if @tooltip
54
+ @element.attr 'title', @tooltip.text()
55
+ @active = false
56
+
57
+ @tooltip.fadeOut @constructor.fadeSpeed, @tooltip.detach
58
+
59
+ false
60
+
61
+ _tooltipPosition: (e) ->
62
+ top: "#{e.pageY + 15}px"
63
+ left: "#{e.pageX - 10}px"
64
+
65
+ @initialize: ->
66
+ $('[data-tooltip]').each (index, elem) ->
67
+ new Fustrate.Components.Tooltip elem
68
+
69
+ $.fn.extend
70
+ tooltip: (options) ->
71
+ @each (index, element) ->
72
+ new Fustrate.Components.Tooltip element
@@ -0,0 +1,31 @@
1
+ #= require './generic_page'
2
+
3
+ class Fustrate.GenericForm extends Fustrate.GenericPage
4
+ addEventListeners: =>
5
+ super
6
+
7
+ @root.on 'submit', @onSubmit
8
+
9
+ _reloadUIElements: =>
10
+ super
11
+
12
+ for domObject in $('[name][id]', @root)
13
+ element = $ domObject
14
+ name = element.prop 'name'
15
+
16
+ if captures = name.match /\[([a-z_]+)\]$/
17
+ @fields[captures[1]] = element
18
+ else
19
+ @fields[name] = element
20
+
21
+ validate: -> true
22
+
23
+ onSubmit: (e) =>
24
+ e.preventDefault()
25
+
26
+ unless @validate()
27
+ setTimeout (=> $.rails.enableFormElements(@root)), 100
28
+
29
+ return false
30
+
31
+ true
@@ -0,0 +1,40 @@
1
+ class Fustrate.GenericPage
2
+ constructor: (@root) ->
3
+ @_reloadUIElements()
4
+ @addEventListeners()
5
+ @initialize()
6
+
7
+ addEventListeners: ->
8
+
9
+ # Once the interface is loaded and the event listeners are active, run any
10
+ # other tasks.
11
+ initialize: ->
12
+
13
+ _reloadUIElements: =>
14
+ @fields = {}
15
+ @buttons = {}
16
+
17
+ $('[data-field]', @root).each (index, element) =>
18
+ field = $ element
19
+ @fields[field.data('field')] = field
20
+
21
+ $('[data-button]', @root).each (index, element) =>
22
+ button = $ element
23
+ @buttons[button.data('button')] = button
24
+
25
+ flashSuccess: (message, {icon} = {}) ->
26
+ new Fustrate.Components.Flash.Success(message, icon: icon)
27
+
28
+ flashError: (message, {icon} = {}) ->
29
+ new Fustrate.Components.Flash.Error(message, icon: icon)
30
+
31
+ flashInfo: (message, {icon} = {}) ->
32
+ new Fustrate.Components.Flash.Info(message, icon: icon)
33
+
34
+ setHeader: (text) ->
35
+ $('.header > span', @root).text text
36
+
37
+ # Calls all methods matching /refresh.+/
38
+ refresh: =>
39
+ for own name, func of @
40
+ func() if name.indexOf('refresh') == 0 && name != 'refresh'
@@ -0,0 +1,57 @@
1
+ class Fustrate.GenericTable extends Fustrate.GenericPage
2
+ @blankRow: null
3
+ table: null
4
+
5
+ initialize: =>
6
+ super
7
+
8
+ @reloadTable()
9
+
10
+ reloadTable: ->
11
+
12
+ sortRows: (rows, sortFunction = ->) ->
13
+ sorted = ([sortFunction(row), row] for row in rows)
14
+ sorted.sort (x, y) ->
15
+ if x[0] == y[0] then 0 else if x[0] > y[0] then 1 else -1
16
+ sorted.map (row) -> row[1]
17
+
18
+ createRow: (item) =>
19
+ @updateRow @constructor.blankRow.clone(), item
20
+
21
+ updateRow: (row, item) ->
22
+ row
23
+
24
+ reloadRows: (rows, {sort} = { sort: null }) =>
25
+ tbody = $ 'tbody', @table
26
+
27
+ $('tr.loading', tbody).hide()
28
+
29
+ if rows
30
+ $('tr:not(.no-records):not(.loading)', tbody).remove()
31
+
32
+ tbody.append if sort then @sortRows(rows, sort) else rows
33
+
34
+ @updated()
35
+
36
+ addRow: (row) =>
37
+ $('tbody', @table).append row
38
+ @updated()
39
+
40
+ removeRow: (row) =>
41
+ row.fadeOut =>
42
+ row.remove()
43
+ @updated()
44
+
45
+ updated: =>
46
+ $('tbody tr.no-records', @table)
47
+ .toggle $('tbody tr:not(.no-records):not(.loading)', @table).length < 1
48
+
49
+ getCheckedIds: =>
50
+ (item.value for item in $('td:first-child input:checked', @table))
51
+
52
+ # This should be fed a response from a JSON request for a paginated
53
+ # collection.
54
+ updatePagination: (response) =>
55
+ @pagination = new Fustrate.Components.Pagination response
56
+
57
+ $('.pagination', @root).replaceWith @pagination.generate()
@@ -0,0 +1,25 @@
1
+ class Fustrate.Listenable
2
+ constructor: ->
3
+ @listeners = {}
4
+
5
+ on: (eventNames, callback) =>
6
+ for eventName in eventNames.split(' ')
7
+ @listeners[eventName] = [] unless @listeners[eventName]
8
+ @listeners[eventName].push callback
9
+
10
+ @
11
+
12
+ off: (eventNames) =>
13
+ for eventName in eventNames.split(' ')
14
+ @listeners[eventName] = []
15
+
16
+ @
17
+
18
+ trigger: =>
19
+ [name, args...] = arguments
20
+
21
+ return unless name && @listeners[name]
22
+
23
+ event.apply(@, args) for event in @listeners[name]
24
+
25
+ @
@@ -0,0 +1,21 @@
1
+ class Fustrate.Object extends Fustrate.Listenable
2
+ constructor: (data) ->
3
+ @extractFromData data
4
+
5
+ super
6
+
7
+ # Simple extractor to assign root keys as properties in the current object.
8
+ # Formats a few common attributes as dates with moment.js
9
+ extractFromData: (data) =>
10
+ @[key] = value for key, value of data
11
+
12
+ @date = moment @date if @date
13
+ @created_at = moment @created_at if @created_at
14
+ @updated_at = moment @updated_at if @updated_at
15
+
16
+ # Instantiate a new object of type klass for each item in items
17
+ _createList: (items, klass, additional_attributes = {}) ->
18
+ for item in items
19
+ obj = new klass(item)
20
+ obj[key] = value for key, value of additional_attributes
21
+ obj
@@ -0,0 +1,23 @@
1
+ class Fustrate.Record extends Fustrate.Object
2
+ # Rails class name
3
+ @class: null
4
+
5
+ constructor: (data) ->
6
+ super
7
+
8
+ if typeof data is 'number' or typeof data is 'string'
9
+ # If the parameter was a number or string, it's likely the record ID
10
+ @id = parseInt(data, 10)
11
+ else
12
+ # Otherwise we were probably given a hash of attributes
13
+ @extractFromData data
14
+
15
+ reload: ->
16
+
17
+ save: ->
18
+
19
+ toObject: -> {}
20
+
21
+ update: (data) =>
22
+ @extractFromData data
23
+ @save()
@@ -0,0 +1,7 @@
1
+ @import "bourbon"
2
+ @import "font-awesome"
3
+ @import "awesomplete"
4
+
5
+ @import "fustrate/colors"
6
+ @import "fustrate/settings"
7
+ @import "fustrate/components/components"
@@ -0,0 +1,75 @@
1
+ /* 1795543d988d0fd9ca6237a5ac176f8e88d63990 */
2
+
3
+ [hidden]
4
+ display: none
5
+
6
+ .visually-hidden
7
+ position: absolute
8
+ clip: rect(0, 0, 0, 0)
9
+
10
+ div.awesomplete
11
+ display: block
12
+ position: relative
13
+
14
+ > input
15
+ display: block
16
+
17
+ > ul
18
+ position: absolute
19
+ left: 0
20
+ z-index: 9
21
+ min-width: 100%
22
+ box-sizing: border-box
23
+ list-style: none
24
+ padding: 0
25
+ border-radius: .3em
26
+ margin: .2em 0 0
27
+ background: hsla(0, 0%, 100%, .9)
28
+ background: linear-gradient(to bottom right, white, hsla(0, 0%, 100%, .8))
29
+ border: 1px solid rgba(0, 0, 0, .3)
30
+ box-shadow: .05em .2em .6em rgba(0, 0, 0, .2)
31
+ text-shadow: none
32
+ max-height: 30vh
33
+ overflow-x: scroll
34
+
35
+ &[hidden],
36
+ &:empty
37
+ display: none
38
+
39
+ > li
40
+ position: relative
41
+ padding: .2em .5em
42
+ cursor: pointer
43
+
44
+ &:not(:last-child)
45
+ border-bottom: 1px solid rgba(64, 64, 64, .3)
46
+
47
+ &:hover
48
+ background: hsl(200, 40%, 80%)
49
+ color: black
50
+
51
+ &[aria-selected="true"]
52
+ background: hsl(205, 40%, 40%)
53
+ color: white
54
+
55
+ mark
56
+ background: hsl(65, 100%, 50%)
57
+
58
+ li:hover &
59
+ background: hsl(68, 100%, 41%)
60
+
61
+ li[aria-selected="true"] &
62
+ background: hsl(86, 100%, 21%)
63
+ color: inherit
64
+
65
+ @supports (transform: scale(0))
66
+ div.awesomplete > ul
67
+ transition: .3s cubic-bezier(.4, .2, .5, 1.4)
68
+ transform-origin: 1.43em -.43em
69
+
70
+ &[hidden],
71
+ &:empty
72
+ opacity: 0
73
+ transform: scale(0)
74
+ display: block
75
+ transition-timing-function: ease