lanes 0.5.6 → 0.6.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.
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: ->