marionette.modal 1.0.0
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 +7 -0
- data/.gitignore +36 -0
- data/Gemfile +4 -0
- data/Gruntfile.coffee +109 -0
- data/LICENSE +22 -0
- data/README.md +42 -0
- data/Rakefile +1 -0
- data/backbone.marionette.modals-min.js +1 -0
- data/backbone.marionette.modals.js +104 -0
- data/backbone.modal-min.js +1 -0
- data/backbone.modal.js +382 -0
- data/examples/1_single_view.html +71 -0
- data/examples/2_tab_based.html +104 -0
- data/examples/3_stacked_modal_with_marionette.html +105 -0
- data/examples/4_wizard.html +132 -0
- data/examples/css/style.css +45 -0
- data/examples/img/tab-icons.png +0 -0
- data/examples/style.css +35 -0
- data/examples/vendor/backbone.js +1571 -0
- data/examples/vendor/backbone.marionette.modals.js +104 -0
- data/examples/vendor/backbone.modal.css +24 -0
- data/examples/vendor/backbone.modal.js +382 -0
- data/examples/vendor/backbone.modal.theme.css +324 -0
- data/examples/vendor/jquery-1.9.1.js +9597 -0
- data/examples/vendor/marionette.js +2340 -0
- data/examples/vendor/marionette.modal.css +24 -0
- data/examples/vendor/marionette.modal.js +370 -0
- data/examples/vendor/marionette.modal.theme.css +324 -0
- data/examples/vendor/underscore.js +1227 -0
- data/lib/marionette.modal/version.rb +3 -0
- data/lib/marionette.modal.rb +5 -0
- data/marionette.modal-bundled-min.js +1 -0
- data/marionette.modal-bundled.js +858 -0
- data/marionette.modal-min.js +1 -0
- data/marionette.modal.css +24 -0
- data/marionette.modal.gemspec +23 -0
- data/marionette.modal.js +370 -0
- data/marionette.modal.theme.css +324 -0
- data/package.json +18 -0
- data/src/backbone.marionette.modals.coffee +67 -0
- data/src/backbone.modal.coffee +253 -0
- data/src/marionette.modal.coffee +248 -0
- data/src/marionette.modal.sass +26 -0
- data/src/marionette.modal.theme.sass +486 -0
- data/src/style.sass +48 -0
- data/test/spec/backbone.marionette.modals.spec.js +87 -0
- data/test/spec/backbone.modal.spec.js +224 -0
- data/test/spec.html +41 -0
- data/test/src/backbone.marionette.modals.spec.coffee +47 -0
- data/test/src/backbone.modal.spec.coffee +139 -0
- metadata +128 -0
@@ -0,0 +1,248 @@
|
|
1
|
+
unless Marionette?
|
2
|
+
throw new Error("Marionette is not defined. Please include the latest version from https://github.com/marionettejs/backbone.marionette")
|
3
|
+
|
4
|
+
class Backbone.Modal extends Marionette.View
|
5
|
+
prefix: 'bbm'
|
6
|
+
|
7
|
+
constructor: ->
|
8
|
+
@args = Array::slice.apply(arguments)
|
9
|
+
Marionette.View::constructor.apply(this, @args)
|
10
|
+
|
11
|
+
@setUIElements()
|
12
|
+
@delegateModalEvents()
|
13
|
+
|
14
|
+
render: (options = {}) ->
|
15
|
+
# use openAt or overwrite this with your own functionality
|
16
|
+
data = @serializeData()
|
17
|
+
|
18
|
+
@$el.addClass("#{@prefix}-wrapper")
|
19
|
+
@modalEl = Marionette.$('<div />').addClass("#{@prefix}-modal")
|
20
|
+
@modalEl.html @template(data) if @template
|
21
|
+
@$el.html @modalEl
|
22
|
+
|
23
|
+
# global events for key and click outside the modal
|
24
|
+
Marionette.$('body').on 'keyup', @checkKey
|
25
|
+
Marionette.$('body').on 'click', @clickOutside
|
26
|
+
|
27
|
+
if @viewContainer
|
28
|
+
@viewContainerEl = @modalEl.find(@viewContainer)
|
29
|
+
@viewContainerEl.addClass("#{@prefix}-modal__views")
|
30
|
+
else
|
31
|
+
@viewContainerEl = @modalEl
|
32
|
+
|
33
|
+
@$el.show()
|
34
|
+
@openAt(0) if @views?.length > 0
|
35
|
+
@onRender?()
|
36
|
+
|
37
|
+
@modalEl.css(opacity: 0)
|
38
|
+
@$el.fadeIn
|
39
|
+
duration: 100
|
40
|
+
complete: =>
|
41
|
+
@modalEl.css(opacity: 1).addClass("#{@prefix}-modal--open")
|
42
|
+
|
43
|
+
return this
|
44
|
+
|
45
|
+
setUIElements: ->
|
46
|
+
# get modal options
|
47
|
+
@template = Marionette.getOption(this, 'template')
|
48
|
+
@views = Marionette.getOption(this, 'views')
|
49
|
+
@views?.length = _.size(@views)
|
50
|
+
@viewContainer = Marionette.getOption(this, 'viewContainer')
|
51
|
+
|
52
|
+
# hide modal
|
53
|
+
@$el.hide()
|
54
|
+
|
55
|
+
throw new Error('No template or views defined for Backbone.Modal') if _.isUndefined(@template) and _.isUndefined(@views)
|
56
|
+
throw new Error('No viewContainer defined for Backbone.Modal') if @template and @views and _.isUndefined(@viewContainer)
|
57
|
+
|
58
|
+
serializeData: ->
|
59
|
+
# return the appropriate data for this view
|
60
|
+
data = {}
|
61
|
+
|
62
|
+
data = _.extend(data, @model.toJSON()) if @model
|
63
|
+
data = _.extend(data, {items: @collection.toJSON()}) if @collection
|
64
|
+
|
65
|
+
return data
|
66
|
+
|
67
|
+
delegateModalEvents: ->
|
68
|
+
@active = true
|
69
|
+
|
70
|
+
# get elements
|
71
|
+
cancelEl = Marionette.getOption(this, 'cancelEl')
|
72
|
+
submitEl = Marionette.getOption(this, 'submitEl')
|
73
|
+
|
74
|
+
# set event handlers for submit and cancel
|
75
|
+
@$el.on('click', submitEl, @triggerSubmit) if submitEl
|
76
|
+
@$el.on('click', cancelEl, @triggerCancel) if cancelEl
|
77
|
+
|
78
|
+
# set event handlers for views
|
79
|
+
for key of @views
|
80
|
+
unless key is 'length'
|
81
|
+
match = key.match(/^(\S+)\s*(.*)$/)
|
82
|
+
trigger = match[1]
|
83
|
+
selector = match[2]
|
84
|
+
|
85
|
+
@$el.on trigger, selector, @views[key], @triggerView
|
86
|
+
|
87
|
+
undelegateModalEvents: ->
|
88
|
+
@active = false
|
89
|
+
|
90
|
+
# get elements
|
91
|
+
cancelEl = @getOption('cancelEl')
|
92
|
+
submitEl = @getOption('submitEl')
|
93
|
+
|
94
|
+
# remove event handlers for submit and cancel
|
95
|
+
@$el.off('click', submitEl, @triggerSubmit) if submitEl
|
96
|
+
@$el.off('click', cancelEl, @triggerCancel) if cancelEl
|
97
|
+
|
98
|
+
# remove event handlers for views
|
99
|
+
for key of @views
|
100
|
+
unless key is 'length'
|
101
|
+
match = key.match(/^(\S+)\s*(.*)$/)
|
102
|
+
trigger = match[1]
|
103
|
+
selector = match[2]
|
104
|
+
|
105
|
+
@$el.off trigger, selector, @views[key], @triggerView
|
106
|
+
|
107
|
+
checkKey: (e) =>
|
108
|
+
if @active
|
109
|
+
switch e.keyCode
|
110
|
+
when 27 then @triggerCancel()
|
111
|
+
when 13 then @triggerSubmit()
|
112
|
+
|
113
|
+
clickOutside: (e) =>
|
114
|
+
@triggerCancel(null, true) if Marionette.$(e.target).hasClass("#{@prefix}-wrapper") and @active
|
115
|
+
|
116
|
+
buildView: (viewType) ->
|
117
|
+
# returns a Backbone.View instance, a function or an object
|
118
|
+
return unless viewType
|
119
|
+
if _.isFunction(viewType)
|
120
|
+
view = new viewType(@args[0])
|
121
|
+
|
122
|
+
if view instanceof Backbone.View
|
123
|
+
return {el: view.render().$el, view: view}
|
124
|
+
else
|
125
|
+
return {el: viewType(@args[0])}
|
126
|
+
|
127
|
+
return {view: viewType, el: viewType.$el}
|
128
|
+
|
129
|
+
triggerView: (e) =>
|
130
|
+
# trigger what view should be rendered
|
131
|
+
e?.preventDefault?()
|
132
|
+
options = e.data
|
133
|
+
instance = @buildView(options.view)
|
134
|
+
|
135
|
+
@previousView = @currentView if @currentView
|
136
|
+
@currentView = instance.view || instance.el
|
137
|
+
|
138
|
+
index = 0
|
139
|
+
for key of @views
|
140
|
+
@currentIndex = index if options.view is @views[key].view
|
141
|
+
index++
|
142
|
+
|
143
|
+
if options.onActive
|
144
|
+
if _.isFunction(options.onActive)
|
145
|
+
options.onActive(this)
|
146
|
+
else if _.isString(options.onActive)
|
147
|
+
this[options.onActive].call(this, options)
|
148
|
+
|
149
|
+
if @shouldAnimate
|
150
|
+
@animateToView(instance.el)
|
151
|
+
else
|
152
|
+
@shouldAnimate = true
|
153
|
+
@$(@viewContainerEl).html instance.el
|
154
|
+
|
155
|
+
animateToView: (view) ->
|
156
|
+
style = position: 'relative', top: -9999, left: -9999
|
157
|
+
tester = Marionette.$('<tester/>').css(style)
|
158
|
+
tester.html @$el.clone().css(style)
|
159
|
+
if Marionette.$('tester').length isnt 0 then Marionette.$('tester').replaceWith tester else Marionette.$('body').append tester
|
160
|
+
|
161
|
+
if @viewContainer
|
162
|
+
container = tester.find(@viewContainer)
|
163
|
+
else
|
164
|
+
container = tester.find(".#{@prefix}-modal")
|
165
|
+
|
166
|
+
container.removeAttr('style')
|
167
|
+
|
168
|
+
previousHeight = container.outerHeight()
|
169
|
+
container.html(view)
|
170
|
+
newHeight = container.outerHeight()
|
171
|
+
|
172
|
+
if previousHeight is newHeight
|
173
|
+
@$(@viewContainerEl).html view
|
174
|
+
@previousView?.close?()
|
175
|
+
else
|
176
|
+
@$(@viewContainerEl).css(opacity: 0)
|
177
|
+
|
178
|
+
@$(@viewContainerEl).animate {height: newHeight}, 100, =>
|
179
|
+
@$(@viewContainerEl).css(opacity: 1).removeAttr('style')
|
180
|
+
@$(@viewContainerEl).html view
|
181
|
+
@previousView?.close?()
|
182
|
+
|
183
|
+
# jQuery style event handler
|
184
|
+
triggerSubmit: (e) =>
|
185
|
+
return unless e
|
186
|
+
# triggers submit
|
187
|
+
e?.preventDefault()
|
188
|
+
|
189
|
+
if @beforeSubmit
|
190
|
+
return if @beforeSubmit() is false
|
191
|
+
|
192
|
+
@submit?()
|
193
|
+
|
194
|
+
if @regionEnabled
|
195
|
+
@trigger('modal:close')
|
196
|
+
else
|
197
|
+
@close()
|
198
|
+
|
199
|
+
# jQuery style event handler
|
200
|
+
triggerCancel: (e) =>
|
201
|
+
# triggers cancel
|
202
|
+
e?.preventDefault()
|
203
|
+
|
204
|
+
if @beforeCancel
|
205
|
+
return if @beforeCancel() is false
|
206
|
+
|
207
|
+
@cancel?()
|
208
|
+
|
209
|
+
if @regionEnabled
|
210
|
+
@triggerMethod 'modal:close'
|
211
|
+
else
|
212
|
+
@close()
|
213
|
+
|
214
|
+
close: ->
|
215
|
+
# closes view
|
216
|
+
Marionette.$('body').off 'keyup', @checkKey
|
217
|
+
Marionette.$('body').off 'click', @clickOutside
|
218
|
+
|
219
|
+
@onClose?()
|
220
|
+
|
221
|
+
@shouldAnimate = false
|
222
|
+
@modalEl.addClass("#{@prefix}-modal--close")
|
223
|
+
@$el.fadeOut(duration: 200)
|
224
|
+
|
225
|
+
_.delay =>
|
226
|
+
@currentView?.remove?()
|
227
|
+
@remove()
|
228
|
+
, 200
|
229
|
+
|
230
|
+
openAt: (index) ->
|
231
|
+
# loop through views and trigger the index
|
232
|
+
i = 0
|
233
|
+
for key of @views
|
234
|
+
unless key is 'length'
|
235
|
+
view = @views[key] if i is index
|
236
|
+
i++
|
237
|
+
|
238
|
+
if view
|
239
|
+
@currentIndex = index
|
240
|
+
@triggerView(data: view)
|
241
|
+
|
242
|
+
return this
|
243
|
+
|
244
|
+
next: ->
|
245
|
+
@openAt(@currentIndex + 1) if @currentIndex + 1 < @views.length
|
246
|
+
|
247
|
+
previous: ->
|
248
|
+
@openAt(@currentIndex - 1) if @currentIndex - 1 < @views.length - 1
|
@@ -0,0 +1,26 @@
|
|
1
|
+
/* Modal positioning */
|
2
|
+
.bbm-wrapper
|
3
|
+
box-sizing: border-box
|
4
|
+
position: absolute
|
5
|
+
left: 0
|
6
|
+
top: 0
|
7
|
+
width: 100%
|
8
|
+
height: 100%
|
9
|
+
z-index: 100
|
10
|
+
padding: 50px 10px
|
11
|
+
|
12
|
+
*
|
13
|
+
box-sizing: border-box
|
14
|
+
|
15
|
+
overflow-x: auto
|
16
|
+
overflow-y: scroll
|
17
|
+
|
18
|
+
.bbm-modal
|
19
|
+
border-radius: 3px
|
20
|
+
margin: auto
|
21
|
+
width: auto
|
22
|
+
max-width: 550px
|
23
|
+
|
24
|
+
.bbm-views
|
25
|
+
width: 100%
|
26
|
+
box-sizing: border-box
|