lanes 0.5.6 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -2
  3. data/client/lanes/Config.coffee +6 -5
  4. data/client/lanes/access/LoginDialog.cjsx +4 -2
  5. data/client/lanes/access/Roles.coffee +3 -0
  6. data/client/lanes/access/screens/user-management/Editor.cjsx +1 -1
  7. data/client/lanes/components/grid/Body.cjsx +4 -3
  8. data/client/lanes/components/grid/Grid.cjsx +4 -3
  9. data/client/lanes/components/grid/Header.cjsx +1 -1
  10. data/client/lanes/components/grid/PopOverMixin.cjsx +6 -6
  11. data/client/lanes/components/grid/Selections.cjsx +16 -6
  12. data/client/lanes/components/grid/Toolbar.cjsx +1 -1
  13. data/client/lanes/components/grid/styles.scss +21 -5
  14. data/client/lanes/components/modal/Modal.cjsx +18 -9
  15. data/client/lanes/components/record-finder/Clause.cjsx +1 -2
  16. data/client/lanes/components/record-finder/Dialog.cjsx +11 -3
  17. data/client/lanes/components/record-finder/RecordFinder.cjsx +14 -7
  18. data/client/lanes/components/record-finder/styles.scss +1 -0
  19. data/client/lanes/components/select-field/SelectField.cjsx +36 -10
  20. data/client/lanes/components/select-field/styles.scss +7 -0
  21. data/client/lanes/components/shared/Checkbox.cjsx +34 -0
  22. data/client/lanes/components/shared/DateTime.cjsx +3 -2
  23. data/client/lanes/components/shared/ErrorDisplay.cjsx +1 -1
  24. data/client/lanes/components/shared/FieldMixin.cjsx +26 -15
  25. data/client/lanes/components/shared/FieldSet.cjsx +1 -1
  26. data/client/lanes/components/shared/FieldWrapper.cjsx +2 -2
  27. data/client/lanes/components/shared/FormGroup.cjsx +2 -2
  28. data/client/lanes/components/shared/Icon.cjsx +31 -4
  29. data/client/lanes/components/shared/IconButton.cjsx +8 -0
  30. data/client/lanes/components/shared/ImageAsset.cjsx +8 -5
  31. data/client/lanes/components/shared/IndeterminateCheckbox.cjsx +19 -0
  32. data/client/lanes/components/shared/InputFieldMixin.cjsx +6 -0
  33. data/client/lanes/components/shared/JobProgress.cjsx +4 -3
  34. data/client/lanes/components/shared/NetworkActivityOverlay.cjsx +1 -1
  35. data/client/lanes/components/shared/ScreenWrapper.cjsx +1 -1
  36. data/client/lanes/components/shared/ToggleField.cjsx +2 -1
  37. data/client/lanes/components/shared/Tooltip.cjsx +10 -2
  38. data/client/lanes/components/shared/fields.scss +7 -2
  39. data/client/lanes/components/toolbar/RemoteChangeSets.cjsx +10 -7
  40. data/client/lanes/components/toolbar/SaveButton.cjsx +5 -4
  41. data/client/lanes/components/toolbar/Toolbar.cjsx +18 -19
  42. data/client/lanes/components/toolbar/changes-notification.scss +3 -1
  43. data/client/lanes/components/toolbar/styles.scss +6 -3
  44. data/client/lanes/lib/HotReload.coffee +1 -1
  45. data/client/lanes/lib/all.js +1 -1
  46. data/client/lanes/lib/dom.coffee +14 -1
  47. data/client/lanes/lib/format.coffee +3 -0
  48. data/client/lanes/lib/loader.coffee +0 -2
  49. data/client/lanes/lib/{dom-polyfills.coffee → polyfills.coffee} +3 -0
  50. data/client/lanes/lib/utilFunctions.coffee +5 -7
  51. data/client/lanes/models/Asset.coffee +33 -20
  52. data/client/lanes/models/AssociationMap.coffee +5 -6
  53. data/client/lanes/models/Base.coffee +5 -2
  54. data/client/lanes/models/Collection.coffee +23 -2
  55. data/client/lanes/models/PubSub.coffee +51 -13
  56. data/client/lanes/models/Query.coffee +53 -42
  57. data/client/lanes/models/State.coffee +5 -4
  58. data/client/lanes/models/Sync.coffee +5 -3
  59. data/client/lanes/models/index.js +1 -1
  60. data/client/lanes/models/mixins/TrackCollectionRemovals.coffee +17 -0
  61. data/client/lanes/models/query/ArrayResult.coffee +16 -17
  62. data/client/lanes/models/query/CollectionResult.coffee +4 -4
  63. data/client/lanes/react/Component.coffee +14 -13
  64. data/client/lanes/react/Root.cjsx +1 -1
  65. data/client/lanes/react/Screen.coffee +1 -0
  66. data/client/lanes/react/Viewport.coffee +52 -16
  67. data/client/lanes/react/mixins/Access.coffee +5 -0
  68. data/client/lanes/react/mixins/Data.coffee +52 -144
  69. data/client/lanes/react/mixins/MonitorSize.coffee +1 -1
  70. data/client/lanes/react/mixins/ReadEditingState.coffee +3 -0
  71. data/client/lanes/screens/Commands.coffee +1 -1
  72. data/client/lanes/screens/CommonComponents.cjsx +2 -2
  73. data/client/lanes/screens/Definitions.coffee +3 -1
  74. data/client/lanes/screens/SystemSettings.cjsx +13 -18
  75. data/client/lanes/screens/UserPreferences.cjsx +1 -1
  76. data/client/lanes/styles/fonts.scss +2 -3
  77. data/client/lanes/styles/global/styles.scss +2 -1
  78. data/client/lanes/testing/TestObjects.coffee +6 -2
  79. data/client/lanes/vendor/action_cable.js +590 -0
  80. data/client/lanes/vendor/development/calendar.js +56 -56
  81. data/client/lanes/vendor/development/commons.js +7819 -6690
  82. data/client/lanes/vendor/development/data.js +1877 -1455
  83. data/client/lanes/vendor/development/helpers.js +39 -82
  84. data/client/lanes/vendor/development/toggle.js +20 -20
  85. data/client/lanes/vendor/development/ui.js +14629 -14261
  86. data/client/lanes/vendor/development/widgets.js +3146 -2173
  87. data/client/lanes/vendor/index.js +1 -2
  88. data/client/lanes/vendor/production/calendar.js +56 -56
  89. data/client/lanes/vendor/production/commons.js +6352 -6185
  90. data/client/lanes/vendor/production/data.js +1871 -1456
  91. data/client/lanes/vendor/production/toggle.js +20 -20
  92. data/client/lanes/vendor/production/ui.js +14694 -14286
  93. data/client/lanes/vendor/production/widgets.js +3139 -2166
  94. data/client/lanes/vendor/standalone/index.js +5666 -4586
  95. data/client/lanes/vendor/styles/widgets.scss +5 -5
  96. data/client/lanes/workspace/Layout.cjsx +1 -10
  97. data/client/lanes/workspace/Navbar.cjsx +8 -13
  98. data/client/lanes/workspace/ScreenView.cjsx +3 -4
  99. data/client/lanes/workspace/ScreensMenu.cjsx +8 -19
  100. data/client/lanes/workspace/Tabs.cjsx +6 -14
  101. data/db/migrate/02_create_assets.rb +1 -3
  102. data/lanes.gemspec +16 -11
  103. data/lib/lanes/access/test_fixture_extensions.rb +6 -10
  104. data/lib/lanes/access/track_modifications.rb +24 -0
  105. data/lib/lanes/api/cable.rb +49 -0
  106. data/lib/lanes/api/controller_base.rb +52 -9
  107. data/lib/lanes/api/default_routes.rb +9 -0
  108. data/lib/lanes/api/generic_controller.rb +2 -9
  109. data/lib/lanes/api/handlers/asset.rb +5 -6
  110. data/lib/lanes/api/helper_methods.rb +3 -2
  111. data/lib/lanes/api/javascript_processor.rb +6 -5
  112. data/lib/lanes/api/pub_sub.rb +14 -32
  113. data/lib/lanes/api/request_wrapper.rb +1 -1
  114. data/lib/lanes/api/root.rb +4 -7
  115. data/lib/lanes/api/routing.rb +3 -3
  116. data/lib/lanes/api/sprockets_extension.rb +1 -1
  117. data/lib/lanes/api.rb +4 -0
  118. data/lib/lanes/asset.rb +25 -25
  119. data/lib/lanes/concerns/asset_uploader.rb +25 -47
  120. data/lib/lanes/concerns/export_scope.rb +5 -7
  121. data/lib/lanes/concerns/queries.rb +7 -3
  122. data/lib/lanes/concerns/set_attribute_data.rb +12 -6
  123. data/lib/lanes/configuration.rb +4 -7
  124. data/lib/lanes/db.rb +3 -1
  125. data/lib/lanes/guard_tasks.rb +2 -1
  126. data/lib/lanes/hot_reload_plugin.rb +2 -1
  127. data/lib/lanes/job.rb +0 -1
  128. data/lib/lanes/logger.rb +3 -3
  129. data/lib/lanes/model.rb +0 -3
  130. data/lib/lanes/spec_helper.rb +1 -1
  131. data/lib/lanes/system_settings.rb +8 -14
  132. data/lib/lanes/version.rb +1 -1
  133. data/npm-build/data.js +1 -0
  134. data/npm-build/package.json +21 -19
  135. data/npm-build/ui.js +3 -0
  136. data/npm-build/update-dayz +2 -2
  137. data/npm-build/update-model-bindings.js +5 -0
  138. data/spec/command-reference-files/initial/Gemfile +1 -1
  139. data/spec/command-reference-files/initial/config/database.yml +1 -0
  140. data/spec/command-reference-files/initial/config/lanes.rb +3 -3
  141. data/spec/command-reference-files/screen/spec/appy-app/screens/ready-set-go/ReadySetGoSpec.coffee +1 -1
  142. data/spec/lanes/components/grid/GridSpec.coffee +2 -2
  143. data/spec/lanes/components/grid/PopoverEditorSpec.coffee +11 -12
  144. data/spec/lanes/components/select-field/SelectFieldSpec.coffee +6 -4
  145. data/spec/lanes/components/shared/NetworkActivityOverlaySpec.coffee +1 -1
  146. data/spec/lanes/models/PubSubSpec.coffee +6 -8
  147. data/spec/lanes/models/QuerySpec.coffee +19 -0
  148. data/spec/lanes/react/mixins/DataSpec.coffee +18 -16
  149. data/spec/server/api/coffeescript_processor_spec.rb +1 -1
  150. data/spec/server/api/controller_base_spec.rb +77 -0
  151. data/spec/server/api/pub_sub_spec.rb +9 -0
  152. data/spec/server/asset_spec.rb +23 -18
  153. data/spec/server/concerns/export_scope_spec.rb +2 -1
  154. data/spec/server/concerns/exported_limits_spec.rb +14 -9
  155. data/spec/server/concerns/set_attribute_data_spec.rb +17 -0
  156. data/spec/server/spec_helper.rb +29 -11
  157. data/templates/config/database.yml +1 -0
  158. data/templates/spec/client/Screen.coffee +1 -1
  159. data/views/specs.erb +4 -1
  160. metadata +138 -89
  161. data/client/lanes/vendor/message-bus-ajax.js +0 -44
  162. data/client/lanes/vendor/message-bus.js +0 -414
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7af4d51495c7acfa673103570cc84a4c780a7433
4
- data.tar.gz: 080438194e772ac2ef164c2c984f7d65f6ecae88
3
+ metadata.gz: 3733c06e6d643586d352406448ab55ed6812e860
4
+ data.tar.gz: 79c1d5bdebb6522539c01b5844e67cb25a404431
5
5
  SHA512:
