trestle 0.9.0 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +28 -0
  3. data/.gitignore +3 -0
  4. data/CONTRIBUTING.md +38 -0
  5. data/Gemfile +12 -5
  6. data/README.md +9 -2
  7. data/app/assets/bundle/trestle/bundle.css +9 -9
  8. data/app/assets/bundle/trestle/bundle.js +50 -48
  9. data/app/assets/bundle/trestle/fa-brands-400.eot +0 -0
  10. data/app/assets/bundle/trestle/fa-brands-400.svg +818 -597
  11. data/app/assets/bundle/trestle/fa-brands-400.ttf +0 -0
  12. data/app/assets/bundle/trestle/fa-brands-400.woff +0 -0
  13. data/app/assets/bundle/trestle/fa-brands-400.woff2 +0 -0
  14. data/app/assets/bundle/trestle/fa-regular-400.eot +0 -0
  15. data/app/assets/bundle/trestle/fa-regular-400.svg +93 -95
  16. data/app/assets/bundle/trestle/fa-regular-400.ttf +0 -0
  17. data/app/assets/bundle/trestle/fa-regular-400.woff +0 -0
  18. data/app/assets/bundle/trestle/fa-regular-400.woff2 +0 -0
  19. data/app/assets/bundle/trestle/fa-solid-900.eot +0 -0
  20. data/app/assets/bundle/trestle/fa-solid-900.svg +1209 -842
  21. data/app/assets/bundle/trestle/fa-solid-900.ttf +0 -0
  22. data/app/assets/bundle/trestle/fa-solid-900.woff +0 -0
  23. data/app/assets/bundle/trestle/fa-solid-900.woff2 +0 -0
  24. data/app/assets/bundle/trestle/flatpickr/ar-dz.js +1 -0
  25. data/app/assets/bundle/trestle/flatpickr/ar.js +1 -1
  26. data/app/assets/bundle/trestle/flatpickr/at.js +1 -1
  27. data/app/assets/bundle/trestle/flatpickr/az.js +1 -1
  28. data/app/assets/bundle/trestle/flatpickr/be.js +1 -1
  29. data/app/assets/bundle/trestle/flatpickr/bg.js +1 -1
  30. data/app/assets/bundle/trestle/flatpickr/bn.js +1 -1
  31. data/app/assets/bundle/trestle/flatpickr/bs.js +1 -1
  32. data/app/assets/bundle/trestle/flatpickr/cat.js +1 -1
  33. data/app/assets/bundle/trestle/flatpickr/ckb.js +1 -0
  34. data/app/assets/bundle/trestle/flatpickr/cs.js +1 -1
  35. data/app/assets/bundle/trestle/flatpickr/cy.js +1 -1
  36. data/app/assets/bundle/trestle/flatpickr/da.js +1 -1
  37. data/app/assets/bundle/trestle/flatpickr/de.js +1 -1
  38. data/app/assets/bundle/trestle/flatpickr/default.js +1 -1
  39. data/app/assets/bundle/trestle/flatpickr/eo.js +1 -1
  40. data/app/assets/bundle/trestle/flatpickr/es.js +1 -1
  41. data/app/assets/bundle/trestle/flatpickr/et.js +1 -1
  42. data/app/assets/bundle/trestle/flatpickr/fa.js +1 -1
  43. data/app/assets/bundle/trestle/flatpickr/fi.js +1 -1
  44. data/app/assets/bundle/trestle/flatpickr/fo.js +1 -1
  45. data/app/assets/bundle/trestle/flatpickr/fr.js +1 -1
  46. data/app/assets/bundle/trestle/flatpickr/ga.js +1 -1
  47. data/app/assets/bundle/trestle/flatpickr/gr.js +1 -1
  48. data/app/assets/bundle/trestle/flatpickr/he.js +1 -1
  49. data/app/assets/bundle/trestle/flatpickr/hi.js +1 -1
  50. data/app/assets/bundle/trestle/flatpickr/hr.js +1 -1
  51. data/app/assets/bundle/trestle/flatpickr/hu.js +1 -1
  52. data/app/assets/bundle/trestle/flatpickr/hy.js +1 -0
  53. data/app/assets/bundle/trestle/flatpickr/id.js +1 -1
  54. data/app/assets/bundle/trestle/flatpickr/is.js +1 -1
  55. data/app/assets/bundle/trestle/flatpickr/it.js +1 -1
  56. data/app/assets/bundle/trestle/flatpickr/ja.js +1 -1
  57. data/app/assets/bundle/trestle/flatpickr/ka.js +1 -1
  58. data/app/assets/bundle/trestle/flatpickr/km.js +1 -1
  59. data/app/assets/bundle/trestle/flatpickr/ko.js +1 -1
  60. data/app/assets/bundle/trestle/flatpickr/kz.js +1 -1
  61. data/app/assets/bundle/trestle/flatpickr/lt.js +1 -1
  62. data/app/assets/bundle/trestle/flatpickr/lv.js +1 -1
  63. data/app/assets/bundle/trestle/flatpickr/mk.js +1 -1
  64. data/app/assets/bundle/trestle/flatpickr/mn.js +1 -1
  65. data/app/assets/bundle/trestle/flatpickr/ms.js +1 -1
  66. data/app/assets/bundle/trestle/flatpickr/my.js +1 -1
  67. data/app/assets/bundle/trestle/flatpickr/nl.js +1 -1
  68. data/app/assets/bundle/trestle/flatpickr/nn.js +1 -0
  69. data/app/assets/bundle/trestle/flatpickr/no.js +1 -1
  70. data/app/assets/bundle/trestle/flatpickr/pa.js +1 -1
  71. data/app/assets/bundle/trestle/flatpickr/pl.js +1 -1
  72. data/app/assets/bundle/trestle/flatpickr/pt.js +1 -1
  73. data/app/assets/bundle/trestle/flatpickr/ro.js +1 -1
  74. data/app/assets/bundle/trestle/flatpickr/ru.js +1 -1
  75. data/app/assets/bundle/trestle/flatpickr/si.js +1 -1
  76. data/app/assets/bundle/trestle/flatpickr/sk.js +1 -1
  77. data/app/assets/bundle/trestle/flatpickr/sl.js +1 -1
  78. data/app/assets/bundle/trestle/flatpickr/sq.js +1 -1
  79. data/app/assets/bundle/trestle/flatpickr/sr-cyr.js +1 -1
  80. data/app/assets/bundle/trestle/flatpickr/sr.js +1 -1
  81. data/app/assets/bundle/trestle/flatpickr/sv.js +1 -1
  82. data/app/assets/bundle/trestle/flatpickr/th.js +1 -1
  83. data/app/assets/bundle/trestle/flatpickr/tr.js +1 -1
  84. data/app/assets/bundle/trestle/flatpickr/uk.js +1 -1
  85. data/app/assets/bundle/trestle/flatpickr/uz.js +1 -0
  86. data/app/assets/bundle/trestle/flatpickr/uz_latn.js +1 -0
  87. data/app/assets/bundle/trestle/flatpickr/vn.js +1 -1
  88. data/app/assets/bundle/trestle/flatpickr/zh-tw.js +1 -1
  89. data/app/assets/bundle/trestle/flatpickr/zh.js +1 -1
  90. data/app/assets/javascripts/trestle/admin.js +1 -0
  91. data/app/assets/javascripts/trestle/i18n.js.erb +8 -0
  92. data/{lib → app/controllers/concerns}/trestle/controller/breadcrumbs.rb +1 -1
  93. data/app/controllers/concerns/trestle/controller/title.rb +20 -0
  94. data/app/controllers/concerns/trestle/controller/toolbars.rb +30 -0
  95. data/app/controllers/concerns/trestle/resource/controller/actions.rb +133 -0
  96. data/app/controllers/concerns/trestle/resource/controller/data_methods.rb +52 -0
  97. data/app/controllers/concerns/trestle/resource/controller/redirection.rb +23 -0
  98. data/app/controllers/concerns/trestle/resource/controller/toolbar.rb +11 -0
  99. data/app/controllers/trestle/admin_controller.rb +31 -0
  100. data/app/controllers/trestle/application_controller.rb +12 -0
  101. data/app/controllers/trestle/dashboard_controller.rb +2 -2
  102. data/app/controllers/trestle/resource_controller.rb +6 -0
  103. data/app/helpers/trestle/avatar_helper.rb +6 -2
  104. data/app/helpers/trestle/container_helper.rb +12 -4
  105. data/app/helpers/trestle/grid_helper.rb +51 -7
  106. data/app/helpers/trestle/hook_helper.rb +1 -25
  107. data/app/helpers/trestle/i18n_helper.rb +8 -15
  108. data/app/helpers/trestle/layout_helper.rb +20 -0
  109. data/app/helpers/trestle/navigation_helper.rb +23 -0
  110. data/app/helpers/trestle/params_helper.rb +4 -1
  111. data/app/helpers/trestle/table_helper.rb +22 -3
  112. data/app/helpers/trestle/title_helper.rb +11 -0
  113. data/app/helpers/trestle/toolbars_helper.rb +0 -11
  114. data/app/helpers/trestle/url_helper.rb +1 -11
  115. data/app/views/layouts/trestle/admin.html.erb +6 -6
  116. data/app/views/trestle/application/_dialog.html.erb +1 -1
  117. data/app/views/trestle/application/_header.html.erb +1 -3
  118. data/app/views/trestle/flash/_alert.html.erb +3 -1
  119. data/app/views/trestle/flash/_debug.html.erb +13 -8
  120. data/app/views/trestle/flash/_flash.html.erb +1 -1
  121. data/app/views/trestle/resource/_scopes.html.erb +17 -8
  122. data/app/views/trestle/resource/edit.html.erb +2 -2
  123. data/app/views/trestle/resource/index.html.erb +3 -3
  124. data/app/views/trestle/resource/new.html.erb +1 -1
  125. data/app/views/trestle/resource/show.html.erb +2 -2
  126. data/app/views/trestle/shared/_sidebar.html.erb +5 -6
  127. data/app/views/trestle/table/_pagination.html.erb +1 -1
  128. data/config/locales/de.rb +18 -0
  129. data/config/locales/de.yml +101 -0
  130. data/config/locales/vi.rb +18 -0
  131. data/config/locales/vi.yml +101 -0
  132. data/config/routes.rb +1 -1
  133. data/frontend/css/components/_alerts.scss +31 -0
  134. data/frontend/css/components/_avatar.scss +14 -0
  135. data/frontend/css/components/_buttons.scss +0 -1
  136. data/frontend/css/components/_datepicker.scss +1 -0
  137. data/frontend/css/components/_fields.scss +6 -0
  138. data/frontend/css/components/_modal.scss +2 -0
  139. data/frontend/css/components/_scopes.scss +66 -21
  140. data/frontend/css/components/_table.scss +20 -0
  141. data/frontend/css/components/_tags.scss +2 -0
  142. data/frontend/css/components/_timestamp.scss +1 -1
  143. data/frontend/css/layout/_content.scss +5 -2
  144. data/frontend/css/layout/_sidebar.scss +9 -3
  145. data/frontend/css/variables/_bootstrap.scss +1 -0
  146. data/frontend/css/variables/_trestle.scss +25 -5
  147. data/frontend/js/components/confirmation.js +4 -2
  148. data/frontend/js/components/dialog.js +15 -4
  149. data/frontend/js/components/form.js +9 -0
  150. data/frontend/js/components/navigation.js +63 -0
  151. data/frontend/js/components/pagination.js +51 -0
  152. data/frontend/js/components/sidebar.js +8 -11
  153. data/frontend/js/components/table.js +33 -2
  154. data/frontend/js/components/tabs.js +3 -3
  155. data/frontend/js/core/events.js +7 -3
  156. data/frontend/js/index.js +1 -0
  157. data/frontend/theme/trestle/theme/_defaults.scss +4 -4
  158. data/frontend/theme/trestle/theme/bootstrap/_buttons.scss +3 -0
  159. data/lib/generators/trestle/install/install_generator.rb +6 -2
  160. data/lib/generators/trestle/install/templates/_custom.css +8 -0
  161. data/lib/generators/trestle/install/templates/_theme.scss +1 -1
  162. data/lib/generators/trestle/install/templates/trestle.rb.erb +8 -2
  163. data/lib/generators/trestle/resource/resource_generator.rb +15 -1
  164. data/lib/generators/trestle/resource/templates/admin.rb.erb +15 -8
  165. data/lib/trestle/adapters/active_record_adapter.rb +17 -1
  166. data/lib/trestle/adapters.rb +1 -1
  167. data/lib/trestle/admin/builder.rb +7 -3
  168. data/lib/trestle/admin.rb +18 -8
  169. data/lib/trestle/configurable.rb +16 -0
  170. data/lib/trestle/configuration.rb +7 -8
  171. data/lib/trestle/debug_errors.rb +24 -0
  172. data/lib/trestle/engine.rb +3 -10
  173. data/lib/trestle/evaluation_context.rb +11 -3
  174. data/lib/trestle/form/automatic.rb +4 -2
  175. data/lib/trestle/form/builder.rb +6 -7
  176. data/lib/trestle/form/field.rb +44 -17
  177. data/lib/trestle/form/fields/check_box_helpers.rb +1 -1
  178. data/lib/trestle/form/fields/collection_select.rb +1 -1
  179. data/lib/trestle/form/fields/date_picker.rb +9 -3
  180. data/lib/trestle/form/fields/date_select.rb +1 -1
  181. data/lib/trestle/form/fields/datetime_select.rb +1 -1
  182. data/lib/trestle/form/fields/file_field.rb +1 -1
  183. data/lib/trestle/form/fields/form_group.rb +18 -5
  184. data/lib/trestle/form/fields/grouped_collection_select.rb +1 -1
  185. data/lib/trestle/form/fields/range_field.rb +2 -4
  186. data/lib/trestle/form/fields/select.rb +2 -3
  187. data/lib/trestle/form/fields/static_field.rb +7 -3
  188. data/lib/trestle/form/fields/tag_select.rb +3 -1
  189. data/lib/trestle/form/fields/time_select.rb +1 -1
  190. data/lib/trestle/form/fields/time_zone_select.rb +1 -1
  191. data/lib/trestle/form/fields.rb +6 -38
  192. data/lib/trestle/form/renderer.rb +11 -4
  193. data/lib/trestle/form.rb +5 -7
  194. data/lib/trestle/hook/helpers.rb +33 -0
  195. data/lib/trestle/hook/set.rb +32 -0
  196. data/lib/trestle/hook.rb +5 -31
  197. data/lib/trestle/lazy.rb +56 -0
  198. data/lib/trestle/navigation/group.rb +5 -2
  199. data/lib/trestle/navigation/item.rb +7 -3
  200. data/lib/trestle/navigation.rb +3 -6
  201. data/lib/trestle/registry.rb +61 -0
  202. data/lib/trestle/reloader.rb +24 -12
  203. data/lib/trestle/resource/builder.rb +4 -3
  204. data/lib/trestle/resource/collection.rb +1 -5
  205. data/lib/trestle/resource/toolbar.rb +42 -0
  206. data/lib/trestle/resource.rb +8 -9
  207. data/lib/trestle/scopes/block.rb +9 -8
  208. data/lib/trestle/scopes/definition.rb +22 -0
  209. data/lib/trestle/scopes/scope.rb +6 -2
  210. data/lib/trestle/scopes.rb +38 -11
  211. data/lib/trestle/tab.rb +2 -0
  212. data/lib/trestle/table/actions_column.rb +6 -6
  213. data/lib/trestle/table/automatic.rb +10 -3
  214. data/lib/trestle/table/builder.rb +4 -4
  215. data/lib/trestle/table/column.rb +34 -25
  216. data/lib/trestle/table/row.rb +13 -13
  217. data/lib/trestle/table/select_column.rb +19 -7
  218. data/lib/trestle/table.rb +17 -12
  219. data/lib/trestle/toolbar.rb +4 -11
  220. data/lib/trestle/version.rb +1 -1
  221. data/lib/trestle.rb +62 -48
  222. data/package.json +21 -24
  223. data/trestle.gemspec +6 -6
  224. data/webpack.config.js +40 -49
  225. data/yarn.lock +3044 -4730
  226. metadata +66 -22
  227. data/.travis.yml +0 -27
  228. data/lib/trestle/admin/controller.rb +0 -28
  229. data/lib/trestle/application_controller.rb +0 -12
  230. data/lib/trestle/resource/controller.rb +0 -174
  231. /data/{lib → app/controllers/concerns}/trestle/controller/callbacks.rb +0 -0
  232. /data/{lib → app/controllers/concerns}/trestle/controller/dialog.rb +0 -0
  233. /data/{lib → app/controllers/concerns}/trestle/controller/helpers.rb +0 -0
  234. /data/{lib → app/controllers/concerns}/trestle/controller/layout.rb +0 -0
  235. /data/{lib → app/controllers/concerns}/trestle/controller/location.rb +0 -0
