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
@@ -1,9 +1,9 @@
1
1
  module Trestle
2
2
  module GridHelper
3
- # Renders a row div, one of the building blocks of Bootstrap's grid system.
4
- # https://getbootstrap.com/docs/4.4/layout/grid/
3
+ # Renders a row <div> tag, one of the building blocks of Bootstrap's grid system.
4
+ # https://getbootstrap.com/docs/5.3/layout/grid/
5
5
  #
6
- # attrs - Hash of attributes that will be passed to the tag (e.g. id, data, class).
6
+ # attributes - Hash of attributes that will be passed to the tag (e.g. id, data, class)
7
7
  #
8
8
  # Examples
9
9
  #
@@ -14,15 +14,13 @@ module Trestle
14
14
  # <%= row class: "row-cols-2", id: "my-row" do %> ...
15
15
  #
16
16
  # Returns a HTML-safe String.
17
- def row(attrs={})
17
+ def row(**attributes)
18
18
  defaults = Trestle::Options.new(class: ["row"])
19
- options = defaults.merge(attrs)
20
-
21
- content_tag(:div, options) { yield }
19
+ tag.div(**defaults.merge(attributes)) { yield }
22
20
  end
23
21
 
24
- # Renders a column div, one of the building blocks of Bootstrap's grid system.
25
- # https://getbootstrap.com/docs/4.4/layout/grid/
22
+ # Renders a column <div> tag, one of the building blocks of Bootstrap's grid system.
23
+ # https://getbootstrap.com/docs/5.3/layout/grid/
26
24
  #
27
25
  # Column divs should always be rendered inside of a row div.
28
26
  #
@@ -53,16 +51,16 @@ module Trestle
53
51
  classes += breakpoints.map { |breakpoint, span| "col-#{breakpoint}-#{span}" }
54
52
  end
55
53
 
56
- content_tag(:div, class: classes) { yield }
54
+ tag.div(class: classes) { yield }
57
55
  end
58
56
 
59
- # Renders an <HR> (horizontal rule) HTML tag.
57
+ # Renders an <hr> (horizontal rule) HTML tag.
60
58
  #
61
- # attrs - Hash of attributes that will be passed to the tag (e.g. id, data, class).
59
+ # attributes - Hash of attributes that will be passed to the <hr> tag
62
60
  #
63
61
  # Returns a HTML-safe String.
64
- def divider(attrs={})
65
- tag(:hr, attrs)
62
+ def divider(**attributes)
63
+ tag.hr(**attributes)
66
64
  end
67
65
  end
68
66
  end
@@ -1,27 +1,6 @@
1
1
  module Trestle
2
2
  module HeadingsHelper
3
- def h1(text, options={})
4
- content_tag(:h1, text, options)
5
- end
6
-
7
- def h2(text, options={})
8
- content_tag(:h2, text, options)
9
- end
10
-
11
- def h3(text, options={})
12
- content_tag(:h3, text, options)
13
- end
14
-
15
- def h4(text, options={})
16
- content_tag(:h4, text, options)
17
- end
18
-
19
- def h5(text, options={})
20
- content_tag(:h5, text, options)
21
- end
22
-
23
- def h6(text, options={})
24
- content_tag(:h6, text, options)
25
- end
3
+ # These methods are delegated to the ActionView::Helpers::TagHelper proxy object for convenience.
4
+ delegate :h1, :h2, :h3, :h4, :h5, :h6, to: :tag
26
5
  end
27
6
  end
@@ -1,4 +1,5 @@
1
1
  module Trestle
2
+ # [Internal]
2
3
  module I18nHelper
3
4
  FLATPICKR_LOCALE_CONVERSIONS = { ca: "cat", el: "gr", nb: "no", vi: "vn" }.stringify_keys
4
5
 
@@ -1,8 +1,21 @@
1
1
  module Trestle
2
2
  module IconHelper
3
- def icon(*classes)
4
- options = classes.extract_options!
5
- content_tag(:i, "", options.merge(class: classes))
3
+ # Renders an icon (as an <i> tag).
4
+ #
5
+ # Trestle includes the FontAwesome icon library but other font
6
+ # libraries can be included via custom CSS.
7
+ #
8
+ # classes - List of font name classes to add to the <i> tag
9
+ # attributes - Additional HTML attributes to add to the <i> tag
10
+ #
11
+ # Examples
12
+ #
13
+ # <%= icon("fas fa-star") %>
14
+ # <%= icon("fas", "fa-star", class: "fa-fw text-muted")
15
+ #
16
+ # Return the HTML i tag for the icon.
17
+ def icon(*classes, **attributes)
18
+ tag.i("", **attributes.merge(class: [*classes, attributes[:class]]))
6
19
  end