6
- metadata.gz: f9f04438ab66b899000c5fbe01601bd05a43c31aa7ba566793df869ecd65ac5b598e482af9468f9b223c0f7d6b1aa67e5b85fe12129010c1489493da80d74048
7
- data.tar.gz: ced3986071d0069200a9327ebc8fba3156f0a104dafd4076d6146d50f5d5edc9c51188fd5d256e4a8f1b55b0ecbe78e0fa47ddf8e0575023ee4cec3ba40a760d
6
+ metadata.gz: 3ee768c759d3cefbc9a8f855b88842efbba485873d9f3f11121710826b93e13469cf46ee635338802ecb8d06c0aa064b78233947215db3751a98d58fdbab4746
7
+ data.tar.gz: 2ce0dcc798cce89666e00a7c4da30a90c9821f5a366460b5f9ccb8e4161ed15d8a90af3984cf94d073e2e004abd30c320d6280ef96ce3ea031046cf1396048fb
data/Gemfile CHANGED
@@ -1,7 +1,11 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem "yard-activerecord", github: 'nathanstitt/yard-activerecord', branch: 'develop'
4
- gem "active_record_mocks", github: 'nathanstitt/active_record_mocks', branch: 'develop'
3
+ gem "yard-activerecord",
4
+ git: 'https://github.com/nathanstitt/yard-activerecord',
5
+ branch: 'develop'
6
+ gem "active_record_mocks",
7
+ git: 'https://github.com/active_record_mocks',
8
+ branch: 'develop'
5
9
 