@@ -0,0 +1,51 @@
1
+ /* global history */
2
+
3
+ import $ from 'jquery'
4
+
5
+ import { triggerInit } from '../core/events'
6
+
7
+ const REMOTE_PAGINATION_SELECTOR = '.page-link[data-remote]'
8
+
9
+ $(document)
10
+ .on('ajax:send', function (e) {
11
+ const $context = $(e.target).closest('[data-context]')
12
+ const $table = $context.find('.table-container')
13
+ $table.addClass('loading')
14
+ })
15
+
16
+ .on('ajax:complete', function (e) {
17
+ const $context = $(e.target).closest('[data-context]')
18
+ const $table = $context.find('.table-container')
19
+ $table.removeClass('loading')
20
+ })
21
+
22
+ .on('ajax:success', REMOTE_PAGINATION_SELECTOR, function (e) {
23
+ const $link = $(e.target)
24
+ const $context = $link.closest('[data-context]')
25
+
26
+ // Update table and pagination
27
+ const $doc = $(e.detail[0])
28
+
29
+ const $table = $doc.find('.table-container')
30
+ $context.find('.table-container').replaceWith($table)
31
+
32
+ triggerInit($table)
33
+
34
+ const $pagination = $doc.find('.pagination-container')
35
+ $context.find('.pagination-container').replaceWith($pagination)
36
+
37
+ // Update the URL in the browser and context
38
+ const location = $link.attr('href')
39
+
40
+ if (location) {
41
+ // TODO: Implement URL updating with back button support
42
+ // if ($context.hasClass('app-main')) {
43
+ // history.pushState({}, '', location)
44
+ // }
45
+
46
+ $context.data('context', location)
47
+ }
48
+
49
+ // Scroll to top of table
50
+ $table[0].scrollIntoView(true)
51
+ })
@@ -3,6 +3,8 @@ import $ from 'jquery'
3
3
  import { ready } from '../core/events'