7
20
  end
8
21
  end
@@ -1,4 +1,5 @@
1
1
  module Trestle
2
+ # [Internal]
2
3
  module LayoutHelper
3
4
  SIDEBAR_CLASSES = {
4
5
  "expanded" => "sidebar-expanded",
@@ -1,16 +1,34 @@
1
1
  module Trestle
2
2
  module ModalHelper
3
+ # Merges the given options with the existing hash of defined modal options.
4
+ #
5
+ # Trestle uses Bootstrap modal markup (https://getbootstrap.com/docs/5.3/components/modal/)
6
+ # to render modals, which consist of an outer .modal wrapper div with an inner
7
+ # .modal-dialog div.
8
+ #
9
+ # The `class` attribute (e.g. `"modal-lg"`) is applied to the inner element, and all
10
+ # other attributes are applied to the outer element. A class can be applied to the
11
+ # wrapper element using the `wrapper_class` option.
12
+ #
13
+ # Examples
14
+ #
15
+ # <% modal_options! class: "modal-lg", controller: "modal-stimulus" %>
16
+ #
17
+ # <% modal_options! wrapper_class: "custom-modal-animation" %>
18
+ #
3
19
  def modal_options!(options)
4
20
  modal_options.merge!(options)
5
21
  end
6
22
 
23
+ # Returns a hash of the currently defined modal options
7
24
  def modal_options
8
25
  @_modal_options ||= {}
9
26
  end
10
27
 
28
+ # Returns the HTML attributes to apply to the modal wrapper (.modal) <div>
11
29
  def modal_wrapper_attributes
12
30
  {
13
- class: ["modal", "fade", modal_options[:wrapper_class]],
31
+ class: ["modal", "fade", modal_options[:wrapper_class]].compact,
14
32
  tabindex: "-1",
15
33
  role: "dialog",
16
34
  data: {
@@ -19,9 +37,10 @@ module Trestle
19
37
  }.deep_merge(modal_options.except(:class, :wrapper_class, :controller))
20
38
  end
21
39
 
40
+ # Returns the HTML attributes to apply to the inner modal dialog (.modal-dialog) <div>
22
41
  def modal_dialog_attributes
23
42
  {
24
- class: ["modal-dialog", modal_options[:class]],
43
+ class: ["modal-dialog", modal_options[:class]].compact,
25
44
  role: "document"
26
45
  }
27
46
  end
@@ -1,4 +1,5 @@
1
1
  module Trestle
2
+ # [Internal]
2
3
  module NavigationHelper
3
4
  def current_navigation_item?(item)
4
5
  current_page?(item.path) || (item.admin && current_admin?(item.admin))
@@ -1,4 +1,5 @@
1
1
  module Trestle
2
+ # [Internal]
2
3
  module PaginationHelper
3
4
  # Custom version of Kaminari's page_entries_info helper to use a
4
5
  # Trestle-scoped I18n key and add a delimiter to the total count.
@@ -1,10 +1,42 @@
1
1
  module Trestle
2
2
  module ParamsHelper
3
+ # Returns a subset of the params "hash" (an instance of ActionController::Parameters)
4
+ # limited to only those keys that should be considered persistent throughout
5
+ # reordering and pagination.
6
+ #
7
+ # This could be a scope or the current order, but this may be extended by Trestle
8
+ # plugins and the application itself in `Trestle.config.persistent_params` to include
9
+ # search queries, filters, etc.
10
+ #
11
+ # By default this list is set to: [:sort, :order, :scope]
12
+ #
13
+ # Returns an instance of ActionController::Parameters.
3
14
  def persistent_params
4
15
  flat, nested = Trestle.config.persistent_params.partition { |p| !p.is_a?(Hash) }
5
16
  nested = nested.inject({}) { |result, param| result.merge(param) }
6
17
 
7
18
  params.slice(*(flat + nested.keys)).permit(*(flat << nested))
8
19
  end
20
+
21
+ # Converts the current set of persistent params into hidden form fields, suitable
22
+ # for inclusion within a form tag (e.g for search or filtering).
23
+ #
24
+ # except - List of param keys to exclude
25
+ # only - List of param keys to only include
26
+ #
27
+ # Returns a HTML-safe String.
28
+ def serialize_persistent_params(except: [], only: [])
29
+ params = persistent_params
30
+
31
+ if Array(only).any?
32
+ params = params.slice(*only)
33
+ elsif Array(except).any?
34
+ params = params.except(*except)
35
+ end
36
+
37
+ safe_join(to_form_params(params).map { |param|
38
+ tag.input(type: "hidden", name: param[:name], value: param[:value], autocomplete: "off")
39
+ }, "\n")
40
+ end
9
41
  end
10
42
  end
@@ -1,20 +1,47 @@
1
1
  module Trestle
2
2
  module SortHelper
3
- def sort_link(text, field, options={})
4
- sort_link = SortLink.new(field, persistent_params, options)
3
+ # Renders a sort link for a table column header.
4
+ #
5
+ # The `sort` and `order` params are used to determine whether the sort link is
6
+ # active, and which is the current sort direction (asc or desc). CSS classes are
7
+ # applied accordingly which are used to render the appropriate icons alongside the link.
8
+ #
9
+ # The current set of `persistent_params` is merged with the new `sort`/`order` params
10
+ # to build the target link URL.
11
+ #
12
+ # text - Text or HTML content to render as the link label
13
+ # field - The name of the current field, which should match the `sort` param
14
+ # when the collection is being sorted by this field
15
+ # options - Hash of options (default: {}):
16
+ # :default - (Boolean) Set to true if the field is considered to be active
17
+ # even when the `sort` param is blank (default: false)
18
+ # :default_order - (String/Symbol) Specify the default collection order when
19
+ # the `order` param is blank (default: "asc")
20
+ #
21
+ # Examples
22
+ #
23
+ # <%= sort_link "Title", :title, default: true %>
24
+ #
25
+ # <%= sort_link "Created", :created_at, default_order: "desc" %>
26
+ #
27
+ # Returns a HTML-safe String.
28
+ def sort_link(text, field, **options)
29
+ sort_link = SortLink.new(field, persistent_params, **options)
5
30
  link_to text, sort_link.params, class: sort_link.classes
6
31
  end
7
32
 
8
33
  class SortLink
9
34
  attr_reader :field
10
35
 
11
- def initialize(field, params, options)
12
- @field, @params, @options = field, params, options
36
+ def initialize(field, params, default: false, default_order: "asc")
37
+ @field, @params = field, params
38
+
39
+ @default = default
40
+ @default_order = default_order.to_s
13
41
  end
14
42
 
15
43
  def active?
16
- @params[:sort] == field.to_s ||
17
- (@options[:default] && !@params.key?(:sort))
44
+ @params[:sort] == field.to_s || (default? && !@params.key?(:sort))
18
45
  end
19
46
 
20
47
  def params
@@ -33,8 +60,12 @@ module Trestle
33
60
  @params[:order] || default_order
34
61
  end
35
62
 
63
+ def default?
64
+ @default
65
+ end
66
+
36
67
  def default_order
37
- @options.fetch(:default_order, "asc").to_s
68
+ @default_order
38
69
  end
39
70
 
40
71
  def classes
@@ -1,8 +1,24 @@
1
1
  module Trestle
2
2
  module StatusHelper
3
- def status_tag(label, status=:primary, options={})
4
- options[:class] ||= ["badge", "badge-#{status}"]
5
- content_tag(:span, label, options)
3
+ # Renders a status indicator as a Bootstrap badge.
4
+ # (https://getbootstrap.com/docs/5.3/components/badge/)
5
+ #
6
+ # label - Status badge text or HTML content
7
+ # status - Status class (as .badge-{status}) to apply to the badge (default: :primary)
8
+ # attributes - Additional HTML attributes to add to the <span> tag
9
+ #
10
+ # Examples
11
+ #
12
+ # <%= status_tag("Status Text") %>
13
+ #
14
+ # <%= status_tag(icon("fas fa-check"), :success) %>
15
+ #
16
+ # <%= status_tag(safe_join([icon("fas fa-warning"), "Error"], " "), :danger,
17
+ # data: { controller: "tooltip" }, title: "Full error message") %>
18
+ #
19
+ # Returns a HTML-safe String.
20
+ def status_tag(label, status=:primary, **attributes)
21
+ tag.span(label, **attributes.merge(class: ["badge", "badge-#{status}", attributes[:class]]))
6
22
  end
7
23
  end
8
24
  end
@@ -1,13 +1,34 @@
1
1
  module Trestle
2
2
  module TabHelper
3
- def tabs
4
- @_trestle_tabs ||= {}
5
- end
3
+ # Creates a tab pane using content via the given block, :partial option or partial
4
+ # template automatically inferred from the tab name.
5
+ #
6
+ # It also appends a Trestle::Tab object to the list of declared tabs that is
7
+ # accessible via the #tabs helper (e.g. for rendering the tab links).
8
+ #
9
+ # name - (Symbol) Internal name for the tab
10
+ # options - Hash of options (default: {}):
11
+ # :label - Optional tab label. If not provided, will be inferred by the
12
+ # admin-translated tab name (`admin.tabs.{name}` i18n scope)
13
+ # :badge - Optional badge to show next to the tab label (e.g. a counter)
14
+ # :partial - Optional partial template name to use when a block is not provided
15
+ #
16
+ # Examples
17
+ #
18
+ # <%= tab :details %>
19
+ # => Automatically renders the 'details' partial (e.g. "_details.html.erb") as the tab content
20
+ #
21
+ # <%= tab :metadata, partial: "meta" %>
22
+ # => Renders the 'meta' partial (e.g. "_meta.html.erb") as the tab content
23
+ #
24
+ # <%= tab :comments do %> ...
25
+ # => Renders the given block as the tab content
26
+ #
27
+ # Returns a HTML-safe String.
28
+ def tab(name, **options)
29
+ tabs[name] = tab = Tab.new(name, **options)
6
30
 
7
- def tab(name, options={})
8
- tabs[name] = tab = Tab.new(name, options)
9
-
10
- content_tag(:div, id: tab.id(("modal" if modal_request?)), class: ["tab-pane", ('active' if name == tabs.keys.first)], role: "tabpanel") do
31
+ tag.div(id: tab.id(("modal" if modal_request?)), class: ["tab-pane", ('active' if name == tabs.keys.first)], role: "tabpanel") do
11
32
  if block_given?
12
33
  yield
13
34
  elsif options[:partial]
@@ -17,5 +38,19 @@ module Trestle
17
38
  end
18
39
  end
19
40
  end
41
+
42
+ # Returns a hash (name => Trestle::Tab) of the currently declared tabs.
43
+ def tabs
44
+ @_trestle_tabs ||= {}
45
+ end
46
+
47
+ # Captures the given block (using `content_for`) as the sidebar content.
48
+ def sidebar(&block)
49
+ content_for(:sidebar, &block)
50
+ end
51
+
52
+ def render_sidebar_as_tab?
53
+ modal_request? && content_for?(:sidebar)
54
+ end
20
55
  end
21
56
  end
@@ -2,15 +2,17 @@ module Trestle
2
2
  module TableHelper
3
3
  # Renders an existing named table or builds and renders a custom table if a block is provided.
4
4
  #
5
- # name - The (optional) name of the table to render (as a Symbol), or the actual Trestle::Table instance itself.
6
- # options - Hash of options that will be passed to the table builder (default: {}):
7
- # :collection - The collection that should be rendered within the table. It should be an
8
- # Array-like object, but will most likely be an ActiveRecord scope. It can
9
- # also be a callable object (i.e. a Proc) in which case the result of calling
10
- # the block will be used as the collection.
11
- # See Trestle::Table::Builder for additional options.
12
- # block - An optional block that is passed to Trestle::Table::Builder to define a custom table.
13
- # One of either the name or block must be provided, but not both.
5
+ # One of either the name or block must be provided, but not both.
6
+ #
7
+ # name - The (optional) name of the table to render (as a Symbol), or the
8
+ # actual Trestle::Table instance itself
9
+ # collection - The collection that should be rendered within the table.
10
+ # It should be an Array-like object, but will most likely be an
11
+ # ActiveRecord scope. It can also be a callable object (i.e. a Proc) in
12
+ # which case the result of calling the block will be used as the collection
13
+ # options - Hash of options that will be passed to the table builder (default: {}).
14
+ # See Trestle::Table::Builder for additional options
15
+ # block - An optional block that is passed to Trestle::Table::Builder to define a custom table
14
16
  #
15
17
  # Examples
16
18
  #
@@ -23,26 +25,23 @@ module Trestle
23
25
  # <%= table :accounts %>
24
26
  #
25
27
  # Returns the HTML representation of the table as a HTML-safe String.
26
- def table(name=nil, options={}, &block)
28
+ def table(name=nil, collection: nil, **options, &block)
27
29
  if block_given?
28
- if name.is_a?(Hash)
29
- options = name
30
- else
31
- collection = name
32
- end
33
-
34
- table = Table::Builder.build(options, &block)
30
+ table = Table::Builder.build(**options, &block)
35
31
  else
36
32
  if name.is_a?(Trestle::Table)
37
33
  table = name
38
34
  else
39
- table = admin.tables.fetch(name) { raise ArgumentError, "Unable to find table named #{name.inspect}" }
35
+ table = admin.tables.fetch(name) {
36
+ raise ArgumentError, "Unable to find table named #{name.inspect}"
37
+ }
40
38
  end
41
39
 
42
- table = table.with_options(options.reverse_merge(sortable: false))
40
+ table = table.with_options(sortable: false, **options)
43
41
  end
44
42
 
45
- collection ||= options[:collection] || table.options[:collection]
43
+ collection ||= name if block_given?
44
+ collection ||= table.options[:collection]
46
45
  collection = collection.call if collection.respond_to?(:call)
47
46
 
48
47
  render "trestle/table/table", table: table, collection: collection
@@ -50,12 +49,13 @@ module Trestle
50
49
 
51
50
  # Renders the pagination controls for a collection.
52
51
  #
53
- # collection - The paginated Kaminari collection to render controls for (required).
54
- # options - Hash of options that will be passed to the Kaminari #paginate method (default: {}):
52
+ # collection - The paginated Kaminari collection to render controls for (required)
53
+ # entry_name - Custom item name passed to the Kaminari #page_entries_info helper
54
+ # options - Hash of options that will be passed to the Kaminari #paginate method (default: {})
55
55
  #
56
56
  # Examples
57
57
  #
58
- # <%= pagination collection: Account.page(params[:page]), remote: true %>
58
+ # <%= pagination collection: Account.page(params[:page]) %>
59
59
  #
60
60
  # Returns the HTML representation of the pagination controls as a HTML-safe String.
61
61
  def pagination(collection:, entry_name: nil, **options)
@@ -1,13 +1,13 @@
1
1
  module Trestle
2
2
  module TimestampHelper
3
- # Renders a Time object as a formatted timestamp (using a <time> tag)
3
+ # Renders a Time object as a formatted timestamp (using a <time> tag).
4
4
  #
5
- # time - The Time object to format.
6
- # options - Hash of options (default: {}):
7
- # :class - Additional HTML classes to add to the <time> tag.
8
- # :precision - Time precision, either :minutes or :seconds (default: :minutes).
9
- # :date_format - I18n date format to use for the date (default: :trestle_date).
10
- # :time_format - I18n time format to use for the time (default: :trestle_time).
5
+ # time - The Time object to format
6
+ # precision - Time precision, either :minutes or :seconds (default: :minutes)
7
+ # date_format - I18n date format to use for the date (default: :trestle_date)
8
+ # time_format - I18n time format to use for the time (default: :trestle_time,
9
+ # or :trestle_time_with_seconds if precision is :seconds)
10
+ # attributes - Additional HTML attributes to add to the <time> tag
11
11
  #
12
12
  # Examples
13
13
  #
@@ -16,28 +16,24 @@ module Trestle
16
16
  # <%= timestamp(Time.current, class: "timestamp-inline", precision: :seconds) %>
17
17
  #
18
18
  # Returns the HTML representation of the given Time.
19
- def timestamp(time, options={})
19
+ def timestamp(time, precision: Trestle.config.timestamp_precision, date_format: :trestle_date, time_format: nil, **attributes)
20
20
  return unless time
21
21
 
22
- classes = ["timestamp", options[:class]].compact
23
- precision = options.fetch(:precision) { Trestle.config.timestamp_precision }
24
- date_format = options.fetch(:date_format) { :trestle_date }
25
- time_format = options.fetch(:time_format) { precision == :seconds ? :trestle_time_with_seconds : :trestle_time }
22
+ time_format ||= (precision == :seconds) ? :trestle_time_with_seconds : :trestle_time
26
23
 
27
- time_tag(time, class: classes) do
24
+ time_tag(time, **attributes.merge(class: ["timestamp", attributes[:class]])) do
28
25
  safe_join([
29
- l(time, format: date_format, default: proc { |date| "#{date.day.ordinalize} %b %Y" }),
30
- content_tag(:small, l(time, format: time_format, default: "%l:%M:%S %p"))
26
+ tag.span(l(time, format: date_format, default: proc { |date| "#{date.day.ordinalize} %b %Y" })),
27
+ tag.small(l(time, format: time_format, default: "%l:%M:%S %p"))
31
28
  ], "\n")
32
29
  end
33
30
  end
34
31
 
35
- # Renders a Date object as formatted datestamp (using a <time> tag)
32
+ # Renders a Date object as formatted datestamp (using a <time> tag).
36
33
  #
37
- # date - The Date object to format.
38
- # options - Hash of options (default: {}):
39
- # :class - Additional HTML classes to add to the <time> tag.
40
- # :format - I18n date format to use (default: :trestle_calendar).
34
+ # date - The Date object to format
35
+ # format - I18n date format to use (default: :trestle_calendar)
36
+ # attributes - Additional HTML attributes to add to the <time> tag
41
37
  #
42
38
  # Examples
43
39
  #
@@ -46,13 +42,10 @@ module Trestle
46
42
  # <%= datestamp(article.created_at, format: :trestle_date, class: "custom-datestamp") %>
47
43
  #
48
44
  # Returns the HTML representation of the given Date.
49
- def datestamp(date, options={})
45
+ def datestamp(date, format: :trestle_calendar, **attributes)
50
46
  return unless date
51
47
 
52
- classes = ["datestamp", options[:class]].compact
53
- format = options.fetch(:format) { :trestle_calendar}
54
-
55
- time_tag(date, class: classes) do
48
+ time_tag(date, **attributes.merge(class: ["datestamp", attributes[:class]])) do
56
49
  l(date, format: format, default: "%-m/%-d/%Y")
57
50
  end
58
51
  end
@@ -1,5 +1,7 @@
1
1
  module Trestle
2
2
  module TitleHelper
3
+ # Returns the page title (if set using content_for), falling back to
4
+ # the titleized action name as a default if not set.
3
5
  def title
4
6
  content_for(:title) || default_title
5
7
  end
@@ -1,9 +1,10 @@
1
1
  module Trestle
2
+ # [Internal]
2
3
  module ToolbarsHelper
3
4
  def render_toolbar(toolbar, *args)
4
5
  result = toolbar.groups(self, *args).map do |items|
5
6
  if items.many?
6
- content_tag(:div, class: "btn-group", role: "group") do
7
+ tag.div(class: "btn-group", role: "group") do
7
8
  safe_join(items, "\n")
8
9
  end
9
10
  else
@@ -1,19 +1,20 @@
1
1
  module Trestle
2
2
  module Turbo
3
3
  module FrameHelper
4
- def index_turbo_frame(options={}, &block)
5
- defaults = {
6
- id: "index",
7
- data: {
8
- controller: "reloadable",
9
- turbo_action: "advance"
10
- }
11
- }
12
-
13
- content_tag("turbo-frame", defaults.merge(options), &block)
14
- end
15
-
16
- def resource_turbo_frame(instance, options={}, &block)
4
+ # Renders a <turbo-frame> container for an instance/resource view. A resource
5
+ # turbo frame sets its DOM id from the given instance and has a default target of
6
+ # "_top" (except for modal requests).
7
+ #
8
+ # attributes - Additional HTML attributes to add to the <turbo-frame> tag
9
+ #
10
+ # Examples
11
+ #
12
+ # <%= resource_turbo_frame(article) do %> ...
13
+ #
14
+ # <%= resource_turbo_frame(article, id: dom_id(article, "comment")) %> ...
15
+ #
16
+ # Returns a HTML-safe String.
17
+ def resource_turbo_frame(instance, **attributes, &block)
17
18
  defaults = {
18
19
  id: dom_id(instance),
19
20
  target: ("_top" unless modal_request?),
@@ -22,7 +23,17 @@ module Trestle
22
23
  }
23
24
  }
24
25
 
25
- content_tag("turbo-frame", defaults.merge(options), &block)
26
+ tag.turbo_frame(**defaults.merge(attributes), &block)
27
+ end
28
+
29
+ # [DEPRECATED]
30
+ #
31
+ # The #content turbo-frame found in app/views/trestle/application/_layout.html.erb
32
+ # is now used as a common top-level hook for the 'reloadable' Stimulus controller.
33
+ def index_turbo_frame(**attributes, &block)
34
+ Trestle.deprecator.warn("The index_turbo_frame helper is deprecated and will be removed in future versions of Trestle.")
35
+ yield
36
+ nil
26
37
  end
27
38
  end
28
39
  end