6
10
  gem 'puma'
7
11
  gem 'pry-byebug'
@@ -28,13 +28,14 @@ class SystemSettings extends Lanes.Models.Base
28
28
  class Config extends Lanes.Models.State
29
29
 
30
30
  session:
31
- csrf_token: { type: 'string', setOnce: true }
32
- root_path: { type: 'string', setOnce: true }
33
- api_path: { type: 'string', setOnce: true }
34
- assets_path_prefix: { type: 'string', setOnce: true }
31
+ csrf_token: { type: 'string', setOnce: true }
32
+ root_path: { type: 'string', setOnce: true }
33
+ api_path: { type: 'string', default: '/api' }
34
+ api_host: { type: 'string', default: window.location.host }
35
35
  environment: { type: 'string', setOnce: true }
36
- initial_workspace_screen_id: { type: 'string', setOnce: true }
37
36
  system_settings: { type: 'state', required: true }
37
+ assets_path_prefix: { type: 'string', setOnce: true }
38
+ initial_workspace_screen_id: { type: 'string', setOnce: true }
38
39
 
39
40
  derived:
40
41
  env:
@@ -43,7 +43,7 @@ class Lanes.Access.LoginDialog extends Lanes.React.Component
43
43
  getDefaultProps: ->
44
44
  writable: true, editOnly: true
45
45
 
46
- dataObjects: ->
46
+ modelBindings: ->
47
47
  model: 'props'
48
48
 
49
49
  warning: ->
@@ -53,7 +53,9 @@ class Lanes.Access.LoginDialog extends Lanes.React.Component
53
53
 
54
54
  render: ->
55
55
  <div>
