trestle 0.10.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/rspec.yml +4 -0
- data/app/assets/bundle/trestle/admin.css +3 -3
- data/app/assets/bundle/trestle/admin.js +13 -13
- data/app/assets/bundle/trestle/photoswipe-2d522a3abaa59f8a8f73.digested.js +6 -0
- data/app/helpers/trestle/avatar_helper.rb +20 -14
- data/app/helpers/trestle/card_helper.rb +27 -9
- data/app/helpers/trestle/container_helper.rb +37 -6
- data/app/helpers/trestle/display_helper.rb +11 -0
- data/app/helpers/trestle/flash_helper.rb +1 -10
- data/app/helpers/trestle/form_helper.rb +32 -18
- data/app/helpers/trestle/format_helper.rb +47 -17
- data/app/helpers/trestle/gravatar_helper.rb +48 -0
- data/app/helpers/trestle/grid_helper.rb +12 -14
- data/app/helpers/trestle/headings_helper.rb +2 -23
- data/app/helpers/trestle/i18n_helper.rb +1 -0
- data/app/helpers/trestle/icon_helper.rb +16 -3
- data/app/helpers/trestle/layout_helper.rb +1 -0
- data/app/helpers/trestle/modal_helper.rb +21 -2
- data/app/helpers/trestle/navigation_helper.rb +1 -0
- data/app/helpers/trestle/pagination_helper.rb +1 -0
- data/app/helpers/trestle/params_helper.rb +32 -0
- data/app/helpers/trestle/sort_helper.rb +38 -7
- data/app/helpers/trestle/status_helper.rb +19 -3
- data/app/helpers/trestle/tab_helper.rb +42 -7
- data/app/helpers/trestle/table_helper.rb +23 -23
- data/app/helpers/trestle/timestamp_helper.rb +18 -25
- data/app/helpers/trestle/title_helper.rb +2 -0
- data/app/helpers/trestle/toolbars_helper.rb +2 -1
- data/app/helpers/trestle/turbo/frame_helper.rb +25 -14
- data/app/helpers/trestle/url_helper.rb +124 -54
- data/app/views/kaminari/trestle/_first_page.html.erb +1 -2
- data/app/views/kaminari/trestle/_gap.html.erb +0 -1
- data/app/views/kaminari/trestle/_last_page.html.erb +1 -2
- data/app/views/kaminari/trestle/_page.html.erb +1 -2
- data/app/views/kaminari/trestle/_paginator.html.erb +0 -1
- data/app/views/layouts/trestle/admin.html.erb +3 -3
- data/app/views/layouts/trestle/modal.html.erb +2 -2
- data/app/views/trestle/application/_layout.html.erb +22 -18
- data/app/views/trestle/application/_tabs.html.erb +1 -1
- data/app/views/trestle/flash/_alert.html.erb +3 -1
- data/app/views/trestle/flash/_flash.html.erb +7 -4
- data/app/views/trestle/resource/_scopes.html.erb +3 -3
- data/app/views/trestle/resource/create.turbo_stream.erb +1 -0
- data/app/views/trestle/resource/destroy.turbo_stream.erb +2 -0
- data/app/views/trestle/resource/index.html.erb +10 -12
- data/app/views/trestle/resource/update.turbo_stream.erb +1 -0
- data/app/views/trestle/shared/_sidebar.html.erb +8 -8
- data/app/views/trestle/table/_table.html.erb +2 -2
- data/config/locales/pt-BR.yml +13 -13
- data/frontend/css/components/_scopes.scss +6 -7
- data/frontend/css/core/_theme.scss +3 -3
- data/frontend/css/layout/_sidebar.scss +2 -0
- data/frontend/js/controllers/flatpickr_controller.js +2 -2
- data/frontend/js/controllers/lightbox_controller.js +3 -3
- data/frontend/js/controllers/modal_trigger_controller.js +2 -2
- data/frontend/js/controllers/sidebar_controller.js +13 -3
- data/frontend/js/controllers/tab_errors_controller.js +2 -2
- data/frontend/js/core/backdrop.js +30 -28
- data/frontend/js/core/error_modal.js +7 -9
- data/frontend/js/core/fetch.js +2 -0
- data/lib/trestle/admin.rb +9 -2
- data/lib/trestle/configuration.rb +3 -0
- data/lib/trestle/engine.rb +1 -1
- data/lib/trestle/evaluation_context.rb +2 -4
- data/lib/trestle/form/automatic.rb +1 -1
- data/lib/trestle/form/field.rb +1 -1
- data/lib/trestle/form/fields/check_box.rb +1 -1
- data/lib/trestle/form/fields/collection_check_boxes.rb +1 -1
- data/lib/trestle/form/fields/collection_radio_buttons.rb +1 -1
- data/lib/trestle/form/fields/date_select.rb +1 -1
- data/lib/trestle/form/fields/datetime_select.rb +1 -1
- data/lib/trestle/form/fields/form_control.rb +2 -2
- data/lib/trestle/form/fields/form_group.rb +4 -4
- data/lib/trestle/form/fields/radio_button.rb +1 -1
- data/lib/trestle/form/fields/static_field.rb +1 -1
- data/lib/trestle/form/fields/time_select.rb +1 -1
- data/lib/trestle/form/renderer.rb +2 -4
- data/lib/trestle/hook/helpers.rb +21 -0
- data/lib/trestle/navigation/block.rb +8 -15
- data/lib/trestle/navigation/group.rb +2 -2
- data/lib/trestle/navigation/item.rb +21 -4
- data/lib/trestle/registry.rb +14 -11
- data/lib/trestle/resource/builder.rb +9 -6
- data/lib/trestle/resource/toolbar.rb +4 -4
- data/lib/trestle/resource.rb +7 -5
- data/lib/trestle/scopes/block.rb +8 -12
- data/lib/trestle/scopes/definition.rb +6 -2
- data/lib/trestle/scopes/scope.rb +13 -10
- data/lib/trestle/tab.rb +2 -2
- data/lib/trestle/table/column.rb +4 -3
- data/lib/trestle/table/row.rb +1 -1
- data/lib/trestle/toolbar/builder.rb +6 -6
- data/lib/trestle/toolbar/context.rb +2 -4
- data/lib/trestle/toolbar/item.rb +10 -19
- data/lib/trestle/toolbar/menu.rb +9 -9
- data/lib/trestle/version.rb +1 -1
- data/lib/trestle.rb +2 -2
- data/package.json +7 -7
- data/yarn.lock +517 -564
- metadata +5 -5
- data/app/assets/bundle/trestle/photoswipe-063ce7be40e10b3e6848.digested.js +0 -6
- data/app/views/layouts/trestle/admin.turbo_stream.erb +0 -4
@@ -109,7 +109,7 @@ export default class extends ApplicationController {
|
|
109
109
|
itemData.type = 'html'
|
110
110
|
itemData.html = `<video controls><source src="${itemData.src}"></video>`
|
111
111
|
|
112
|
-
this
|
112
|
+
this.#setDefaultVideoDimensions(itemData)
|
113
113
|
}
|
114
114
|
|
115
115
|
return itemData
|
@@ -126,7 +126,7 @@ export default class extends ApplicationController {
|
|
126
126
|
itemData.type = 'html'
|
127
127
|
itemData.html = `<iframe src="${src}" allowfullscreen></iframe>`
|
128
128
|
|
129
|
-
this
|
129
|
+
this.#setDefaultVideoDimensions(itemData)
|
130
130
|
}
|
131
131
|
}
|
132
132
|
|
@@ -151,7 +151,7 @@ export default class extends ApplicationController {
|
|
151
151
|
}
|
152
152
|
}
|
153
153
|
|
154
|
-
setDefaultVideoDimensions(itemData) {
|
154
|
+
#setDefaultVideoDimensions(itemData) {
|
155
155
|
itemData.w ||= this.defaultVideoWidthValue
|
156
156
|
itemData.h ||= this.defaultVideoHeightValue
|
157
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
|
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
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
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
|
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
|
-
|
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
|
25
|
-
this
|
26
|
-
this
|
28
|
+
this.#config = Default
|
29
|
+
this.#element = null
|
30
|
+
this.#isAppended = false
|
27
31
|
}
|
28
32
|
|
29
33
|
show (callback) {
|
30
|
-
this
|
34
|
+
this.#append()
|
31
35
|
|
32
|
-
if (this.
|
33
|
-
reflow(this
|
36
|
+
if (this.#config.isAnimated) {
|
37
|
+
reflow(this.#getElement())
|
34
38
|
}
|
35
39
|
|
36
|
-
this
|
40
|
+
this.#getElement().classList.add(CLASS_NAME_SHOW)
|
37
41
|
|
38
|
-
this
|
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
|
49
|
+
this.#getElement().classList.remove(CLASS_NAME_SHOW)
|
46
50
|
}
|
47
51
|
|
48
|
-
this
|
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
|
60
|
+
if (!this.#isAppended) {
|
57
61
|
return
|
58
62
|
}
|
59
63
|
|
60
64
|
if (Modal.existing.length === 0) {
|
61
|
-
this.
|
62
|
-
this
|
65
|
+
this.#element.remove()
|
66
|
+
this.#isAppended = false
|
63
67
|
}
|
64
68
|
}
|
65
69
|
|
66
70
|
loading (isLoading) {
|
67
|
-
const el = this
|
71
|
+
const el = this.#getElement()
|
68
72
|
el.classList[isLoading ? 'add' : 'remove'](CLASS_NAME_LOADING)
|
69
73
|
}
|
70
74
|
|
71
|
-
|
72
|
-
|
73
|
-
_getElement () {
|
74
|
-
if (!this._element) {
|
75
|
+
#getElement () {
|
76
|
+
if (!this.#element) {
|
75
77
|
const backdrop = document.createElement('div')
|
76
|
-
backdrop.className = this.
|
77
|
-
if (this.
|
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
|
83
|
+
this.#element = backdrop
|
82
84
|
}
|
83
85
|
|
84
|
-
return this
|
86
|
+
return this.#element
|
85
87
|
}
|
86
88
|
|
87
|
-
|
88
|
-
if (this
|
89
|
+
#append () {
|
90
|
+
if (this.#isAppended) {
|
89
91
|
return
|
90
92
|
}
|
91
93
|
|
92
|
-
document.body.append(this
|
94
|
+
document.body.append(this.#getElement())
|
93
95
|
|
94
|
-
this
|
96
|
+
this.#isAppended = true
|
95
97
|
}
|
96
98
|
|
97
|
-
|
98
|
-
executeAfterTransition(callback, this
|
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
|
36
|
+
this.#append(this.#buildModal())
|
37
37
|
}
|
38
38
|
|
39
|
-
|
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
|
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
|
-
|
49
|
+
#buildWrapper () {
|
52
50
|
return new DOMParser().parseFromString(TEMPLATE(), 'text/html').body.childNodes[0]
|
53
51
|
}
|
54
52
|
|
55
|
-
|
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
|
-
|
60
|
+
#append (el) {
|
63
61
|
document.getElementById('modal').append(el)
|
64
62
|
}
|
65
63
|
}
|
data/frontend/js/core/fetch.js
CHANGED
@@ -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=
|
122
|
-
|
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)
|
data/lib/trestle/engine.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
19
|
+
tag.span(I18n.t("admin.format.blank"), class: "blank")
|
20
20
|
end
|
21
21
|
end
|
22
22
|
else
|
data/lib/trestle/form/field.rb
CHANGED
@@ -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
|
-
|
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]))
|
@@ -12,7 +12,7 @@ module Trestle
|
|
12
12
|
|
13
13
|
def input_group
|
14
14
|
if @prepend || @append
|
15
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
33
|
+
tag.p(message, class: classes)
|
34
34
|
end
|
35
35
|
|
36
36
|
def error_messages
|
37
|
-
|
37
|
+
tag.ul(class: "invalid-feedback") do
|
38
38
|
safe_join(errors.map { |error|
|
39
|
-
|
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
|
-
|
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)
|
@@ -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
|
|
@@ -46,7 +43,7 @@ module Trestle
|
|
46
43
|
concat(result)
|
47
44
|
end
|
48
45
|
|
49
|
-
|
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) ||
|
data/lib/trestle/hook/helpers.rb
CHANGED
@@ -1,6 +1,25 @@
|
|
1
1
|
module Trestle
|
2
2
|
class Hook
|
3
3
|
module Helpers
|
4
|
+
# Evaluates any defined hooks with the given name and returns the result.
|
5
|
+
#
|
6
|
+
# Each hook is evaluated and passed any provided arguments, and the result
|
7
|
+
# is concatenated together. If no hooks are defined, and a block is passed
|
8
|
+
# to this helper, then the block will be evaluated instead.
|
9
|
+
#
|
10
|
+
# name - Name of hook to evaluate
|
11
|
+
# args - Arguments to pass to hook blocks
|
12
|
+
# block - Optional block to evaluate as a fallback if no hooks are defined
|
13
|
+
#
|
14
|
+
# Examples
|
15
|
+
#
|
16
|
+
# <%= hook("index.toolbar.primary", toolbar) %>
|
17
|
+
#
|
18
|
+
# <%= hook("view.title") do %>
|
19
|
+
# Default Title
|
20
|
+
# <% end %>
|
21
|
+
#
|
22
|
+
# Returns a HTML-safe string.
|
4
23
|
def hook(name, *args, &block)
|
5
24
|
hooks = hooks(name)
|
6
25
|
|
@@ -13,6 +32,8 @@ module Trestle
|
|
13
32
|
end
|
14
33
|
end
|
15
34
|
|
35
|
+
# Returns true or false depending on whether there are any defined hooks
|
36
|
+
# (either on the current admin or globally) with the given name.
|
16
37
|
def hook?(name)
|
17
38
|
hooks(name).any?
|
18
39
|
end
|
@@ -17,6 +17,8 @@ module Trestle
|
|
17
17
|
class Evaluator
|
18
18
|
include EvaluationContext
|
19
19
|
|
20
|
+
delegate :path, to: :@admin
|
21
|
+
|
20
22
|
attr_reader :items
|
21
23
|
|
22
24
|
def initialize(admin=nil, context=nil)
|
@@ -24,30 +26,21 @@ module Trestle
|
|
24
26
|
@items = []
|
25
27
|
end
|
26
28
|
|
27
|
-
def
|
28
|
-
@admin ? @admin.path : nil
|
29
|
-
end
|
30
|
-
|
31
|
-
def item(name, path=nil, options={})
|
32
|
-
if path.is_a?(Hash)
|
33
|
-
options = path
|
34
|
-
path = nil
|
35
|
-
end
|
36
|
-
|
29
|
+
def item(name, path=nil, **options)
|
37
30
|
if options[:group]
|
38
31
|
group = Group.new(options[:group])
|
39
32
|
elsif @current_group
|
40
33
|
group = @current_group
|
41
34
|
end
|
42
35
|
|
43
|
-
options
|
44
|
-
options
|
36
|
+
options.merge!(group: group) if group
|
37
|
+
options.merge!(admin: @admin) if @admin
|
45
38
|
|
46
|
-
items << Item.new(name, path
|
39
|
+
items << Item.new(name, path, **options)
|
47
40
|
end
|
48
41
|
|
49
|
-
def group(name, options
|
50
|
-
@current_group = Group.new(name, options)
|
42
|
+
def group(name, **options)
|
43
|
+
@current_group = Group.new(name, **options)
|
51
44
|
yield
|
52
45
|
ensure
|
53
46
|
@current_group = nil
|
@@ -3,7 +3,7 @@ module Trestle
|
|
3
3
|
class Group
|
4
4
|
attr_reader :name, :options
|
5
5
|
|
6
|
-
def initialize(name, options
|
6
|
+
def initialize(name, **options)
|
7
7
|
@name, @options = name.to_s, options
|
8
8
|
end
|
9
9
|
|
@@ -26,7 +26,7 @@ module Trestle
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def merge(other)
|
29
|
-
self.class.new(name, options.merge(other.options))
|
29
|
+
self.class.new(name, **options.merge(other.options))
|
30
30
|
end
|
31
31
|
|
32
32
|
def priority
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module Trestle
|
2
2
|
class Navigation
|
3
3
|
class Item
|
4
|
-
attr_reader :name, :
|
4
|
+
attr_reader :name, :options
|
5
5
|
|
6
|
-
def initialize(name, path=nil, options
|
6
|
+
def initialize(name, path=nil, **options)
|
7
7
|
@name, @path, @options = name.to_s, path, options
|
8
8
|
end
|
9
9
|
|
@@ -35,8 +35,25 @@ module Trestle
|
|
35
35
|
options[:group] || NullGroup.new
|
36
36
|
end
|
37
37
|
|
38
|
+
def path
|
39
|
+
if @path
|
40
|
+
@path
|
41
|
+
elsif admin = self.admin
|
42
|
+
admin.path(options[:action])
|
43
|
+
else
|
44
|
+
"#"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
38
48
|
def admin
|
39
|
-
options[:admin]
|
49
|
+
case options[:admin]
|
50
|
+
when nil, false
|
51
|
+
return
|
52
|
+
when Symbol, String
|
53
|
+
Trestle.lookup(options[:admin]) or raise ActionController::UrlGenerationError, "No admin found named #{options[:admin].inspect}"
|
54
|
+
else
|
55
|
+
options[:admin]
|
56
|
+
end
|
40
57
|
end
|
41
58
|
|
42
59
|
def label
|
@@ -56,7 +73,7 @@ module Trestle
|
|
56
73
|
end
|
57
74
|
|
58
75
|
def html_options
|
59
|
-
options.except(:admin, :badge, :group, :icon, :if, :label, :priority, :unless)
|
76
|
+
options.except(:action, :admin, :badge, :group, :icon, :if, :label, :priority, :unless)
|
60
77
|
end
|
61
78
|
|
62
79
|
def visible?(context)
|