diff --git a/lib/prism/view/helpers.rb b/lib/prism/view/helpers.rb new file mode 100644 index 0000000..ec95c69 --- /dev/null +++ b/lib/prism/view/helpers.rb @@ -0,0 +1,26 @@ +require_relative 'tag_builder' +require_relative 'form_builder' +require_relative 'form_helpers' +require_relative 'url_helpers' +require_relative 'content_for_helpers' +require_relative 'cache_helpers' +require_relative 'pagination_helpers' +require_relative 'asset_helpers' + +module Prism + module ViewHelpers + include TagBuilder + include FormHelpers + include UrlHelpers + include ContentForHelpers + include CacheHelpers + include PaginationHelpers + include AssetHelpers + end + + module Template + class Context + include ViewHelpers + end + end +end diff --git a/lib/prism/view/pagination_helpers.rb b/lib/prism/view/pagination_helpers.rb new file mode 100644 index 0000000..33d0c10 --- /dev/null +++ b/lib/prism/view/pagination_helpers.rb @@ -0,0 +1,47 @@ +module Prism + # Renders page links for an `Orm::Relation` that has been through + # `.page(n)`. Deliberately decoupled from the router/request: the caller + # supplies `path` (a Proc mapping a page number to a URL string), so the + # helper works for `/posts?page=n`, `/blog/:id/comments?page=n`, or + # anything else without knowing about routes itself. + module PaginationHelpers + def paginate(relation, path:, window: 2) + return '' if relation.total_pages <= 1 + + links = +'' + links << pagination_nav_link('Previous', relation.prev_page, path) + pagination_page_numbers(relation, window).each do |page| + links << if page == :gap + content_tag(:span, '...', class: 'page-gap') + else + pagination_page_link(page, relation.current_page, path) + end + end + links << pagination_nav_link('Next', relation.next_page, path) + "#{tag(:nav, class: 'pagination')}#{links}" + end + + private + + def pagination_page_numbers(relation, window) + total = relation.total_pages + current = relation.current_page + visible = (1..total).select { |page| page == 1 || page == total || (page - current).abs <= window } + + visible.each_with_object([]) do |page, result| + result << :gap if result.any? && result.last != :gap && page - result.last != 1 + result << page + end + end + + def pagination_page_link(page, current_page, path) + classes = page == current_page ? 'page-link current' : 'page-link' + content_tag(:a, page.to_s, href: path.call(page), class: classes) + end + + def pagination_nav_link(text, target_page, path) + return content_tag(:span, text, class: 'page-link disabled') unless target_page + content_tag(:a, text, href: path.call(target_page), class: 'page-link') + end + end +end diff --git a/lib/prism/view/tag_builder.rb b/lib/prism/view/tag_builder.rb new file mode 100644 index 0000000..e4469cd --- /dev/null +++ b/lib/prism/view/tag_builder.rb @@ -0,0 +1,38 @@ +module Prism + # HTML tag construction shared by every other view helper. `tag` and the + # content-returning form of `content_tag` are meant to be used from a + # `<%== %>` template tag (they hand back an already-escaped string); + # the block form of `content_tag` is meant to be used from a plain + # `<% %>` tag and appends itself to the template's output buffer, same + # as `form_for` (see Prism::Template::Context#capture). + module TagBuilder + def tag(name, attributes = {}) + "<#{name}#{html_attributes(attributes)}>" + end + + def content_tag(name, content = nil, attributes = {}) + if block_given? + attributes = content if content.is_a?(Hash) + inner = capture { yield } + @__out << "<#{name}#{html_attributes(attributes)}>#{inner}" + nil + else + "<#{name}#{html_attributes(attributes)}>#{escape_html(content.to_s)}" + end + end + + def html_attributes(attributes) + return '' if attributes.nil? || attributes.empty? + attributes.filter_map { |key, value| html_attribute(key, value) }.join + end + + private + + def html_attribute(key, value) + return nil if value.nil? || value == false + name = key.to_s.tr('_', '-') + return %( #{name}="#{name}") if value == true + %( #{name}="#{escape_html(value.to_s)}") + end + end +end diff --git a/lib/prism/view/url_helpers.rb b/lib/prism/view/url_helpers.rb new file mode 100644 index 0000000..5b16c91 --- /dev/null +++ b/lib/prism/view/url_helpers.rb @@ -0,0 +1,17 @@ +module Prism + module UrlHelpers + def link_to(text, url, attributes = {}) + content_tag(:a, text, attributes.merge(href: url)) + end + + def button_to(text, url, method: :post, form: {}, **button_attrs) + verb = method.to_s.upcase + markup = +"
" + markup << hidden_method_field(verb) unless verb == 'POST' + markup << csrf_hidden_field + markup << content_tag(:button, text, { type: 'submit' }.merge(button_attrs)) + markup << '
' + markup + end + end +end