56
- {@warning() if @state?.hasError}
56
+ <LC.ErrorDisplay model={@props.model} />
57
+ <LC.NetworkActivityOverlay model={@props.model} />
58
+
57
59
  <BS.Row>
58
60
  <BS.Col mdOffset={2} xs={12} md={8}>
59
61
 
@@ -50,6 +50,9 @@ class Lanes.Models.UserRoleSet
50
50
  @roles.push( role )
51
51
  @locked_fields = role.decodeLockedFields(access.locked_fields, @locked_fields)
52
52
 
53
+ includes: (name) ->
54
+ !!_.find(@roles, {type: name})
55
+
53
56
  can: (method, model, field) ->
54
57
  return true unless model
55
58
  if model.constructor and not model.prototype
@@ -17,4 +17,4 @@ class Lanes.Access.Screens.UserManagement.Editor extends Lanes.React.Component
17
17
  </form>
18
18
 
19
19
  render: ->
20
- @renderPopover()
20
+ @renderPopover(height: 380)
@@ -43,7 +43,7 @@ class Lanes.Components.Grid.Body extends Lanes.React.BaseComponent
43
43
  if @fieldConvertors[field.type] then @fieldConvertors[field.type](value) else value
44
44
 
45
45
  renderColumn: (rowNum, index, field, results, row) ->
46
- value = results.valueForField(rowNum, field)
46
+ value = results.valueForField(row, field)
47
47
 
48
48
  onColClick = if field.onColumnClick
49
49
  _.partial(field.onColumnClick, _,
@@ -51,7 +51,6 @@ class Lanes.Components.Grid.Body extends Lanes.React.BaseComponent
51
51
  )
52
52
  else
53
53
  null
54
-
55
54
  value = if field.format
56
55
  field.format(value, row, @props.query)
57
56
  else
@@ -77,7 +76,9 @@ class Lanes.Components.Grid.Body extends Lanes.React.BaseComponent
77
76
 
78
77
  if @props.onRowClick
79
78
  onClick = _.partial(@onRowClick, _, row, rowNum)
80
- <div key={rowNum} ref={ref} className="r" onClick={onClick} >
79
+
80
+ classes = _.classnames('r', focused: @props.isRowFocused?(row, rowNum))
81
+ <div key={rowNum} ref={ref} className={classes} onClick={onClick} >
81
82
  {fields}
82
83
  </div>
83
84
 
@@ -24,6 +24,7 @@ class Lanes.Components.Grid extends Lanes.React.Component
24
24
  onSelectionChange: React.PropTypes.func
25
25
  autoLoadQuery: React.PropTypes.bool
26
26
  renderCompleteResults: React.PropTypes.bool
27
+ isRowFocused: React.PropTypes.func
27
28
  toolbarChildren: React.PropTypes.oneOfType([
28
29
  React.PropTypes.element,
29
30
  React.PropTypes.arrayOf(React.PropTypes.element)
@@ -33,10 +34,10 @@ class Lanes.Components.Grid extends Lanes.React.Component
33
34
  Lanes.React.Mixins.MonitorSize
34
35
  ]
35
36
 
36
- dataObjects:
37
+ modelBindings:
37
38
  query: 'props'
38
39
 
39
- bindDataEvents:
40
+ bindEvents:
40
41
  query: 'load change sort'
41
42
 
42
43
  componentWillReceiveProps: (nextProps) ->
@@ -91,7 +92,6 @@ class Lanes.Components.Grid extends Lanes.React.Component
91
92
 
92
93
  render: ->
93
94
  cellStyles = new Lanes.Components.Grid.CellStyles(@query.fields)
94
-
95
95
  <div className='grid-component'>
96
96
  <Lanes.Components.Grid.Toolbar {...@props} startEdit={@startEdit} />
97
97
  <Lanes.Components.Grid.Header {...@props} cellStyles={cellStyles} />
@@ -103,6 +103,7 @@ class Lanes.Components.Grid extends Lanes.React.Component
103
103
  onEditCancel={@cancelEdit}
104
104
  isLoading={@state.isLoading}
105
105
  cellStyles={cellStyles}
106
+ isRowFocused={@props.isRowFocused}
106
107
  onRowClick={@onRowClick if @props.editor or @props.onSelectionChange}
107
108
  />
108
109
  </div>
@@ -19,7 +19,7 @@ class Lanes.Components.Grid.Header extends Lanes.React.BaseComponent
19
19
  desc: sorted and not @props.query.sortAscending
20
20
  @props.cellStyles.props[i].className
21
21
  )
22
- <div key={i}
22
+ <div key={f.cid}
23
23
  {...@props.cellStyles.props[i]}
24
24
  onClick={_.partial(@onColumnClick, f)}
25
25
  className={classNames}
