repolho 0.1.1 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9efc21c49b8e192f0a25b4fd4565e3c503ac68fc3a392e06702490707015ddd
4
- data.tar.gz: 46d1538f52bffb72d9cbdbe65cd698bbc68665aceb106a377c35e834866bb5e2
3
+ metadata.gz: 3e44c3e92b2bf34c0785294e65149968cc4f8ce0c5dd1eabd4eebdb9f200340e
4
+ data.tar.gz: 4ec8fc094c8da9d07d1cac32ef07ab41908c47cb95419db424c4d8a1c9dc611a
5
5
  SHA512:
6
- metadata.gz: a6d7d7cd30223e35a25f0d5b67c25d66466cb3c1ea472795d5b440ea3193542df3b853842988aa9cde1e776cff1b17fd9c4974a19363866d5eabbe9ac453ab63
7
- data.tar.gz: 4f881bf9c547e9b947829e1256546f6512eff796573e23026294a507e2e6e24e3b7956b52b47efe781556fe4e81ca9dd922e60b844fda5fe91e7e1accc01eedf
6
+ metadata.gz: b422a100451181e9b6bf1f96cf1fa4255be8c854677e6cb214f1149a175f464990d3b3064e3512c682cad13a8d63f191d347cac92850c5ad62bed1e458584bd9
7
+ data.tar.gz: f73bc63146708f0ec37c71f5e402c39c0eadec437d6a70f784e23e1bd5b36d46f98b3b011a01f09f995ed8eca3f06ce3cd8aebfa927fe9925c16ac9ce1532fd5
checksums.yaml.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -58,7 +58,7 @@ sections). Everything inside the custom elements is processed as ERB.
58
58
 
59
59
  | Element | Purpose | Attributes |
60
60
  |---------|---------|------------|
61
- | `<report>` | Root container of the report | — |
61
+ | `<report>` | Root container of the report | `pagination` (optional, `"true"`): enables client-side page navigation. Also accepts `pagination-first`, `pagination-prev`, `pagination-next`, `pagination-last` for custom labels. |
62
62
  | `<report-page>` | Defines a page (required, only one) | `rows` (required): max data rows per page |
63
63
  | `<report-group>` | Nested grouping | `id` (required), `attr` (required), `reprint-header-on-page-break` (optional, `"true"`) |
64
64
  | `<report-header>` | Header of the current container | — |
@@ -95,6 +95,56 @@ Repolho automatically wraps each page like this:
95
95
  The `data-page` attribute and the `break-after` style are injected for you.
96
96
  You do not need to write the `<tbody>` yourself.
97
97
 
98
+ ### Client-side pagination
99
+
100
+ If you set `pagination="true"` on the root `<report>` element, Repolho
101
+ appends a navigation bar just before the `after` section (still inside
102
+ the table) and a small pure-ES6 script at the end of the document
103
+ (inserted before `</body>` when present, otherwise appended). The bar
104
+ is omitted when the report has only one page.
105
+
106
+ Optional attributes let you customize the labels:
107
+
108
+ | Attribute | Default | Description |
109
+ |-----------|---------|-------------|
110
+ | `pagination-first` | `First` | First page link text |
111
+ | `pagination-prev` | `<` | Previous page link text |
112
+ | `pagination-next` | `>` | Next page link text |
113
+ | `pagination-last` | `Last` | Last page link text |
114
+
115
+ ```html
116
+ <report pagination="true"
117
+ pagination-first="Primeira"
118
+ pagination-prev="«"
119
+ pagination-next="»"
120
+ pagination-last="Última">
121
+ ...
122
+ </report>
123
+ ```
124
+
125
+ The generated controls look like:
126
+
127
+ ```html
128
+ <tbody id="report-pagination" data-max="N">
129
+ <tr>
130
+ <td colspan="200" id="report-pagination-links">
131
+ <a href="#" id="report-first-page">First</a>
132
+ <a href="#" id="report-prev-page"><</a>
133
+ <input type="number" id="report-current-page" min="1" max="N" value="1"/>
134
+ <a href="#" id="report-next-page">></a>
135
+ <a href="#" id="report-last-page">Last</a>
136
+ </td>
137
+ </tr>
138
+ </tbody>
139
+ ```
140
+
141
+ You can restyle the links and the input with normal CSS. The script is
142
+ wrapped in try/catch so a failure does not break the rest of the page.
143
+
144
+ When pagination is enabled, `page_number` (available in ERB) holds the
145
+ total number of pages after the report has been fully generated.
146
+
147
+
98
148
  ### Template example