4
4
  import cookie from '../core/cookie'
5
5
 
6
+ import Navigation from './navigation'
7
+
6
8
  ready(function () {
7
9
  const $body = $('body')
8
10
  const $wrapper = $('.app-wrapper')
@@ -24,10 +26,10 @@ ready(function () {
24
26
  // Interacting outside of the sidebar closes the navigation
25
27
 
26
28
  $wrapper.on('click touchstart', function (e) {
27
- let navExpanded = $('body').hasClass('mobile-nav-expanded')
29
+ const navExpanded = $('body').hasClass('mobile-nav-expanded')
28
30
 
29
- let clickInHeader = $(e.target).closest('.app-header').length
30
- let clickInSidebar = $(e.target).closest('.app-sidebar').length
31
+ const clickInHeader = $(e.target).closest('.app-header').length
32
+ const clickInSidebar = $(e.target).closest('.app-sidebar').length
31
33
 
32
34
  if (navExpanded && !clickInHeader && !clickInSidebar) {
33
35
  e.stopPropagation()
@@ -60,18 +62,13 @@ ready(function () {
60
62
  $sidebar.find('.nav-header a').on('click', function (e) {
61
63
  e.preventDefault()
62
64
 
63
- $(this).closest('ul').toggleClass('collapsed')
64
-
65
- let collapsed = $sidebar.find('.collapsed .nav-header a').map(function () {
66
- return $(this).attr('href').replace(/^#/, '')
67
- }).toArray()
68
-
69
- cookie.set('trestle:navigation:collapsed', collapsed.join(','))
65
+ const list = $(this).closest('ul')
66
+ Navigation.toggle(list)
70
67
  })
71
68
 
72
69
  // Scroll sidebar to active item
73
70
 
74
- let $active = $sidebar.find('.active')
71
+ const $active = $sidebar.find('.active')
75
72
  if ($active.length) {
76
73
  $sidebar.find('.app-sidebar-inner').scrollTop($active.offset().top - 100)
77
74
  }
@@ -5,7 +5,7 @@ import visit from '../core/visit'
5
5
  // Allow clicking on any part of a table row to follow either the table's data-url
6
6
  // or the first link within the row (that is not in the actions column).
7
7
  $(document).on('click', 'tr[data-url]:not([data-behavior="dialog"])', function (e) {
8
- let row = $(e.currentTarget)
8
+ const row = $(e.currentTarget)
9
9
 
10
10
  let url
11
11
 
@@ -25,6 +25,37 @@ $(document).on('click', 'tr[data-url]:not([data-behavior="dialog"])', function (
25
25
  })
26
26
 
27
27
  // Ignore the above event handler when clicking directly on a link or input element
28
- $(document).on('click', 'tr[data-url] a, tr[data-url] input', function (e) {
28
+ $(document).on('click', 'tr[data-url] a, tr[data-url] input, .select-row', function (e) {
29
29
  e.stopPropagation()
30
30
  })
31
+
32
+ // Handle clicking on select all checkbox in table header
33
+ $(document).on('click', 'th.select-row input', function (e) {
34
+ const table = $(this).closest('table')
35
+ const checked = $(this).is(':checked')
36
+
37
+ table.find('td.select-row input').prop('checked', checked)
38
+ })
39
+
40
+ // Handle single row selection to update header row status
41
+ $(document).on('click', 'td.select-row input', function (e) {
42
+ const table = $(this).closest('table')
43
+
44
+ const checkboxes = table.find('td.select-row input')
45
+ const selectedCheckboxes = checkboxes.filter(':checked')
46
+
47
+ const header = table.find('th.select-row input')
48
+
49
+ if (checkboxes.length === selectedCheckboxes.length) {
50
+ // All checked
51
+ header.prop('indeterminate', false)
52
+ header.prop('checked', true)
53
+ } else if (selectedCheckboxes.length === 0) {
54
+ // None checked
55
+ header.prop('indeterminate', false)
56
+ header.prop('checked', false)
57
+ } else {
58
+ // Some checked
59
+ header.prop('indeterminate', true)
60
+ }
61
+ })
@@ -2,11 +2,11 @@
2
2
 
3
3
  import $ from 'jquery'
4
4
 
5
- import { init, ready } from '../core/events'
5
+ import { init } from '../core/events'
6
6
 
7
7
  const TAB_SELECTOR = 'a[data-toggle="tab"]'
8
8
  const TAB_PANE_SELECTOR = '.tab-pane'
9
- const ERROR_SELECTOR = '.is-invalid:not([type="hidden"])'
9
+ const ERROR_SELECTOR = '.has-error'
10
10
 
11
11
  // Save active tab to URL hash
12
12
  init(function (root) {
@@ -21,7 +21,7 @@ init(function (root) {
21
21
  })
22
22
 
23
23
  // Restore active tab when loading
24
- ready(function () {
24
+ init(function () {
25
25
  focusActiveTab()
26
26
  })
27
27
 
@@ -1,5 +1,7 @@
1
1
  import $ from 'jquery'
2
2
 
3
+ import turbolinks from './turbolinks'
4
+
3
5
  // The ready function sets up a callback to run on each page load.
4
6
  //
5
7
  // Trestle.ready(function() {
@@ -41,6 +43,8 @@ ready(function () {
41
43
  })
42
44
 
43
45
  // Trigger the page load events.
44
- $(document).on('ready turbolinks:load', function () {
45
- triggerReady()
46
- })
46
+ if (turbolinks) {
47
+ $(document).on('turbolinks:load', triggerReady)
48
+ } else {
49
+ $(document).ready(triggerReady)
50
+ }
data/frontend/js/index.js CHANGED
@@ -29,6 +29,7 @@ import Dialog from './components/dialog'
29
29
  import './components/file'
30
30
  import './components/form'
31
31
  import './components/gallery'
32
+ import './components/pagination'
32
33
  import './components/select'
33
34
  import './components/sidebar'
34
35
  import './components/table'
@@ -8,7 +8,7 @@ $theme-colors: (
8
8
  $theme-color-interval: 8% !default;
9
9
 
10
10
  // The yiq lightness value that determines when the lightness of color changes from "dark" to "light". Acceptable values are between 0 and 255.
11
- $yiq-contrasted-threshold: 150 !default;
11
+ $yiq-contrasted-threshold: 175 !default;
12
12
 
13
13
  // Customize the light and dark text colors for use in our YIQ color contrast function.
14
14
  $yiq-text-dark: #212529 !default;
@@ -115,9 +115,9 @@ $badge-focus-width: $input-btn-focus-width !default;
115
115
 
116
116
  // Alerts
117
117
 
118
- $alert-bg-level: -10 !default;
119
- $alert-border-level: -9 !default;
120
- $alert-color-level: 6 !default;
118
+ $alert-bg-level: 2 !default;
119
+ $alert-border-level: 3 !default;
120
+ $alert-color-level: -12 !default;
121
121
 
122
122
 
123
123
  // Progress bars
@@ -11,6 +11,9 @@
11
11
 
12
12
  &:focus,
13
13
  &.focus {
14
+ color: color-yiq($hover-background);
15
+ background-color: $hover-background;
16
+ border-color: $hover-border;
14
17
  box-shadow: 0 0 0 $btn-focus-width rgba(mix(color-yiq($background), $border, 15%), .5);
15
18
  }
16
19
 
@@ -10,8 +10,12 @@ module Trestle
10
10
  end
11
11
 
12
12
  def create_assets
13
- template "_theme.scss", "app/assets/stylesheets/trestle/_theme.scss"
14
- template "_custom.scss", "app/assets/stylesheets/trestle/_custom.scss"
13
+ if defined?(Sass) || defined?(SassC)
14
+ template "_custom.scss", "app/assets/stylesheets/trestle/_custom.scss"
15
+ template "_theme.scss", "app/assets/stylesheets/trestle/_theme.scss"
16
+ else
17
+ template "_custom.css", "app/assets/stylesheets/trestle/_custom.css"
18
+ end
15
19
 
16
20
  template "custom.js", "app/assets/javascripts/trestle/custom.js"
17
21
  end
@@ -0,0 +1,8 @@
1
+ /* This file may be used for providing additional customizations to the Trestle
2
+ * admin. It will be automatically included within all admin pages.
3
+ *
4
+ * For organizational purposes, you may wish to define your customizations
5
+ * within individual partials in this folder and they'll be required below.
6
+ *
7
+ *= require_tree .
8
+ */
@@ -9,5 +9,5 @@
9
9
  //
10
10
  // $theme-colors: (
11
11
  // "primary": #337ab7,
12
- // "secondary": #b3b53d
12
+ // "secondary": #719dc3
13
13
  // );
@@ -15,7 +15,7 @@ Trestle.configure do |config|
15
15
  #
16
16
  # config.site_logo_small = "logo-small.png"
17
17
 
18
- # Speficy a favicon to be used within the admin.
18
+ # Specify a favicon to be used within the admin.
19
19
  #
20
20
  # config.favicon = "favicon.ico"
21
21
 
@@ -70,6 +70,7 @@ Trestle.configure do |config|
70
70
  # Specify helper modules to expose to the admin.
71
71
  #
72
72
  # config.helper :all
73
+ # config.helper -> { CustomHelper }
73
74
 
74
75
  # Register callbacks to run before, after or around all Trestle actions.
75
76
  #
@@ -103,6 +104,11 @@ Trestle.configure do |config|
103
104
  #
104
105
  # config.persistent_params << :query
105
106
 
107
+ # List of methods to try calling on an instance when displayed by the `display` helper.
108
+ # Defaults to [:display_name, :full_name, :name, :title, :username, :login, :email].
109
+ #
110
+ # config.display_methods.unshift(:admin_label)
111
+
106
112
  # Customize the default adapter class used by all admin resources.
107
113
  # See the documentation on Trestle::Adapters::Adapter for details on
108
114
  # the adapter methods that can be customized.
@@ -118,7 +124,7 @@ Trestle.configure do |config|
118
124
  # def render; end
119
125
  # end
120
126
  #
121
- # config.form_field :custom, CustomFormField
127
+ # config.form_field :custom, -> { CustomFormField }
122
128
 
123
129
  # == Debugging Options
124
130
  #
@@ -3,10 +3,24 @@ module Trestle
3
3
  class ResourceGenerator < ::Rails::Generators::NamedBase
4
4
  desc "Creates a Trestle admin resource"
5
5
 
6
+ class_option :singular, type: :boolean, default: false, desc: "Generate a singular resource"
7
+
6
8
  source_root File.expand_path("../templates", __FILE__)
7
9
 
8
10
  def create_admin
9
- template "admin.rb.erb", File.join("app/admin", class_path, "#{plural_name}_admin.rb")
11
+ template "admin.rb.erb", File.join("app/admin", class_path, "#{admin_name}_admin.rb")
12
+ end
13
+
14
+ def admin_name
15
+ singular? ? singular_name : plural_name
16
+ end
17
+
18
+ def parameter_name
19
+ singular_name.singularize
20
+ end
21
+
22
+ def singular?
23
+ options[:singular]
10
24
  end
11
25
 
12
26
  protected
@@ -1,8 +1,14 @@
1
- Trestle.resource(:<%= plural_name %><% if module? %>, scope: <%= module_name %><% end %>) do
1
+ Trestle.resource(:<%= admin_name %><% if module? %>, scope: <%= module_name %><% end %><% if singular? %>, singular: true<% end %>) do
2
2
  menu do
3
- item :<%= plural_name %>, icon: "fa fa-star"
3
+ item :<%= admin_name %>, icon: "fa fa-star"
4
4
  end
5
5
 
6
+ <%- if singular? -%>
7
+ instance do
8
+ # Override this block to define the instance that this resource represents.
9
+ raise NotImplementedError, "Singular resources must define an instance block."
10
+ end
11
+ <%- else -%>
6
12
  # Customize the table columns shown on the index view.
7
13
  #
8
14
  # table do
@@ -10,19 +16,20 @@ Trestle.resource(:<%= plural_name %><% if module? %>, scope: <%= module_name %><
10
16
  # column :created_at, align: :center
11
17
  # actions
12
18
  # end
19
+ <%- end -%>
13
20
 
14
- # Customize the form fields shown on the new/edit views.
21
+ # Customize the form fields shown on the <% if singular? %>edit view<% else %>new/edit views<% end %>.
15
22
  #
16
- # form do |<%= singular_table_name %>|
23
+ # form do |<%= parameter_name %>|
17
24
  # text_field :name
18
25
  #
19
26
  # row do
20
- # col(xs: 6) { datetime_field :updated_at }
21
- # col(xs: 6) { datetime_field :created_at }
27
+ # col { datetime_field :updated_at }
28
+ # col { datetime_field :created_at }
22
29
  # end
23
30
  # end
24
31
 
25
- # By default, all parameters passed to the update and create actions will be
32
+ # By default, all parameters passed to the <% if singular? %>update action<% else %>update and create actions<% end %> will be
26
33
  # permitted. If you do not have full trust in your users, you should explicitly
27
34
  # define the list of permitted parameters.
28
35
  #
@@ -30,6 +37,6 @@ Trestle.resource(:<%= plural_name %><% if module? %>, scope: <%= module_name %><
30
37
  # http://guides.rubyonrails.org/action_controller_overview.html#strong-parameters
31
38
  #
32
39
  # params do |params|
33
- # params.require(:<%= singular_table_name %>).permit(:name, ...)
40
+ # params.require(:<%= parameter_name %>).permit(:name, ...)
34
41
  # end
35
42
  end
@@ -30,7 +30,7 @@ module Trestle
30
30
  end
31
31
 
32
32
  def count(collection)
33
- collection.count
33
+ collection.count(:all)
34
34
  end
35
35
 
36
36
  def sort(collection, field, order)
@@ -60,6 +60,8 @@ module Trestle
60
60
  Attribute::Association.new(column.name, class: -> { reflection.klass }, name: name, polymorphic: reflection.polymorphic?, type_column: reflection.foreign_type)
61
61
  elsif column.name.end_with?("_type") && (name = column.name.sub(/_type$/, '')) && (reflection = model.reflections[name])
62
62
  # Ignore type columns for polymorphic associations
63
+ elsif enum_column?(column)
64
+ Attribute.new(column.name, :enum, values: enum_values(column))
63
65
  else
64
66
  Attribute.new(column.name, column.type, array_column?(column) ? { array: true } : {})
65
67
  end
@@ -81,6 +83,20 @@ module Trestle
81
83
  def array_column?(column)
82
84
  column.respond_to?(:array?) && column.array?
83
85
  end
86
+
87
+ def enum_column?(column)
88
+ model.defined_enums.key?(column.name)
89
+ end
90
+
91
+ def enum_values(column)
92
+ model.defined_enums[column.name].map { |key, value|
93
+ [value, enum_human_name(column, key)]
94
+ }
95
+ end
96
+
97
+ def enum_human_name(column, value)
98
+ human_attribute_name([column.name.pluralize, value].join("."), default: value.humanize)
99
+ end
84
100
  end
85
101
  end
86
102
  end
@@ -2,7 +2,7 @@ module Trestle
2
2
  module Adapters
3
3
  extend ActiveSupport::Autoload
4
4
 
5
- autoload :Adapter
5
+ require_relative "adapters/adapter"
6
6
 
7
7
  autoload :ActiveRecordAdapter
8
8
  autoload :DraperAdapter
@@ -7,7 +7,7 @@ module Trestle
7
7
  self.admin_class = Admin
8
8
 
9
9
  class_attribute :controller
10
- self.controller = Controller
10
+ self.controller = -> { AdminController }
11
11
 
12
12
  delegate :helper, :before_action, :after_action, :around_action, to: :@controller
13
13
 
@@ -27,7 +27,7 @@ module Trestle
27
27
  # Define admin controller class
28
28
  # This is done using class_eval rather than Class.new so that the full
29
29
  # class name and parent chain is set when Rails' inherited hooks are called.
30
- admin.class_eval("class AdminController < #{controller.name}; end")
30
+ admin.class_eval("class AdminController < #{controller.call.name}; end")
31
31
 
32
32
  # Set a reference on the controller class to the admin class
33
33
  controller = admin.const_get(:AdminController)
@@ -53,7 +53,11 @@ module Trestle
53
53
  end
54
54
 
55
55
  def form(options={}, &block)
56
- admin.form = Form.new(options, &block)
56
+ if block_given?
57
+ admin.form = Form.new(options, &block)
58
+ else
59
+ admin.form = Form::Automatic.new(admin, options)
60
+ end
57
61
  end
58
62
 
59
63
  def hook(name, options={}, &block)
data/lib/trestle/admin.rb CHANGED
@@ -1,9 +1,6 @@
1
1
  module Trestle
2
2
  class Admin
3
- extend ActiveSupport::Autoload
4
-
5
- autoload :Builder
6
- autoload :Controller
3
+ require_relative "admin/builder"
7
4
 
8
5
  delegate :to_param, to: :class
9
6
 
@@ -67,7 +64,10 @@ module Trestle
67
64
  end
68
65
 
69
66
  def default_breadcrumb
70
- Breadcrumb.new(human_admin_name, path)
67
+ deprecated = I18n.t(:"admin.breadcrumbs.#{i18n_key}", default: human_admin_name)
68
+ label = translate("breadcrumbs.index", default: deprecated)
69
+
70
+ Breadcrumb.new(label, path)
71
71
  end
72
72
 
73
73
  def admin_name
@@ -79,7 +79,7 @@ module Trestle
79
79
  end
80
80
 
81
81
  def human_admin_name
82
- I18n.t("admin.breadcrumbs.#{i18n_key}", default: default_human_admin_name)
82
+ translate("name", default: default_human_admin_name)
83
83
  end
84
84
 
85
85
  def default_human_admin_name
@@ -90,12 +90,12 @@ module Trestle
90
90
  defaults = [:"admin.#{i18n_key}.#{key}", :"admin.#{key}"]
91
91
  defaults << options[:default] if options[:default]
92
92
 
93
- I18n.t(defaults.shift, options.merge(default: defaults))
93
+ I18n.t(defaults.shift, **options.merge(default: defaults))
94
94
  end
95
95
  alias t translate
96
96
 
97
97
  def parameter_name
98
- admin_name.singularize
98
+ unscope_path(admin_name.singularize)
99
99
  end
100
100
 
101
101
  def route_name
@@ -165,6 +165,16 @@ module Trestle
165
165
  def validate!
166
166
  # No validations by default. This can be overridden in subclasses.
167
167
  end
168
+
169
+ private
170
+ def unscope_path(path)
171
+ path = path.to_s
172
+ if i = path.rindex("/")
173
+ path[(i + 1)..-1]
174
+ else
175
+ path
176
+ end
177
+ end
168
178
  end
169
179
  end
170
180
  end
@@ -15,6 +15,12 @@ module Trestle
15
15
  options.fetch(name) {
16
16
  if defaults.key?(name)
17
17
  value = defaults[name]
18
+
19
+ # Avoid references to the same instance of a default value
20
+ if value.respond_to?(:dup) && !value.is_a?(Proc)
21
+ value = value.dup
22
+ end
23
+
18
24
  assign(name, value)
19
25
  end
20
26
  }
@@ -62,6 +68,16 @@ module Trestle
62
68
 
63
69
  defaults[name] = default
64
70
  end
71
+
72
+ def deprecated_option(name, message=nil)
73
+ define_method("#{name}=") do |value|
74
+ ActiveSupport::Deprecation.warn(message)
75
+ end
76
+
77
+ define_method(name) do |*args|
78
+ ActiveSupport::Deprecation.warn(message)
79
+ end
80
+ end
65
81
  end
66
82
 
67
83
  module Open
@@ -41,7 +41,7 @@ module Trestle
41
41
  option :root, -> { Trestle.config.path }
42
42
 
43
43
  # Initial breadcrumbs to display in the breadcrumb trail
44
- option :root_breadcrumbs, -> { [Trestle::Breadcrumb.new(I18n.t("admin.breadcrumbs.home", default: "Home"), Trestle.config.root)] }
44
+ option :root_breadcrumbs, -> { [Trestle::Breadcrumb.new(I18n.t(:"admin.breadcrumbs.home", default: "Home"), Trestle.config.root)] }
45
45
 
46
46
  # Default icon class to use when it is not explicitly provided
47
47
  option :default_navigation_icon, "fa fa-arrow-circle-o-right"
@@ -54,18 +54,17 @@ module Trestle
54
54
  menus << Navigation::Block.new(&block)
55
55
  end
56
56
 
57
-
58
57
  ## Extension Options
59
58
 
60
59
  # [Internal] List of helper modules to include in all Trestle controllers
61
- option :helpers, []
60
+ option :helpers, Lazy::List.new
62
61
 
63
62
  # [Internal] Container module for block-defined helpers
64
63
  option :helper_module, Module.new
65
64
 
66
65
  # Register global helpers available to all Trestle admins
67
66
  def helper(*helpers, &block)
68
- self.helpers += helpers
67
+ self.helpers << helpers
69
68
  self.helper_module.module_eval(&block) if block_given?
70
69
  end
71
70
 
@@ -82,8 +81,8 @@ module Trestle
82
81
  option :default_adapter, Adapters.compose(Adapters::ActiveRecordAdapter, Adapters::DraperAdapter)
83
82
 
84
83
  # Register a custom form field class
85
- def form_field(name, klass)
86
- Form::Builder.register(name, klass)
84
+ def form_field(name, field)
85
+ Form::Builder.register(name, field)
87
86
  end
88
87
 
89
88
  # [Internal] List of registered hooks
@@ -105,8 +104,8 @@ module Trestle
105
104
  -> { ActiveSupport::Dependencies.autoload_paths.grep(/\/app\/admin\Z/) }
106
105
  ]
107
106
 
108
- # When to reload Trestle admin within a to_prepare block (`:always` or `:on_update`)
109
- option :reload, :on_update
107
+ # [DEPRECATED] When to reload Trestle admin within a to_prepare block
108
+ deprecated_option :reload, "The config.reload option is deprecated. Admins are now always reloaded when config.to_prepare is called."
110
109
 
111
110
 
112
111
  ## Debugging
@@ -0,0 +1,24 @@
1
+ module Trestle
2
+ class DebugErrors
3
+ def initialize(errors)
4
+ @errors = errors
5
+ end
6
+
7
+ def any?
8
+ @errors.any?
9
+ end
10
+
11
+ def each
12
+ if defined?(ActiveModel::Error)
13
+ # Rails 6.1 introduces a unified Error class
14
+ @errors.each do |error|
15
+ yield error.attribute, error.message
16
+ end
17
+ else
18
+ @errors.each do |error, message|
19
+ yield error, message
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -44,16 +44,9 @@ module Trestle
44
44
  Engine.reset_helpers!
45
45
  end
46
46
 
47
- config.to_prepare do
48
- if Trestle.config.reload == :always
49
- Engine.reloader.execute
50
- else
51
- Engine.reloader.execute_if_updated
52
- end
53
- end
54
-
55
- def reloader
56
- @reloader ||= Trestle::Reloader.new
47
+ config.after_initialize do |app|
48
+ reloader = Trestle::Reloader.new(*app.watchable_args)
49
+ reloader.install(app) unless app.config.eager_load
57
50
  end
58
51
 
59
52
  def reset_helpers!