trestle 0.10.0.pre2 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +4 -0
  3. data/Gemfile +2 -2
  4. data/README.md +1 -1
  5. data/app/assets/bundle/trestle/admin.css +9 -9
  6. data/app/assets/bundle/trestle/admin.js +13 -13
  7. data/app/assets/bundle/trestle/fa-brands-400.ttf +0 -0
  8. data/app/assets/bundle/trestle/fa-brands-400.woff2 +0 -0
  9. data/app/assets/bundle/trestle/fa-regular-400.ttf +0 -0
  10. data/app/assets/bundle/trestle/fa-regular-400.woff2 +0 -0
  11. data/app/assets/bundle/trestle/fa-solid-900.ttf +0 -0
  12. data/app/assets/bundle/trestle/fa-solid-900.woff2 +0 -0
  13. data/app/assets/bundle/trestle/photoswipe-2d522a3abaa59f8a8f73.digested.js +6 -0
  14. data/app/assets/sprockets/trestle/icons/font-awesome.css.erb +1 -1
  15. data/app/controllers/concerns/trestle/controller/modal.rb +3 -1
  16. data/app/helpers/trestle/avatar_helper.rb +20 -14
  17. data/app/helpers/trestle/card_helper.rb +27 -9
  18. data/app/helpers/trestle/container_helper.rb +37 -6
  19. data/app/helpers/trestle/display_helper.rb +11 -0
  20. data/app/helpers/trestle/flash_helper.rb +1 -10
  21. data/app/helpers/trestle/form_helper.rb +32 -18
  22. data/app/helpers/trestle/format_helper.rb +47 -17
  23. data/app/helpers/trestle/gravatar_helper.rb +48 -0
  24. data/app/helpers/trestle/grid_helper.rb +12 -14
  25. data/app/helpers/trestle/headings_helper.rb +2 -23
  26. data/app/helpers/trestle/i18n_helper.rb +1 -0
  27. data/app/helpers/trestle/icon_helper.rb +16 -3
  28. data/app/helpers/trestle/layout_helper.rb +1 -0
  29. data/app/helpers/trestle/modal_helper.rb +21 -2
  30. data/app/helpers/trestle/navigation_helper.rb +1 -0
  31. data/app/helpers/trestle/pagination_helper.rb +1 -0
  32. data/app/helpers/trestle/params_helper.rb +32 -0
  33. data/app/helpers/trestle/sort_helper.rb +38 -7
  34. data/app/helpers/trestle/status_helper.rb +19 -3
  35. data/app/helpers/trestle/tab_helper.rb +42 -7
  36. data/app/helpers/trestle/table_helper.rb +23 -23
  37. data/app/helpers/trestle/timestamp_helper.rb +18 -25
  38. data/app/helpers/trestle/title_helper.rb +2 -0
  39. data/app/helpers/trestle/toolbars_helper.rb +2 -1
  40. data/app/helpers/trestle/turbo/frame_helper.rb +25 -14
  41. data/app/helpers/trestle/url_helper.rb +124 -54
  42. data/app/views/kaminari/trestle/_first_page.html.erb +1 -2
  43. data/app/views/kaminari/trestle/_gap.html.erb +0 -1
  44. data/app/views/kaminari/trestle/_last_page.html.erb +1 -2
  45. data/app/views/kaminari/trestle/_page.html.erb +1 -2
  46. data/app/views/kaminari/trestle/_paginator.html.erb +0 -1
  47. data/app/views/layouts/trestle/admin.html.erb +3 -3
  48. data/app/views/layouts/trestle/modal.html.erb +2 -2
  49. data/app/views/trestle/application/_layout.html.erb +22 -18
  50. data/app/views/trestle/application/_tabs.html.erb +1 -1
  51. data/app/views/trestle/flash/_alert.html.erb +3 -1
  52. data/app/views/trestle/flash/_flash.html.erb +7 -4
  53. data/app/views/trestle/resource/_scopes.html.erb +3 -3
  54. data/app/views/trestle/resource/create.turbo_stream.erb +1 -0
  55. data/app/views/trestle/resource/destroy.turbo_stream.erb +2 -0
  56. data/app/views/trestle/resource/index.html.erb +10 -12
  57. data/app/views/trestle/resource/update.turbo_stream.erb +1 -0
  58. data/app/views/trestle/shared/_sidebar.html.erb +8 -8
  59. data/app/views/trestle/table/_table.html.erb +2 -2
  60. data/config/locales/pt-BR.yml +13 -13
  61. data/frontend/css/components/_scopes.scss +6 -7
  62. data/frontend/css/core/_theme.scss +3 -3
  63. data/frontend/css/layout/_sidebar.scss +9 -5
  64. data/frontend/css/variables/_trestle.scss +3 -0
  65. data/frontend/js/controllers/confirm_delete_controller.js +4 -4
  66. data/frontend/js/controllers/flatpickr_controller.js +2 -2
  67. data/frontend/js/controllers/gallery_controller.js +2 -0
  68. data/frontend/js/controllers/lightbox_controller.js +3 -5
  69. data/frontend/js/controllers/modal_trigger_controller.js +2 -2
  70. data/frontend/js/controllers/sidebar_controller.js +13 -3
  71. data/frontend/js/controllers/tab_errors_controller.js +2 -2
  72. data/frontend/js/core/backdrop.js +30 -28
  73. data/frontend/js/core/error_modal.js +7 -9
  74. data/frontend/js/core/fetch.js +2 -0
  75. data/lib/trestle/admin.rb +9 -2
  76. data/lib/trestle/configuration.rb +3 -0
  77. data/lib/trestle/engine.rb +1 -1
  78. data/lib/trestle/evaluation_context.rb +2 -4
  79. data/lib/trestle/form/automatic.rb +1 -1
  80. data/lib/trestle/form/field.rb +1 -1
  81. data/lib/trestle/form/fields/check_box.rb +1 -1
  82. data/lib/trestle/form/fields/collection_check_boxes.rb +1 -1
  83. data/lib/trestle/form/fields/collection_radio_buttons.rb +1 -1
  84. data/lib/trestle/form/fields/date_select.rb +1 -1
  85. data/lib/trestle/form/fields/datetime_select.rb +1 -1
  86. data/lib/trestle/form/fields/form_control.rb +2 -2
  87. data/lib/trestle/form/fields/form_group.rb +4 -4
  88. data/lib/trestle/form/fields/radio_button.rb +1 -1
  89. data/lib/trestle/form/fields/static_field.rb +1 -1
  90. data/lib/trestle/form/fields/time_select.rb +1 -1
  91. data/lib/trestle/form/renderer.rb +3 -5
  92. data/lib/trestle/hook/helpers.rb +21 -0
  93. data/lib/trestle/navigation/block.rb +8 -15
  94. data/lib/trestle/navigation/group.rb +2 -2
  95. data/lib/trestle/navigation/item.rb +21 -4
  96. data/lib/trestle/registry.rb +14 -11
  97. data/lib/trestle/resource/builder.rb +9 -6
  98. data/lib/trestle/resource/toolbar.rb +5 -5
  99. data/lib/trestle/resource.rb +7 -5
  100. data/lib/trestle/scopes/block.rb +8 -12
  101. data/lib/trestle/scopes/definition.rb +6 -2
  102. data/lib/trestle/scopes/scope.rb +13 -10
  103. data/lib/trestle/tab.rb +2 -2
  104. data/lib/trestle/table/column.rb +4 -3
  105. data/lib/trestle/table/row.rb +1 -1
  106. data/lib/trestle/toolbar/builder.rb +6 -6
  107. data/lib/trestle/toolbar/context.rb +2 -4
  108. data/lib/trestle/toolbar/item.rb +10 -19
  109. data/lib/trestle/toolbar/menu.rb +9 -9
  110. data/lib/trestle/version.rb +1 -1
  111. data/lib/trestle.rb +2 -2
  112. data/package.json +7 -7
  113. data/trestle.gemspec +1 -1
  114. data/yarn.lock +751 -796
  115. metadata +7 -7
  116. data/app/assets/bundle/trestle/photoswipe-7aa1aec9c3c1fd65c382.digested.js +0 -6
  117. data/app/views/layouts/trestle/admin.turbo_stream.erb +0 -4