99
149
 
100
150
  ```erb
data/lib/repolho/main.rb CHANGED
@@ -6,19 +6,83 @@ require_relative "report"
6
6
  require_relative "stats"
7
7
  require "nokogiri"
8
8
  require "erb"
9
+ require "cgi"
9
10
 
10
11
  module Repolho
11
12
  class Main
13
+ PAGINATION_TBODY = <<~HTML
14
+ <tbody id="report-pagination" data-max="<%= page_number %>">
15
+ <tr>
16
+ <td colspan="200" id="report-pagination-links">
17
+ <a href="#" id="report-first-page"><%= @pagination_first %></a>
18
+ <a href="#" id="report-prev-page"><%= @pagination_prev %></a>
19
+ <input type="number" id="report-current-page" min="1" max="<%= page_number %>" value="1"/>
20
+ <a href="#" id="report-next-page"><%= @pagination_next %></a>
21
+ <a href="#" id="report-last-page"><%= @pagination_last %></a>
22
+ </td>
23
+ </tr>
24
+ </tbody>
25
+ HTML
26
+
27
+ PAGINATION_SCRIPT = <<~HTML
28
+ <script>
29
+ function change_report_page(page) {
30
+ try {
31
+ const pages = document.querySelectorAll(".repolho-page");
32
+ page = parseInt(page, 10);
33
+ if (isNaN(page) || page < 1) page = 1;
34
+ if (page > pages.length) page = pages.length;
35
+
36
+ pages.forEach(function(p) { p.style.display = "none"; });
37
+ var target = document.querySelector('[data-page="' + page + '"]');
38
+ if (target) target.style.display = "";
39
+
40
+ var input = document.getElementById("report-current-page");
41
+ if (input) input.value = page;
42
+ } catch (e) {
43
+ console.error("Repolho pagination error:", e);
44
+ }
45
+ }
46
+
47
+ document.addEventListener("DOMContentLoaded", function() {
48
+ try {
49
+ var first = document.getElementById("report-first-page");
50
+ var prev = document.getElementById("report-prev-page");
51
+ var next = document.getElementById("report-next-page");
52
+ var last = document.getElementById("report-last-page");
53
+ var input = document.getElementById("report-current-page");
54
+
55
+ if (first) first.addEventListener("click", function(e) { e.preventDefault(); change_report_page(1); });
56
+ if (prev) prev.addEventListener("click", function(e) { e.preventDefault(); change_report_page(parseInt(input && input.value || 1, 10) - 1); });
57
+ if (next) next.addEventListener("click", function(e) { e.preventDefault(); change_report_page(parseInt(input && input.value || 1, 10) + 1); });
58
+ if (last) last.addEventListener("click", function(e) { e.preventDefault(); change_report_page(9999); });
59
+ if (input) input.addEventListener("change", function(e) { change_report_page(e.target.value); });
60
+
61
+ change_report_page(1);
62
+ } catch (e) {
63
+ console.error("Repolho pagination init error:", e);
64
+ }
65
+ });
66
+ </script>
67
+ HTML
68
+
12
69
  def initialize(path, data, locals = {})
13
- @path = path
14
- @data = normalize_data(data)
15
- @locals = locals
16
- @columns = make_columns
17
- @page = Page.new("page", locals, make_stats)
18
- @report = Report.new("report", locals, make_stats)
19
- @previous = ""
20
- @after = ""
21
- @body = ""
70
+ @path = path
71
+ @data = normalize_data(data)
72
+ @locals = locals
73
+ @columns = make_columns
74
+ @page = Page.new("page", locals, make_stats)
75
+ @report = Report.new("report", locals, make_stats)
76
+ @previous = ""
77
+ @after = ""
78
+ @body = ""
79
+ @pagination = false
80
+ @pagination_labels = {
81
+ first: "First",
82
+ prev: "<",
83
+ next: ">",
84
+ last: "Last"
85
+ }
22
86
 
23
87
  @report.containers << @page
24
88
  @report.page = @page
@@ -39,12 +103,39 @@ module Repolho
39
103
  end
40
104
 
41
105
  @report.call(:finish, nil)
42
- @page.append_raw(render_erb(@after))
43
106
  @page.finalize
107
+
108
+ # After finalize, page_number is one past the last page.
109
+ # Set it to the real total so ERB (pagination + @after) sees the correct value.
110
+ total_pages = [@page.page_number - 1, 1].max
111
+ @page.instance_variable_set(:@page_number, total_pages)
112
+
113
+ after_content = render_erb(@after)
114
+
115
+ if @pagination && total_pages > 1
116
+ inject_pagination_labels
117
+ @page.append_raw(render_erb(PAGINATION_TBODY))
118
+
119
+ script = PAGINATION_SCRIPT
120
+ if after_content =~ /<\/body>/i
121
+ after_content = after_content.sub(/<\/body>/i, "#{script}</body>")
122
+ else
123
+ after_content += script
124
+ end
125
+ end
126
+
127
+ @page.append_raw(after_content)
44
128
  end
45
129
 
46
130
  private
47
131
 
132
+ def inject_pagination_labels
133
+ @page.instance_variable_set(:@pagination_first, @pagination_labels[:first])
134
+ @page.instance_variable_set(:@pagination_prev, @pagination_labels[:prev])
135
+ @page.instance_variable_set(:@pagination_next, @pagination_labels[:next])
136
+ @page.instance_variable_set(:@pagination_last, @pagination_labels[:last])
137
+ end
138
+
48
139
  def make_columns
49
140
  return [] if @data.empty?
50
141
 
@@ -94,11 +185,11 @@ module Repolho
94
185
  end
95
186
 
96
187
  def extract_sections(content)
97
- matches = content.gsub(/\n/, "").match(/(?<previous>\A.*?)(?<body><body[^>]*>.*?)(?<report><report>.*?<\/report>)(?<after>.*)\z/m)
188
+ matches = content.gsub(/\n/, "").match(/(?<previous>\A.*?)(?<body><body[^>]*>.*?)(?<report><report[^>]*>.*?<\/report>)(?<after>.*)\z/m)
98
189
 
99
- @body = matches[:body]
100
- @previous = matches[:previous]
101
- @after = matches[:after]
190
+ @body = matches[:body]
191
+ @previous = matches[:previous]
192
+ @after = matches[:after]
102
193
  @report_xml = matches[:report]
103
194
  end
104
195
 
@@ -106,6 +197,19 @@ module Repolho
106
197
  parsed = Nokogiri::HTML.parse(@report_xml)
107
198
  report = parsed.search("report")
108
199
 
200
+ report_node = report.first
201
+ @pagination = report_node["pagination"] == "true"
202
+
203
+ if @pagination
204
+ @pagination_labels[:first] = report_node["pagination-first"] if report_node["pagination-first"]
205
+ @pagination_labels[:prev] = report_node["pagination-prev"] if report_node["pagination-prev"]
206
+ @pagination_labels[:next] = report_node["pagination-next"] if report_node["pagination-next"]
207
+ @pagination_labels[:last] = report_node["pagination-last"] if report_node["pagination-last"]
208
+
209
+ # Escape for safe HTML output
210
+ @pagination_labels.transform_values! { |v| CGI.escapeHTML(v.to_s) }
211
+ end
212
+
109
213
  @report.header = info(report.search("> report-header").first)
110
214
  @report.footer = info(report.search("> report-footer").first)
111
215
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Repolho
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
5
5
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,11 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: repolho
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eustáquio Rangel
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain:
11
10
  - |
@@ -36,7 +35,7 @@ cert_chain:
36
35
  L0XcRn7yJijWxXFMBrnwp/4PHjPT0l6Qci+zxGly0D7LCTHa5WSyWujq4BZw55Yp
37
36
  G13dwkTphaMB
38
37
  -----END CERTIFICATE-----
39
- date: 2026-10-21 00:00:00.000000000 Z
38
+ date: 1980-01-02 00:00:00.000000000 Z
40
39
  dependencies:
41
40
  - !ruby/object:Gem::Dependency
42
41
  name: nokogiri
@@ -77,7 +76,6 @@ licenses:
77
76
  metadata:
78
77
  homepage_uri: https://github.com/taq/repolho
79
78
  source_code_uri: https://github.com/taq/repolho
80
- post_install_message:
81
79
  rdoc_options: []
82
80
  require_paths:
83
81
  - lib
@@ -92,8 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
90
  - !ruby/object:Gem::Version
93
91
  version: '0'
94
92
  requirements: []
95
- rubygems_version: 3.5.3
96
- signing_key:
93
+ rubygems_version: 4.0.20
97
94
  specification_version: 4
98
95
  summary: Report generator for Ruby and Rails from .rep.erb templates
99
96
  test_files: []
metadata.gz.sig CHANGED
Binary file