@@ -13,23 +13,23 @@ Lanes.Components.Grid.PopoverMixin = {
13
13
  getDefaultProps: ->
14
14
  width: 280, height: 350
15
15
 
16
- renderPopover: (child) ->
17
- props = _.extend({}, @props)
18
- {position} = @props
16
+ renderPopover: (props) ->
17
+ props = _.extend({}, @props, props)
18
+ {position} = props
19
19
  if position.left > ((position.container.width / 2) - 150)
20
20
  props.placement = 'left'
21
- props.positionLeft = position.left - @props.width
21
+ props.positionLeft = position.left - props.width
22
22
  else
23
23
  props.placement = 'right'
24
24
  props.positionLeft = position.left
25
- props.arrowOffsetTop = Math.min(position.top + 20, (@props.height - 75))
25
+ props.arrowOffsetTop = Math.min(position.top + 20, (props.height - 75))
26
26
  props.positionTop = Math.max(5, position.top - props.arrowOffsetTop + (position.rowHeight / 2))
27
27
 
28
28
  <div className="editor po">
29
29
  <BS.Popover
30
30
  id="editing-form"
31
31
  {...props}
32
- style={height: @props.height, width: @props.width}
32
+ style={height: props.height, width: props.width}
33
33
  title = "Edit #{@model.name || 'Record'}"
34
34
  >
35
35
  {@renderEditingBody()}
@@ -1,7 +1,7 @@
1
1
  class CheckBox extends Lanes.React.BaseComponent
2
2
  d: -> @props.query.results.xtraData(@props.row)
3
3
 
4
- onChange: (ev) -> #, me, results, index) ->
4
+ onChange: (ev) ->
5
5
  @d().selected = ev.target.checked
6
6
  @props.selections.onChange?(@props)
7
7
  @forceUpdate()
@@ -9,7 +9,8 @@ class CheckBox extends Lanes.React.BaseComponent
9
9
  render: ->
10
10
  selected = @d().selected
11
11
  selected = @props.selections.selectionDefault unless selected?
12
- <input type="checkbox" checked={selected} onChange={@onChange} />
12
+ attrs = @props.selections.attributesGetter?(@props) || {}
13
+ <input type="checkbox" checked={selected} onChange={@onChange} {...attrs} />
13
14
 
14
15
 
15
16
 
@@ -23,9 +24,17 @@ DEFAULTS =
23
24
  false == this.xtraData(indx)?.selected
24
25
 
25
26
  class Lanes.Components.Grid.Selections
26
- constructor: (options) ->
27
+
28
+ @setRow: (row, xd, selected) ->
29
+ xd.selected = selected
30
+
31
+ @isSelected: (row, xd, selected) ->
32
+ !(xd && false == xd.selected)
33
+
34
+
35
+ constructor: (options = {}) ->
27
36
  _.extend(@, DEFAULTS)
28
- @onChange = options.onChange
37
+ _.extend(@, _.pick(options, 'onChange', 'attributesGetter'))
29
38
  @choices = {}
30
39
  _.bindAll(@, 'onColumnClick')
31
40
  @component = _.partial(@component, _, @)
@@ -33,8 +42,9 @@ class Lanes.Components.Grid.Selections
33
42
  onColumnClick: (ev, props) ->
34
43
  unless ev.target.tagName is 'INPUT'
35
44
  input = ev.target.querySelector('input')
36
- xd = props.query.results.xtraData(props.rowNum)
37
- xd.selected = input.checked = !input.checked
45
+ unless input.disabled
46
+ xd = props.query.results.xtraData(props.rowNum)
47
+ xd.selected = input.checked = !input.checked
38
48
  @onChange?(props)
39
49
  ev.stopPropagation()
40
50
 
@@ -13,7 +13,7 @@ class Lanes.Components.Grid.Toolbar extends Lanes.React.BaseComponent
13
13
  @props.startEdit(0)
14
14
 
15
15
  AddButton: ->
16
- return null unless @props.onAddRecord and @props.allowCreate
16
+ return null unless @props.allowCreate
17
17
  <BS.Button className="navbar-btn add-row pull-right"
18
18
  onClick={@onAddRecord} bsSize='small'
19
19
  >
@@ -43,7 +43,6 @@
43
43
  flex-direction: row;
44
44
  align-items: center;
45
45
  justify-content: space-between;
46
-
47
46
  &.sort {
48
47
  &:after {
49
48
  content: $fa-var-sort;
@@ -52,6 +51,14 @@
52
51
  right: -5px;
53
52
  color: lightgray;
54
53
  }
54
+ &.center {
55
+ position: relative;
56
+ &:after {
57
+ right: 5px;
58
+ position: absolute;
59
+ }
60
+ }
61
+
55
62
  &:not(.asc):not(.desc) {
56
63
  &:after {
57
64
  @include hidden-print;
@@ -70,8 +77,9 @@
70
77
  }
71
78
  .r {
72
79
  margin-right: 10px;
73
- /* page-break-inside: avoid; */
80
+ page-break-inside: avoid;
74
81
  break-inside: avoid-page;
82
+ margin-left: 5px;
75
83
  }
76
84
  .grid-body {
77
85
  border-top: 1px solid $table-border-color;
@@ -83,12 +91,20 @@
83
91
  overflow: visible;
84
92
  height: initial;
85
93
  }
86
- .r:nth-child(odd) {
87
- background-color: $table-bg-accent;
88
- }
94
+
89
95
  .c {
90
96
  align-items: center;
91
97
  }
98
+ .r {
99
+ &.focused {
100
+ outline-style: dotted;
101
+ outline-width: 2px;
102
+ outline-color: gray;
103
+ }
104
+ &:nth-child(odd) {
105
+ background-color: $table-bg-accent;
106
+ }
107
+ }
92
108
  }
93
109
 
94
110
  .toolbar {
@@ -60,15 +60,24 @@ class Lanes.Components.Modal extends Lanes.React.Component
60
60
 
61
61
  cls = _.classnames('lanes-modal', @state.className, @context.uistate?.layout_size)
62
62
  Body = @state.body
63
- <BS.Modal.Dialog className={cls} bsSize={@state.size} onHide={@_hide}>
64
- <BS.Modal.Header>
65
- <BS.Modal.Title>{@state.title}</BS.Modal.Title>
66
- </BS.Modal.Header>
63
+ <span>
64
+ <BS.Modal
65
+ show={@state.show}
66
+ className={cls}
67
+ bsSize={@state.size}
68
+ onHide={@_hide}
69
+ container={@}
70
+ >
67
71
 
68
- <BS.Modal.Body style={maxHeight: @context.viewport.height - 250}>
69
- <Body ref="body" {...@props} modal={@} />
70
- </BS.Modal.Body>
72
+ <BS.Modal.Header>
73
+ <BS.Modal.Title>{@state.title}</BS.Modal.Title>
74
+ </BS.Modal.Header>
71
75
 
72
- <BS.Modal.Footer>{buttons}</BS.Modal.Footer>
76
+ <BS.Modal.Body style={maxHeight: @context.viewport.height - 250}>
77
+ <Body {...@props} modal={@} />
78
+ </BS.Modal.Body>
73
79
 
74
- </BS.Modal.Dialog>
80
+ <BS.Modal.Footer>{buttons}</BS.Modal.Footer>
81
+
82
+ </BS.Modal>
83
+ </span>
@@ -11,7 +11,6 @@ class Field extends Lanes.React.Component
11
11
 
12
12
  class Operator extends Lanes.React.Component
13
13
  render: ->
14
- return null unless @model.valid
15
14
  <BS.Row>
16
15
  <input id={@model.id} type="radio" name="operator" value={@model.id}
17
16
  checked={@model.selected}
@@ -53,7 +52,7 @@ class Lanes.Components.RecordFinder.Clause extends Lanes.React.Component
53
52
  </div>
54
53
  <div className="col-xs-6">
55
54
  <div className="operators">
56
- {@model.operators.map (o) ->
55
+ {@model.operators.valid.map (o) ->
57
56
  <Operator key=o.id model=o /> }
58
57
  </div>
59
58
  </div>
@@ -1,6 +1,6 @@
1
1
  class Lanes.Components.RecordFinder.Dialog extends Lanes.React.Component
2
2
 
3
- dataObjects:
3
+ modelBindings:
4
4
  clauses: ->
5
5
  @props.query.clauses
6
6
 
@@ -11,12 +11,19 @@ class Lanes.Components.RecordFinder.Dialog extends Lanes.React.Component
11
11
  contextTypes:
12
12
  viewport: Lanes.PropTypes.State.isRequired
13
13
 
14
+ getInitialState: -> {}
15
+
14
16
  warning: ->
15
17
  <BS.Alert bsStyle='warning'>
16
18
  <strong>{@model.lastServerMessage}</strong>
17
19
  </BS.Alert>
18
20
 
19
- onRecordSelect: (model) ->
21
+ isGridRowFocused: (row, index) ->
22
+ @state.selectedRow is index
23
+
24
+ onRecordSelect: (model, selectedRow) ->
25
+ @setState({selectedRow})
26
+ @props.query.markModified()
20
27
  return unless model
21
28
  @props.query.loadModel(model).then @props.onRecordSelect
22
29
  _.delay( =>
@@ -47,7 +54,8 @@ class Lanes.Components.RecordFinder.Dialog extends Lanes.React.Component
47
54
  </div>
48
55
  <LC.Grid
49
56
  onColumnClick={@onColumnSort}
57
+ isRowFocused={@isGridRowFocused}
50
58
  query={@props.query} height=200 autoLoadQuery
51
- onSelectionChange=@onRecordSelect
59
+ onSelectionChange={@onRecordSelect}
52
60
  />
53
61
  </div>
@@ -1,11 +1,14 @@
1
1
  class Lanes.Components.RecordFinder extends Lanes.React.Component
2
2
 
3
+ listenNetworkEvents: true
4
+
3
5
  propTypes:
4
6
  query: Lanes.PropTypes.State.isRequired
5
7
  model: Lanes.PropTypes.State
6
8
  parentModel: Lanes.PropTypes.State
7
9
  commands: React.PropTypes.object
8
10
  onModelSet: React.PropTypes.func
11
+ associationName: React.PropTypes.string
9
12
 
10
13
  mixins: [
11
14
  Lanes.Components.Form.InputFieldMixin
@@ -14,6 +17,9 @@ class Lanes.Components.RecordFinder extends Lanes.React.Component
14
17
  contextTypes:
15
18
  viewport: Lanes.PropTypes.State.isRequired
16
19
 
20
+ modelBindings: ->
21
+ query: 'props'
22
+
17
23
  modelForAccess: ->
18
24
  if @props.parentModel
19
25
  @props.parentModel[@props.associationName]
@@ -21,13 +27,14 @@ class Lanes.Components.RecordFinder extends Lanes.React.Component
21
27
  @props.model
22
28
 
23
29
  showFinder: ->
24
- @props.query.reset()
30
+ query = @props.query.clone()
31
+ query.autoRetrieve = true
25
32
  @context.viewport.displayModal(
26
33
  title: "Find #{@props.query.title}"
27
34
  buttons: [{title: 'Cancel'}]
28
35
  autoHide: true
29
36
  body: =>
30
- <LC.RecordFinder.Dialog query={@props.query} onRecordSelect={@setModel} />
37
+ <LC.RecordFinder.Dialog query={query} onRecordSelect={@setModel} />
31
38
  )
32
39
 
33
40
  setModel: (model) ->
@@ -54,13 +61,10 @@ class Lanes.Components.RecordFinder extends Lanes.React.Component
54
61
  @props.model[@props.name]
55
62
  value or ''
56
63
 
57
-
58
64
  renderInputField: (props, handlers) ->
59
- # editOnly writable
60
65
  model = @props.parentModel or @props.model
61
66
 
62
67
  <BS.InputGroup className="record-finder">
63
-
64
68
  <BS.FormControl
65
69
  {...props} {...handlers}
66
70
  onChange={@fieldMixinSetValue}
@@ -69,7 +73,10 @@ class Lanes.Components.RecordFinder extends Lanes.React.Component
69
73
  />
70
74
 
71
75
  <BS.InputGroup.Button>
72
- <button className="btn btn-primary icon icon-search icon-lg"
73
- onClick={@showFinder} />
76
+ <button className='btn btn-primary' onClick={@showFinder}>
77
+ <LC.Icon lg
78
+ type={if @state.isRequesting then 'spinner' else 'search'}
79
+ />
80
+ </button>
74
81
  </BS.InputGroup.Button>
75
82
  </BS.InputGroup>
@@ -12,6 +12,7 @@
12
12
  }
13
13
  }
14
14
  .input-group-btn {
15
+ .icon { margin: 0; }
15
16
  @include hidden-print;
16
17
  }
17
18
  }
@@ -13,26 +13,32 @@ class Lanes.Components.SelectField extends Lanes.React.Component
13
13
  displayLimit: React.PropTypes.number
14
14
  syncOptions: React.PropTypes.object
15
15
  multiSelect: React.PropTypes.bool
16
- includeBlankRow: React.PropTypes.bool
16
+ fetchOnSelect:React.PropTypes.bool
17
+ withClearBtn: React.PropTypes.bool
18
+ fallBackValue:React.PropTypes.string
17
19
 
18
20
  getDefaultProps: ->
19
21
  labelField: 'label', idField: 'id'
20
22
 
21
23
  fieldClassName: 'select'
22
24
 
23
- dataObjects:
25
+ modelBindings:
24
26
  query: ->
25
- src = @props.queryModel or
26
- @props.model.associations?.collectionFor(@props.name).model
27
27
  query = new Lanes.Models.Query({
28
28
  syncOptions: Lanes.Models.Query.mergedSyncOptions(@props.syncOptions)
29
- src: src
29
+ src: @props.queryModel or @props.model.associations?.collectionFor(@props.name).model
30
30
  fields: [
31
31
  {id: @props.idField, visible: false},
32
32
  @props.labelField
33
33
  ]
34
34
  })
35
35
 
36
+ clearSelectedValue: ->
37
+ @setState(tempDisplayValue: '') if @state.tempDisplayValue
38
+ if @props.setSelection then @props.setSelection(null) else
39
+ @model?.unset(@props.name)
40
+ @forceUpdate()
41
+
36
42
  getValue: ->
37
43
  return undefined if @state.isOpen and not @props.multiSelect
38
44
  return @state.tempDisplayValue if @state.tempDisplayValue
@@ -43,6 +49,8 @@ class Lanes.Components.SelectField extends Lanes.React.Component
43
49
  label = _.find( @props.choices, id: model.id)?[@props.labelField]
44
50
  if label
45
51
  {id: model.id, "#{@props.labelField}": label}
52
+ else if @props.fallBackValue
53
+ @props.fallBackValue
46
54
  else if model.id
47
55
  model.id
48
56
  else
@@ -77,7 +85,8 @@ class Lanes.Components.SelectField extends Lanes.React.Component
77
85
  @setModel(model)
78
86
  else
79
87
  model.fetch(@props.syncOptions).then(@setModel)
80
- @setState(isOpen: false, tempDisplayValue: value, requestInProgress: true)
88
+ @setState(requestInProgress: true)
89
+ @setState(isOpen: false, tempDisplayValue: value)
81
90
  else
82
91
  if not @props.choices
83
92
  c = @getClause()
@@ -102,14 +111,17 @@ class Lanes.Components.SelectField extends Lanes.React.Component
102
111
  value[@props.labelField]
103
112
  else
104
113
  value
105
- <BS.FormControl.Static {...props}>
106
- {label}
107
- </BS.FormControl.Static>
114
+ clean = LC.Form.FieldMixin.statics.cleanSizeProps(props, @)
108
115
 
116
+ <BS.FormControl.Static {...clean}>{label}</BS.FormControl.Static>
109
117
 
110
- renderEdit: (props) ->
118
+ renderSelectControl: (props) ->
111
119
  type = if @props.multiSelect then 'Multiselect' else 'Combobox'
112
120
  Component = Lanes.Vendor.ReactWidgets[type]
121
+
122
+ props = _.without( props, _.keys(@constructor.propTypes) )
123
+
124
+ # props = _.without(props, 'withClearBtn', `getSelection`, `setSelection`
113
125
  <Component
114
126
  ref="select"
115
127
  className={@props.className}
@@ -127,3 +139,17 @@ class Lanes.Components.SelectField extends Lanes.React.Component
127
139
  value={@getValue()}
128
140
  {...props}
129
141
  />
142
+
143
+ renderEdit: (props) ->
144
+ if @props.withClearBtn
145
+ <BS.InputGroup className="selection">
146
+ {@renderSelectControl(props)}
147
+ <BS.InputGroup.Button>
148
+ <button className='btn' onClick={@clearSelectedValue}>
149
+ <LC.Icon type='undo' flush tooltip="Clear Selection"
150
+ tooltipProps={trigger: ['hover', 'focus']} />
151
+ </button>
152
+ </BS.InputGroup.Button>
153
+ </BS.InputGroup>
154
+ else
155
+ @renderSelectControl(props)
@@ -1,5 +1,12 @@
1
1
  @import "lanes/vendor/styles/widgets";
2
2
 
3
+ .input-group {
4
+ .select:first-child {
5
+ border-top-right-radius: 0;
6
+ border-bottom-right-radius: 0;
7
+ }
8
+ }
9
+
3
10
  .rw-combobox, .rw-datetimepicker, .rw-numberpicker, .rw-dropdownlist {
4
11
  padding-bottom: 1px; // push down so border isn't covered
5
12
  input { height: 32px; padding: 6px 12px; }
@@ -0,0 +1,34 @@
1
+ class FakeInputEvent
2
+ constructor: (value) ->
3
+ @target = {value}
4
+ isDefaultPrevented: -> false
5
+
6
+
7
+ class Lanes.Components.Checkbox extends Lanes.React.Component
8
+
9
+ propTypes:
10
+ supportIndeterminate: React.PropTypes.bool
11
+
12
+ componentDidMount: -> @updateIndeterminate()
13
+ componentDidUpdate: -> @updateIndeterminate()
14
+
15
+ mixins: [
16
+ Lanes.Components.Form.InputFieldMixin
17
+ ]
18
+
19
+ updateIndeterminate: ->
20
+ return unless @props.supportIndeterminate
21
+ _.dom(@).el.indeterminate =
22
+ @props.checked isnt true and @props.checked isnt false
23
+
24
+ handleCheckboxChange: (ev) ->
25
+ if ev.target.checked
26
+ @fieldMixinSetValue( new FakeInputEvent(@props.value) )
27
+
28
+ renderInputField: (props, handlers) ->
29
+ <input type="checkbox"
30
+ {...handlers}
31
+ {...props}
32
+ checked={@props.checked}
33
+ onChange={@handleCheckboxChange}
34
+ />
@@ -21,7 +21,8 @@ class Lanes.Components.DateTime extends Lanes.React.Component
21
21
 
22
22
 
23
23
  renderDisplay: (props) ->
24
- <BS.FormControl.Static {...props}>
24
+ clean = LC.Form.FieldMixin.statics.cleanSizeProps(props)
25
+ <BS.FormControl.Static {...clean}>
25
26
  {_.moment(@model[@props.name]).format(@props.format)}
26
27
  </BS.FormControl.Static>
27
28
 
@@ -31,7 +32,7 @@ class Lanes.Components.DateTime extends Lanes.React.Component
31
32
  value: @fieldMixinGetValue()
32
33
  onChange: @handleDateTimeChange
33
34
  }, @props)
34
-
35
+ props = _.omit(LC.Form.FieldMixin.statics.cleanSizeProps(props), 'writable')
35
36
  <Lanes.Vendor.ReactWidgets.DateTimePicker
36
37
  {...props}
37
38
  />
@@ -3,7 +3,7 @@ class Lanes.Components.ErrorDisplay extends Lanes.React.Component
3
3
  propTypes:
4
4
  model: Lanes.PropTypes.State.isRequired
5
5
 
6
- bindDataEvents: ->
6
+ bindEvents: ->
7
7
  model: "change:error"
8
8
 
9
9
  clearErrors: ->