@@ -3,6 +3,8 @@
3
3
  .app-wrapper {
4
4
  --sidebar-width: #{$sidebar-width};
5
5
  --sidebar-width-collapsed: #{$sidebar-width-collapsed};
6
+ --sidebar-padding: #{$sidebar-padding};
7
+ --sidebar-padding-collapsed: #{$sidebar-padding-collapsed};
6
8
  --sidebar-transition-duration: #{$sidebar-transition-duration};
7
9
  --sidebar-transition-timing: ease-out;
8
10
  --sidebar-bg: #{$sidebar-bg};
@@ -69,7 +71,7 @@
69
71
  display: flex;
70
72
  align-items: center;
71
73
 
72
- padding: 0.5rem 0.875rem;
74
+ padding: var(--sidebar-padding);
73
75
 
74
76
  font-size: 1.25rem;
75
77
  font-weight: 500;
@@ -100,6 +102,8 @@
100
102
  display: flex;
101
103
  flex-direction: column;
102
104
 
105
+ position: relative;
106
+
103
107
  overflow-x: hidden;
104
108
  overflow-y: auto;
105
109
 
@@ -176,7 +180,7 @@
176
180
 
177
181
  .app-sidebar-title {
178
182
  justify-content: center;
179
- padding: 0.5rem 0.25rem;
183
+ padding: var(--sidebar-padding-collapsed);
180
184
 
181
185
  // Match right margin with navbar toggler width:
182
186
  // (margin + border + font-size * icon-width + padding)
@@ -200,7 +204,7 @@
200
204
  width: var(--sidebar-width-collapsed);
201
205
  flex-basis: var(--sidebar-width-collapsed);
202
206
 
203
- padding: 0.5rem 0.25rem;
207
+ padding: var(--sidebar-padding-collapsed);
204
208
  justify-content: center;
205
209
 
206
210
  img {
@@ -231,7 +235,7 @@
231
235
  width: var(--sidebar-width);
232
236
  flex-basis: var(--sidebar-width);
233
237
 
234
- padding: 0.5rem 0.25rem;
238
+ padding: var(--sidebar-padding);
235
239
  justify-content: flex-start;
236
240
 
237
241
  img {
@@ -267,7 +271,7 @@
267
271
  width: var(--sidebar-width-collapsed);
268
272
  flex-basis: var(--sidebar-width-collapsed);
269
273
 
270
- padding: 0.5rem 0.25rem;
274
+ padding: var(--sidebar-padding-collapsed);
271
275
  justify-content: center;
272
276
 
273
277
  img {
@@ -61,6 +61,9 @@ $sidebar-mobile-toggle-active-border: $sidebar-mobile-toggle-border-width
61
61
  $sidebar-width: 15.625rem;
62
62
  $sidebar-width-collapsed: 3.5rem;
63
63
 
64
+ $sidebar-padding: 0.5rem 0.875rem;
65
+ $sidebar-padding-collapsed: 0.5rem 0.25rem;
66
+
64
67
  $sidebar-transition-duration: 200ms;
65
68
 
66
69
 
@@ -3,9 +3,9 @@ import ConfirmController from './confirm_controller'
3
3
  import { i18n } from '../core/i18n'
4
4
 
5
5
  export default class extends ConfirmController {
6
- static values = {
7
- confirmLabel: i18n['trestle.confirmation.delete'] || 'Delete'
8
- }
9
-
10
6
  static defaultConfirmClass = 'btn-danger'
7
+
8
+ get confirmLabel () {
9
+ return this.confirmLabelValue || i18n.t('trestle.confirmation.delete', { defaultValue: 'Delete' })
10
+ }
11
11
  }
@@ -23,7 +23,7 @@ export default class extends ApplicationController {
23
23
  }
24
24
 
25
25
  setup (selectedDates, dateStr, instance) {
26
- this._createClearButton(instance)
26
+ this.#createClearButton(instance)
27
27
  }
28
28
 
29
29
  get options () {
@@ -33,7 +33,7 @@ export default class extends ApplicationController {
33
33
  }
34
34
  }
35
35
 
36
- _createClearButton (instance) {
36
+ #createClearButton (instance) {
37
37
  if (this.element.dataset.allowClear) {
38
38
  const clearButton = document.createElement('button')
39
39
 
@@ -1,6 +1,8 @@
1
1
  import LightboxController from './lightbox_controller'
2
2
 
3
3
  export default class extends LightboxController {
4
+ static targets = ["image"]
5
+
4
6
  get options () {
5
7
  return {
6
8
  ...super.options,
@@ -26,8 +26,6 @@ const VideoIframeUrl = {
26
26
  }
27
27
 
28
28
  export default class extends ApplicationController {
29
- static targets = ["image"]
30
-
31
29
  static values = {
32
30
  animationType: { type: String, default: 'zoom' },
33
31
  animationDuration: { type: Number, default: 150 },
@@ -111,7 +109,7 @@ export default class extends ApplicationController {
111
109
  itemData.type = 'html'
112
110
  itemData.html = `<video controls><source src="${itemData.src}"></video>`
113
111
 
114
- this.setDefaultVideoDimensions(itemData)
112
+ this.#setDefaultVideoDimensions(itemData)
115
113
  }
116
114
 
117
115
  return itemData
@@ -128,7 +126,7 @@ export default class extends ApplicationController {
128
126
  itemData.type = 'html'
129
127
  itemData.html = `<iframe src="${src}" allowfullscreen></iframe>`
130
128
 
131
- this.setDefaultVideoDimensions(itemData)
129
+ this.#setDefaultVideoDimensions(itemData)
132
130
  }
133
131
  }
134
132
 
@@ -153,7 +151,7 @@ export default class extends ApplicationController {
153
151
  }
154
152
  }
155
153
 
156
- setDefaultVideoDimensions(itemData) {
154
+ #setDefaultVideoDimensions(itemData) {
157
155
  itemData.w ||= this.defaultVideoWidthValue
158
156
  itemData.h ||= this.defaultVideoHeightValue
159
157
  }
@@ -26,7 +26,7 @@ export default class extends ApplicationController {
26
26
  .then((modal) => {
27
27
  this.modal = modal
28
28
 
29
- const modalController = this._getModalController(modal)
29
+ const modalController = this.#getModalController(modal)
30
30
  modalController.modalTrigger = this
31
31
 
32
32
  this.dispatch('loaded', { detail: modal })
@@ -68,7 +68,7 @@ export default class extends ApplicationController {
68
68
  return this.element.nodeName === 'A'
69
69
  }
70
70
 
71
- _getModalController (modal) {
71
+ #getModalController (modal) {
72
72
  return this.application.getControllerForElementAndIdentifier(modal, 'modal')
73
73
  }
74
74
  }
@@ -5,6 +5,10 @@ import cookie from '../core/cookie'
5
5
  export default class extends ApplicationController {
6
6
  static targets = ["inner"]
7
7
 
8
+ static values = {
9
+ scrollMargin: { type: Number, default: 100 }
10
+ }
11
+
8
12
  connect () {
9
13
  this.scrollToActive()
10
14
  }
@@ -25,9 +29,15 @@ export default class extends ApplicationController {
25
29
  }
26
30
 
27
31
  scrollToActive () {
28
- const active = this.element.getElementsByClassName('active')[0]
29
- if (active && this.hasInnerTarget) {
30
- this.innerTarget.scrollTop = active.offsetTop - 100
32
+ if (!this.hasInnerTarget) return
33
+
34
+ const active = this.element.querySelector('.active')
35
+ if (!active) return
36
+
37
+ // Check if bottom of active element is outside of visible navigation height (plus scroll margin)
38
+ const activeOffset = active.offsetTop + active.offsetHeight + this.scrollMarginValue
39
+ if (activeOffset > this.innerTarget.clientHeight) {
40
+ this.innerTarget.scrollTop = activeOffset - this.innerTarget.clientHeight
31
41
  }
32
42
  }
33
43
  }
@@ -20,7 +20,7 @@ export default class extends ApplicationController {
20
20
  const errorCount = pane.querySelectorAll(this.errorSelectorValue).length
21
21
 
22
22
  if (errorCount > 0) {
23
- const badge = this._createErrorBadge(errorCount)
23
+ const badge = this.#createErrorBadge(errorCount)
24
24
  link.appendChild(badge)
25
25
  }
26
26
  }
@@ -34,7 +34,7 @@ export default class extends ApplicationController {
34
34
  })
35
35
  }
36
36
 
37
- _createErrorBadge (count) {
37
+ #createErrorBadge (count) {
38
38
  const badge = document.createElement('span')
39
39
 
40
40
  badge.classList.add('badge', 'badge-danger', 'badge-pill')
@@ -12,6 +12,10 @@ const CLASS_NAME_SHOW = 'show'
12
12
  const CLASS_NAME_LOADING = 'loading'
13
13
 
14
14
  export default class Backdrop {
15
+ #config
16
+ #element
17
+ #isAppended
18
+
15
19
  static getInstance () {
16
20
  if (!this.instance) {
17
21
  this.instance = new Backdrop()
@@ -21,31 +25,31 @@ export default class Backdrop {
21
25
  }
22
26
 
23
27
  constructor () {
24
- this._config = Default
25
- this._element = null
26
- this._isAppended = false
28
+ this.#config = Default
29
+ this.#element = null
30
+ this.#isAppended = false
27
31
  }
28
32
 
29
33
  show (callback) {
30
- this._append()
34
+ this.#append()
31
35
 
32
- if (this._config.isAnimated) {
33
- reflow(this._getElement())
36
+ if (this.#config.isAnimated) {
37
+ reflow(this.#getElement())
34
38
  }
35
39
 
36
- this._getElement().classList.add(CLASS_NAME_SHOW)
40
+ this.#getElement().classList.add(CLASS_NAME_SHOW)
37
41
 
38
- this._emulateAnimation(() => {
42
+ this.#emulateAnimation(() => {
39
43
  execute(callback)
40
44
  })
41
45
  }
42
46
 
43
47
  hide (callback) {
44
48
  if (Modal.existing.length === 0) {
45
- this._getElement().classList.remove(CLASS_NAME_SHOW)
49
+ this.#getElement().classList.remove(CLASS_NAME_SHOW)
46
50
  }
47
51
 
48
- this._emulateAnimation(() => {
52
+ this.#emulateAnimation(() => {
49
53
  this.dispose()
50
54
  execute(callback)
51
55
  Modal.restorePrevious()
@@ -53,48 +57,46 @@ export default class Backdrop {
53
57
  }
54
58
 
55
59
  dispose () {
56
- if (!this._isAppended) {
60
+ if (!this.#isAppended) {
57
61
  return
58
62
  }
59
63
 
60
64
  if (Modal.existing.length === 0) {
61
- this._element.remove()
62
- this._isAppended = false
65
+ this.#element.remove()
66
+ this.#isAppended = false
63
67
  }
64
68
  }
65
69
 
66
70
  loading (isLoading) {
67
- const el = this._getElement()
71
+ const el = this.#getElement()
68
72
  el.classList[isLoading ? 'add' : 'remove'](CLASS_NAME_LOADING)
69
73
  }
70
74
 
71
- // Private
72
-
73
- _getElement () {
74
- if (!this._element) {
75
+ #getElement () {
76
+ if (!this.#element) {
75
77
  const backdrop = document.createElement('div')
76
- backdrop.className = this._config.className
77
- if (this._config.isAnimated) {
78
+ backdrop.className = this.#config.className
79
+ if (this.#config.isAnimated) {
78
80
  backdrop.classList.add(CLASS_NAME_FADE)
79
81
  }
80
82
 
81
- this._element = backdrop
83
+ this.#element = backdrop
82
84
  }
83
85
 
84
- return this._element
86
+ return this.#element
85
87
  }
86
88
 
87
- _append () {
88
- if (this._isAppended) {
89
+ #append () {
90
+ if (this.#isAppended) {
89
91
  return
90
92
  }
91
93
 
92
- document.body.append(this._getElement())
94
+ document.body.append(this.#getElement())
93
95
 
94
- this._isAppended = true
96
+ this.#isAppended = true
95
97
  }
96
98
 
97
- _emulateAnimation (callback) {
98
- executeAfterTransition(callback, this._getElement(), this._config.isAnimated)
99
+ #emulateAnimation (callback) {
100
+ executeAfterTransition(callback, this.#getElement(), this.#config.isAnimated)
99
101
  }
100
102
  }
@@ -33,33 +33,31 @@ export default class ErrorModal {
33
33
  }
34
34
 
35
35
  show () {
36
- this._append(this._buildModal())
36
+ this.#append(this.#buildModal())
37
37
  }
38
38
 
39
- // Private
40
-
41
- _buildModal () {
42
- const el = this._buildWrapper()
39
+ #buildModal () {
40
+ const el = this.#buildWrapper()
43
41
  el.querySelector('.modal-title').textContent = this.title
44
42
 
45
- const iframe = this._buildIframe(this.content)
43
+ const iframe = this.#buildIframe(this.content)
46
44
  el.querySelector('.modal-body').append(iframe)
47
45
 
48
46
  return el
49
47
  }
50
48
 
51
- _buildWrapper () {
49
+ #buildWrapper () {
52
50
  return new DOMParser().parseFromString(TEMPLATE(), 'text/html').body.childNodes[0]
53
51
  }
54
52
 
55
- _buildIframe () {
53
+ #buildIframe () {
56
54
  const iframe = document.createElement('iframe')
57
55
  iframe.className = 'error-iframe'
58
56
  iframe.srcdoc = this.content
59
57
  return iframe
60
58
  }
61
59
 
62
- _append (el) {
60
+ #append (el) {
63
61
  document.getElementById('modal').append(el)
64
62
  }
65
63
  }
@@ -13,6 +13,7 @@ export function fetchWithErrorHandling (url, options = {}) {
13
13
  .catch(response => {
14
14
  const title = `${response.status} (${response.statusText})`
15
15
  response.text().then(content => ErrorModal.show({ title, content }))
16
+ throw response
16
17
  })
17
18
  }
18
19
 
@@ -29,4 +30,5 @@ export function fetchTurboStream (url, options = {}) {
29
30
  return fetchWithErrorHandling(url, options)
30
31
  .then(response => response.text())
31
32
  .then(html => renderStreamMessage(html))
33
+ .catch(() => { /* Error already handled */ })
32
34
  }
data/lib/trestle/admin.rb CHANGED
@@ -14,6 +14,7 @@ module Trestle
14
14
  super
15
15
  end
16
16
  end
17
+ ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
17
18
 
18
19
  def respond_to_missing?(name, include_private=false)
19
20
  self.class.respond_to?(name, include_private) || super
@@ -118,8 +119,14 @@ module Trestle
118
119
  "#{name.underscore}/admin"
119
120
  end
120
121
 
121
- def path(action=root_action, options={})
122
- Engine.routes.url_for(options.merge(controller: controller_namespace, action: action, only_path: true))
122
+ def path(action=nil, options={})
123
+ defaults = {
124
+ controller: controller_namespace,
125
+ action: action || root_action,
126
+ only_path: true
127
+ }
128
+
129
+ Engine.routes.url_for(defaults.merge(options))
123
130
  end
124
131
 
125
132
  def to_param(*)
@@ -91,6 +91,9 @@ module Trestle
91
91
  # Default adapter class used by all admin resources
92
92
  option :default_adapter, Adapters.compose(Adapters::ActiveRecordAdapter, Adapters::DraperAdapter)
93
93
 
94
+ # List of Stimulus controllers to add to forms by default
95
+ option :default_form_controllers, %w(keyboard-submit form-loading form-error)
96
+
94
97
  # Register a custom form field class
95
98
  def form_field(name, field)
96
99
  Form::Builder.register(name, field)
@@ -17,7 +17,7 @@ module Trestle
17
17
  initializer "trestle.automount" do |app|
18
18
  if Trestle.config.automount
19
19
  app.routes.prepend do
20
- mount Trestle::Engine => Trestle.config.path
20
+ mount Trestle::Engine, at: Trestle.config.path
21
21
  end
22
22
  end
23
23
  end
@@ -5,20 +5,18 @@ module Trestle
5
5
  # both the Adapter/Navigation instance, as well as the controller/view from where they are invoked.
6
6
  module EvaluationContext
7
7
  protected
8
- def self.ruby2_keywords(*)
9
- end unless respond_to?(:ruby2_keywords, true)
10
-
11
8
  # Missing methods are called on the given context if available.
12
9
  #
13
10
  # We include private methods as methods such as current_user
14
11
  # are usually declared as private or protected.
15
- ruby2_keywords def method_missing(name, *args, &block)
12
+ def method_missing(name, *args, &block)
16
13
  if context_responds_to?(name)
17
14
  @context.send(name, *args, &block)
18
15
  else
19
16
  super
20
17
  end
21
18
  end
19
+ ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
22
20
 
23
21
  def respond_to_missing?(name, include_private=false)
24
22
  context_responds_to?(name) || super
@@ -16,7 +16,7 @@ module Trestle
16
16
  if associated_instance = instance.public_send(attribute.association_name)
17
17
  admin_link_to format_value(associated_instance), associated_instance
18
18
  else
19
- content_tag(:span, I18n.t("admin.format.blank"), class: "blank")
19
+ tag.span(I18n.t("admin.format.blank"), class: "blank")
20
20
  end
21
21
  end
22
22
  else
@@ -3,7 +3,7 @@ module Trestle
3
3
  class Field
4
4
  attr_reader :builder, :template, :name, :options, :block
5
5
 
6
- delegate :admin, :content_tag, :concat, :safe_join, :icon, to: :template
6
+ delegate :admin, :tag, :content_tag, :concat, :safe_join, :icon, to: :template
7
7
 
8
8
  def initialize(builder, template, name, options={}, &block)
9
9
  @builder, @template, @name, @block = builder, template, name, block
@@ -19,7 +19,7 @@ module Trestle
19
19
  wrapper_class = options.delete(:class)
20
20
  wrapper_class = default_wrapper_class if wrapper_class.empty?
21
21
 
22
- content_tag(:div, class: wrapper_class) do
22
+ tag.div(class: wrapper_class) do
23
23
  safe_join([
24
24
  builder.raw_check_box(name, options.merge(class: input_class), checked_value, unchecked_value),
25
25
  builder.label(name, options[:label] || admin.human_attribute_name(name), class: label_class, value: (checked_value if options[:multiple]))
@@ -18,7 +18,7 @@ module Trestle
18
18
  if block
19
19
  block.call(b)
20
20
  else
21
- content_tag(:div, class: default_wrapper_class) do
21
+ tag.div(class: default_wrapper_class) do
22
22
  b.check_box(class: input_class) + b.label(class: label_class) { b.text }
23
23
  end
24
24
  end
@@ -18,7 +18,7 @@ module Trestle
18
18
  if block
19
19
  block.call(b)
20
20
  else
21
- content_tag(:div, class: default_wrapper_class) do
21
+ tag.div(class: default_wrapper_class) do
22
22
  b.radio_button(class: input_class) + b.label(class: label_class) { b.text }
23
23
  end
24
24
  end
@@ -11,7 +11,7 @@ module Trestle
11
11
  end
12
12
 
13
13
  def field
14
- content_tag(:div, class: "date-select") do
14
+ tag.div(class: "date-select") do
15
15
  builder.raw_date_select(name, options, html_options, &block)
16
16
  end
17
17
  end
@@ -11,7 +11,7 @@ module Trestle
11
11
  end
12
12
 
13
13
  def field
14
- content_tag(:div, class: "datetime-select") do
14
+ tag.div(class: "datetime-select") do
15
15
  builder.raw_datetime_select(name, options, html_options, &block)
16
16
  end
17
17
  end
@@ -12,7 +12,7 @@ module Trestle
12
12
 
13
13
  def input_group
14
14
  if @prepend || @append
15
- content_tag(:div, class: "input-group") do
15
+ tag.div(class: "input-group") do
16
16
  concat input_group_addon(@prepend) if @prepend
17
17
  concat yield
18
18
  concat input_group_addon(@append) if @append
@@ -24,7 +24,7 @@ module Trestle
24
24
 
25
25
  def input_group_addon(addon)
26
26
  if addon[:wrap]
27
- content_tag(:span, addon[:content], class: "input-group-text")
27
+ tag.span(addon[:content], class: "input-group-text")
28
28
  else
29
29
  addon[:content]
30
30
  end
@@ -12,7 +12,7 @@ module Trestle
12
12
  end
13
13
 
14
14
  def render
15
- content_tag(:div, options.except(*WRAPPER_OPTIONS)) do
15
+ tag.div(**options.except(*WRAPPER_OPTIONS)) do
16
16
  concat label if name && options[:label] != false
17
17
  concat template.capture(&block) if block
18
18
  concat help_message if options[:help]
@@ -30,13 +30,13 @@ module Trestle
30
30
  message = options[:help]
31
31
  end
32
32
 
33
- content_tag(:p, message, class: classes)
33
+ tag.p(message, class: classes)
34
34
  end
35
35
 
36
36
  def error_messages
37
- content_tag(:ul, class: "invalid-feedback") do
37
+ tag.ul(class: "invalid-feedback") do
38
38
  safe_join(errors.map { |error|
39
- content_tag(:li, safe_join([icon("fa fa-warning"), error], " "))
39
+ tag.li(safe_join([icon("fa fa-warning"), error], " "))
40
40
  }, "\n")
41
41
  end
42
42
  end
@@ -20,7 +20,7 @@ module Trestle
20
20
  wrapper_class = options.delete(:class)
21
21
  wrapper_class = default_wrapper_class if wrapper_class.empty?
22
22
 
23
- content_tag(:div, class: wrapper_class) do
23
+ tag.div(class: wrapper_class) do
24
24
  safe_join([
25
25
  builder.raw_radio_button(name, tag_value, options.merge(class: input_class)),
26
26
  builder.label(name, options[:label] || tag_value.to_s.humanize, value: tag_value, class: label_class)
@@ -18,7 +18,7 @@ module Trestle
18
18
  if block
19
19
  template.capture(&block)
20
20
  else
21
- content_tag(:p, value || default_value, class: "form-control-static")
21
+ tag.p(value || default_value, class: "form-control-static")
22
22
  end
23
23
  end
24
24
 
@@ -11,7 +11,7 @@ module Trestle
11
11
  end
12
12
 
13
13
  def field
14
- content_tag(:div, class: "time-select") do
14
+ tag.div(class: "time-select") do
15
15
  builder.raw_time_select(name, options, html_options, &block)
16
16
  end
17
17
  end
@@ -4,9 +4,6 @@ require "action_view/helpers"
4
4
  module Trestle
5
5
  class Form
6
6
  class Renderer
7
- def self.ruby2_keywords(*)
8
- end unless respond_to?(:ruby2_keywords, true)
9
-
10
7
  include ::ActionView::Context
11
8
  include ::ActionView::Helpers::CaptureHelper
12
9
 
@@ -14,7 +11,7 @@ module Trestle
14
11
  include Hook::Helpers
15
12
 
16
13
  # Whitelisted helpers will concatenate their result to the output buffer when called.
17
- WHITELISTED_HELPERS = [:row, :col, :render, :tab, :table, :divider, :h1, :h2, :h3, :h4, :h5, :h6, :card, :panel, :well]
14
+ WHITELISTED_HELPERS = [:row, :col, :render, :tab, :table, :divider, :h1, :h2, :h3, :h4, :h5, :h6, :card, :panel, :well, :turbo_frame_tag]
18
15
 
19
16
  # Raw block helpers will pass their block argument directly to the method without wrapping it in a new output buffer.
20
17
  RAW_BLOCK_HELPERS = [:table, :toolbar]
@@ -46,7 +43,7 @@ module Trestle
46
43
  concat(result)
47
44
  end
48
45
 
49
- ruby2_keywords def method_missing(name, *args, &block)
46
+ def method_missing(name, *args, &block)
50
47
  target = @form.respond_to?(name) ? @form : @template
51
48
 
52
49
  if block_given? && !RAW_BLOCK_HELPERS.include?(name)
@@ -63,6 +60,7 @@ module Trestle
63
60
  result
64
61
  end
65
62
  end
63
+ ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
66
64
 
67
65
  def respond_to_missing?(name, include_all=false)
68
66
  @form.respond_to?(name, include_all) ||