lesli_view 1.1.2 → 1.1.3

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/lesli_view/charts/bar.rb +10 -0
  3. data/lib/lesli_view/charts/general.html.erb +38 -52
  4. data/lib/lesli_view/charts/general.rb +69 -30
  5. data/lib/lesli_view/charts/line.rb +16 -0
  6. data/lib/lesli_view/components/header.html.erb +48 -23
  7. data/lib/lesli_view/components/header.rb +35 -2
  8. data/lib/lesli_view/components/toolbar.css +52 -0
  9. data/lib/lesli_view/components/toolbar.html.erb +36 -21
  10. data/lib/lesli_view/components/toolbar.rb +41 -4
  11. data/lib/lesli_view/elements/button.css +357 -0
  12. data/lib/lesli_view/elements/button.html.erb +15 -12
  13. data/lib/lesli_view/elements/button.rb +74 -34
  14. data/lib/lesli_view/elements/empty.html.erb +31 -6
  15. data/lib/lesli_view/elements/empty.rb +8 -1
  16. data/lib/lesli_view/elements/table-chart.html.erb +42 -0
  17. data/lib/lesli_view/elements/table.css +91 -0
  18. data/lib/lesli_view/elements/table.html.erb +24 -21
  19. data/lib/lesli_view/elements/table.rb +67 -17
  20. data/lib/lesli_view/forms/builder.rb +16 -3
  21. data/lib/lesli_view/forms/builder_horizontal.rb +34 -10
  22. data/lib/lesli_view/forms/fields.rb +103 -70
  23. data/lib/lesli_view/forms/fieldset.rb +3 -3
  24. data/lib/lesli_view/forms/form.css +171 -0
  25. data/lib/lesli_view/forms/form.scss +2 -54
  26. data/lib/lesli_view/forms/inputs.rb +27 -15
  27. data/lib/lesli_view/layout/container.html.erb +8 -2
  28. data/lib/lesli_view/version.rb +2 -2
  29. data/lib/lesli_view/widgets/calendar.html.erb +42 -133
  30. data/lib/lesli_view/widgets/calendar.rb +77 -3
  31. data/lib/lesli_view/widgets/chart.html.erb +25 -26
  32. data/lib/lesli_view/widgets/chart.rb +46 -3
  33. data/lib/lesli_view/widgets/count.html.erb +17 -15
  34. data/lib/lesli_view/widgets/count.rb +13 -1
  35. data/lib/lesli_view/widgets/date.html.erb +23 -21
  36. data/lib/lesli_view/widgets/date.rb +27 -2
  37. data/lib/lesli_view/widgets/weather.html.erb +40 -38
  38. data/lib/lesli_view/widgets/weather.rb +41 -2
  39. data/readme.md +158 -29
  40. metadata +6 -4
  41. data/lib/lesli_view/components/header.scss +0 -69
  42. data/lib/lesli_view/components/toolbar.scss +0 -54
  43. data/lib/lesli_view/elements/table.scss +0 -153
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57b0a11628317815d5fa6dad2de504adc413f303a84162d4d2761b07b515879c
4
- data.tar.gz: de041c2dbecf1fc19004151eb81f59d420812bec6f7fd3c9bf3be11978b0c6a0
3
+ metadata.gz: 9c372c2ae1e73b645cbac17d23b504506b5c0a530de4ad3b9af37ac3f19ce338
4
+ data.tar.gz: d296585f3691d21398b81803cb3c616233b74e313df210c50434ec0adbc12418
5
5
  SHA512:
6
- metadata.gz: d2d9b44c26f6dfb0f6ed8c67483809f5b30b8c44d73800d6516c7803127a034bf8c7ab9ed700823244bcf7ca10bb4fd063ff97c25f538767fde0833ee8de6666
7
- data.tar.gz: 3037e1d9a13c02d2032429d2322a88597be41effb626137c39780176b1b827a45605a94cbc7c694d5613f4231d492e93cb90f863819c276b23742f40f519ddae
6
+ metadata.gz: 906c140274216c77a9ff864dde9b2423919f947cadf4a02e0d327f5d5bd0c3bf9e97bacca7841f48256218a8f1394499ecb3febdd3936e3b665055ea378b3896
7
+ data.tar.gz: 7e914034819645b537b4b157eea35d847f4cb806ac544402cbe099be80dfda12ec4e2fa593bf0813d397df0ba412daa33c4624fcc6c9333304f95096b749b009
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  =begin
2
4
 
3
5
  Lesli
@@ -36,6 +38,14 @@ module LesliView
36
38
  def type
37
39
  "bar"
38
40
  end
41
+
42
+ def dataset_defaults
43
+ {
44
+ backgroundColor: "rgba(14, 165, 233, 0.72)",
45
+ borderColor: "rgb(2, 132, 199)",
46
+ borderWidth: 1
47
+ }
48
+ end
39
49
  end
40
50
  end
41
51
  end
@@ -1,55 +1,41 @@
1
- <%#
2
-
3
- Lesli
4
-
5
- Copyright (c) 2026, Lesli Technologies, S. A.
6
-
7
- This program is free software: you can redistribute it and/or modify
8
- it under the terms of the GNU General Public License as published by
9
- the Free Software Foundation, either version 3 of the License, or
10
- (at your option) any later version.
11
-
12
- This program is distributed in the hope that it will be useful,
13
- but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
- GNU General Public License for more details.
16
-
17
- You should have received a copy of the GNU General Public License
18
- along with this program. If not, see http://www.gnu.org/licenses/.
19
-
20
- Lesli · Ruby on Rails SaaS Development Framework.
21
-
22
- Made with ♥ by LesliTech
23
- Building a better future, one line of code at a time.
24
-
25
- @contact hello@lesli.tech
26
- @website https://www.lesli.tech
27
- @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
-
29
- // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
- // ·
31
- %>
32
- <script>
33
- document.addEventListener("turbo:load", () => {
34
- new LesliChart({
35
- id: "<%= id %>",
36
- type: "<%= type %>",
37
- datasets: <%= raw(datasets.to_json) %>,
38
- labels: <%= raw(labels.to_json) %>,
39
- compact: <%= compact ? "true" : "false" %>
40
- });
41
- });
42
- </script>
43
-
44
- <div class="lesli-charts lesli-charts-<%= type %> section box m-0 p-2">
45
-
46
- <% if title.present? %>
47
- <%= tag.h3(title, class: (compact ? "is-size-7 has-text-centered" : "ml-2")) %>
1
+ <div
2
+ class="lesli-chart lesli-chart-<%= type %> flex h-full min-w-0 flex-col"
3
+ <% if title.present? %>aria-labelledby="<%= title_id %>"<% end %>>
4
+
5
+ <% if title.present? || subtitle.present? %>
6
+ <div class="mb-3">
7
+ <% if title.present? %>
8
+ <h3
9
+ id="<%= title_id %>"
10
+ class="<%= compact ? 'text-center text-xs font-medium text-slate-500' : 'text-base font-semibold text-slate-700' %>">
11
+ <%= title %>
12
+ </h3>
13
+ <% end %>
14
+
15
+ <% if subtitle.present? %>
16
+ <p class="mt-1 text-sm leading-5 text-slate-500"><%= subtitle %></p>
17
+ <% end %>
18
+ </div>
48
19
  <% end %>
49
20
 
50
- <% if subtitle.present? %>
51
- <p class="ml-2"><b><%= subtitle %></b></p>
52
- <% end %>
53
-
54
- <div id="<%= id %>" style="min-height:<%= height %>"></div>
21
+ <div
22
+ id="<%= id %>"
23
+ class="min-w-0 flex-1"
24
+ style="min-height: <%= height %>"></div>
55
25
  </div>
26
+
27
+ <script data-lesli-chart-config="<%= chart_config.to_json %>">
28
+ (() => {
29
+ const script = document.currentScript;
30
+ const renderChart = () => {
31
+ if (!window.LesliChart || !script) return;
32
+ new window.LesliChart(JSON.parse(script.dataset.lesliChartConfig));
33
+ };
34
+
35
+ if (document.readyState === "loading") {
36
+ document.addEventListener("turbo:load", renderChart, { once: true });
37
+ } else {
38
+ renderChart();
39
+ }
40
+ })();
41
+ </script>
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  =begin
2
4
 
3
5
  Lesli
@@ -33,48 +35,62 @@ Building a better future, one line of code at a time.
33
35
  module LesliView
34
36
  module Charts
35
37
  class General < ViewComponent::Base
36
-
37
38
  attr_reader(
38
- :id,
39
- :title,
40
- :subtitle,
41
- :labels,
42
- :datasets,
43
- :height,
39
+ :id,
40
+ :title,
41
+ :subtitle,
42
+ :labels,
43
+ :datasets,
44
+ :height,
44
45
  :compact
45
46
  )
46
47
 
47
48
  def initialize(
48
- id:nil,
49
- title:nil,
50
- subtitle:nil,
51
- labels:nil,
52
- dataset:nil,
53
- datasets:nil,
54
- database_to_dataset:nil,
55
- database_to_datasets:nil,
56
- height:"400px",
57
- compact:false
49
+ id: nil,
50
+ title: nil,
51
+ subtitle: nil,
52
+ labels: nil,
53
+ dataset: nil,
54
+ datasets: nil,
55
+ database_to_dataset: nil,
56
+ database_to_datasets: nil,
57
+ height: "400px",
58
+ compact: false
58
59
  )
59
- @id = id || "lesli-chart-#{rand(1000)}"
60
+ @id = id.presence || "lesli-chart-#{object_id}"
60
61
  @title = title
61
62
  @subtitle = subtitle
62
- @labels = labels
63
- @datasets = datasets
64
- @height = height
65
- @compact = compact
66
-
67
- @datasets = [{ name: title, data: dataset }] if dataset
68
- @datasets = database_to_dataset(database_to_dataset) if database_to_dataset
69
- @datasets = database_to_datasets(database_to_datasets) if database_to_datasets
63
+ @labels = Array(labels)
64
+ @height = normalize_height(height)
65
+ @compact = !!compact
66
+
67
+ source_datasets = datasets
68
+ source_datasets = [{ label: title, data: dataset }] unless dataset.nil?
69
+ source_datasets = convert_database_to_dataset(database_to_dataset) if database_to_dataset
70
+ source_datasets = convert_database_to_datasets(database_to_datasets) if database_to_datasets
71
+ @datasets = normalize_datasets(source_datasets)
70
72
  end
71
73
 
72
74
  def type
73
75
  nil
74
76
  end
75
77
 
76
- def database_to_dataset(data)
77
- [{ data: data.map { |d| { x: d['xaxiskey'], y: d['yaxiskey'] }}}]
78
+ def chart_config
79
+ {
80
+ id: id,
81
+ type: type,
82
+ datasets: datasets,
83
+ labels: labels,
84
+ compact: compact
85
+ }
86
+ end
87
+
88
+ def title_id
89
+ @title_id ||= "#{id}-title"
90
+ end
91
+
92
+ def convert_database_to_dataset(data)
93
+ [{ label: title, data: data.map { |record| { x: record["xaxiskey"], y: record["yaxiskey"] } } }]
78
94
  end
79
95
 
80
96
  # Transforms a database query result into a structure compatible with Chart.js
@@ -94,8 +110,7 @@ module LesliView
94
110
  # 'sum(agent_count) as data'
95
111
  # )
96
112
  #
97
- def database_to_datasets(data)
98
-
113
+ def convert_database_to_datasets(data)
99
114
  # Group records by series name
100
115
  # Example: group all "Chrome" rows together
101
116
  data
@@ -118,6 +133,30 @@ module LesliView
118
133
  }
119
134
  end
120
135
  end
136
+
137
+ # Preserve the original public conversion helpers.
138
+ alias_method :database_to_dataset, :convert_database_to_dataset
139
+ alias_method :database_to_datasets, :convert_database_to_datasets
140
+
141
+ def dataset_defaults
142
+ {}
143
+ end
144
+
145
+ private
146
+
147
+ def normalize_datasets(value)
148
+ Array(value).map do |dataset|
149
+ attributes = dataset.respond_to?(:to_h) ? dataset.to_h.symbolize_keys : { data: Array(dataset) }
150
+ dataset_defaults.merge(attributes)
151
+ end
152
+ end
153
+
154
+ def normalize_height(value)
155
+ value = value.to_s
156
+ return value if value.match?(/\A\d+(?:\.\d+)?(?:px|rem|em|vh|vw|%)\z/)
157
+
158
+ "400px"
159
+ end
121
160
  end
122
161
  end
123
162
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  =begin
2
4
 
3
5
  Lesli
@@ -36,6 +38,20 @@ module LesliView
36
38
  def type
37
39
  "line"
38
40
  end
41
+
42
+ def dataset_defaults
43
+ {
44
+ backgroundColor: "rgba(14, 165, 233, 0.12)",
45
+ borderColor: "rgb(2, 132, 199)",
46
+ borderWidth: 2,
47
+ fill: true,
48
+ pointBackgroundColor: "rgb(2, 132, 199)",
49
+ pointBorderColor: "rgb(255, 255, 255)",
50
+ pointBorderWidth: 2,
51
+ pointRadius: 3,
52
+ pointHoverRadius: 5
53
+ }
54
+ end
39
55
  end
40
56
  end
41
57
  end
@@ -1,29 +1,54 @@
1
- <section class="lesli-element-header is-flex">
2
- <div class="lesli-element-header-title">
3
- <div class="is-flex">
4
- <% if back %>
1
+
2
+ <section
3
+ class="mx-auto my-8 flex flex-col gap-5 sm:flex-row sm:items-start sm:justify-between"
4
+ <% if title.present? %>aria-labelledby="<%= title_id %>"<% end %>>
5
+
6
+ <div class="min-w-0">
7
+ <div class="flex items-start gap-4">
8
+
9
+ <% if back? %>
5
10
  <%= link_to(
6
- back || request.referer || root_path,
7
- class: "button is-info is-outlined mr-4 mb-1",
8
- data: { turbo_frame: '_top' }) do %>
9
- <span class="material-symbols">arrow_back</span>
11
+ back_path,
12
+ class: "mt-1 inline-flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl border border-sky-600 bg-white text-sky-700 shadow-sm transition hover:bg-sky-50 hover:text-sky-800 focus:outline-none focus:ring-2 focus:ring-sky-500/30",
13
+ aria: { label: I18n.t("actions.back", default: "Go back") },
14
+ data: { turbo_frame: "_top" }
15
+ ) do %>
16
+ <span class="material-symbols-rounded text-[26px] leading-none" aria-hidden="true">arrow_back</span>
10
17
  <% end %>
11
18
  <% end %>
12
- <% if title.present? %>
13
- <h1 class="subtitle is-size-3 mb-1"><%= title %></h1>
14
- <% end %>
19
+
20
+ <div class="min-w-0">
21
+ <% if title.present? %>
22
+ <h1 id="<%= title_id %>" class="text-3xl tracking-tight text-slate-700 sm:text-4xl">
23
+ <%= title %>
24
+ </h1>
25
+ <% end %>
26
+
27
+ <% if subtitle.present? %>
28
+ <p class="mt-2 max-w-2xl text-sm leading-6 text-slate-500 sm:text-base">
29
+ <%= subtitle %>
30
+ </p>
31
+ <% end %>
32
+ </div>
33
+
15
34
  </div>
16
- <% if subtitle.present? %>
17
- <p><%= subtitle %></p>
18
- <% end %>
19
- </div>
20
- <div class="lesli-element-header-items is-flex is-flex-grow-1 is-justify-content-end">
21
- <% if new_path %>
22
- <%= render ::LesliView::Elements::Button.new(
23
- "Nuevo", url:new_path, icon:"add", solid:true
24
- ) %>
25
- <% end %>
26
- <%= content %>
27
35
  </div>
28
- </section>
29
36
 
37
+ <% if actions? %>
38
+ <div
39
+ class="flex shrink-0 flex-wrap items-center gap-3 sm:justify-end"
40
+ role="group"
41
+ aria-label="<%= I18n.t('actions.label', default: 'Page actions') %>">
42
+ <% if new_path.present? %>
43
+ <%= render ::LesliView::Elements::Button.new(
44
+ new_button_label,
45
+ url: new_path,
46
+ icon: "add",
47
+ solid: true
48
+ ) %>
49
+ <% end %>
50
+
51
+ <%= content if content? %>
52
+ </div>
53
+ <% end %>
54
+ </section>
@@ -35,13 +35,46 @@ Building a better future, one line of code at a time.
35
35
  module LesliView
36
36
  module Components
37
37
  class Header < ViewComponent::Base
38
- attr_reader :title, :subtitle, :back, :new_path
38
+ attr_reader :title, :subtitle, :back, :new_path, :new_label
39
+
40
+ def initialize(title, subtitle = nil, new_path: nil, new_label: nil, back: nil)
41
+ # Support the legacy positional options hash:
42
+ # Header.new("Title", back: path)
43
+ if subtitle.is_a?(Hash)
44
+ legacy_options = subtitle.symbolize_keys
45
+ subtitle = legacy_options[:subtitle]
46
+ new_path ||= legacy_options[:new_path]
47
+ new_label ||= legacy_options[:new_label]
48
+ back ||= legacy_options[:back]
49
+ end
39
50
 
40
- def initialize(title, subtitle="", new_path:nil, back:nil)
41
51
  @title = title
42
52
  @subtitle = subtitle
43
53
  @back = back
44
54
  @new_path = new_path
55
+ @new_label = new_label
56
+ end
57
+
58
+ def back?
59
+ back.present?
60
+ end
61
+
62
+ def back_path
63
+ return back unless back == true
64
+
65
+ request.referer.presence || "/"
66
+ end
67
+
68
+ def new_button_label
69
+ new_label.presence || I18n.t("actions.new", default: "New")
70
+ end
71
+
72
+ def actions?
73
+ new_path.present? || content?
74
+ end
75
+
76
+ def title_id
77
+ @title_id ||= "lesli-header-title-#{object_id}"
45
78
  end
46
79
  end
47
80
  end
@@ -0,0 +1,52 @@
1
+ /* Tailwind styles for LesliView::Components::Toolbar. */
2
+
3
+ @layer components {
4
+ .lesli-toolbar {
5
+ @apply mb-6 flex w-full flex-col gap-2 sm:flex-row sm:items-center;
6
+ }
7
+
8
+ .lesli-toolbar__form {
9
+ @apply flex min-w-0 flex-1 flex-col gap-2 sm:flex-row sm:items-center;
10
+ }
11
+
12
+ .lesli-toolbar__search {
13
+ @apply relative min-w-0 flex-1;
14
+ }
15
+
16
+ .lesli-toolbar__input {
17
+ @apply h-10 w-full rounded-lg border border-slate-300 bg-white py-2 pl-10 pr-10 text-sm text-slate-900 outline-none transition placeholder:text-slate-400 hover:border-slate-400 focus:border-sky-500 focus:ring-2 focus:ring-sky-500/10;
18
+ }
19
+
20
+ .lesli-toolbar__search-icon {
21
+ @apply pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-lg leading-none text-slate-400;
22
+ }
23
+
24
+ .lesli-toolbar__clear {
25
+ @apply absolute right-1.5 top-1/2 inline-flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded-md text-slate-400 transition hover:bg-slate-100 hover:text-slate-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500/30;
26
+ }
27
+
28
+ .lesli-toolbar__clear .material-symbols {
29
+ @apply text-lg leading-none;
30
+ }
31
+
32
+ .lesli-toolbar__filters {
33
+ @apply flex flex-wrap items-center gap-2;
34
+ }
35
+
36
+ .lesli-toolbar__filters select,
37
+ .lesli-toolbar__filters input {
38
+ @apply h-10 rounded-lg border border-slate-300 bg-white px-3 text-sm text-slate-700 outline-none transition hover:border-slate-400 focus:border-sky-500 focus:ring-2 focus:ring-sky-500/10;
39
+ }
40
+
41
+ .lesli-toolbar__submit {
42
+ @apply inline-flex h-10 w-10 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-slate-300 bg-white text-slate-500 transition hover:border-sky-300 hover:bg-sky-50 hover:text-sky-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500/20;
43
+ }
44
+
45
+ .lesli-toolbar__submit .material-symbols {
46
+ @apply text-xl leading-none;
47
+ }
48
+
49
+ .lesli-toolbar__actions {
50
+ @apply flex shrink-0 flex-wrap items-center gap-2;
51
+ }
52
+ }
@@ -1,25 +1,40 @@
1
- <section class="lesli-toolbar mb-5">
2
- <div class="field is-grouped">
3
- <div class="control is-expanded has-icons-right has-icons-left">
4
- <%= text_field_tag(:search, initial_value,
5
- placeholder: search_placeholder,
6
- class: "input is-shadowless",
7
- data: { action: "input->toolbar#search" }
8
- )%>
9
- <span class="icon is-small is-left has-text-grey-light">
10
- <span class="material-symbols">
11
- search
12
- </span>
13
- </span>
14
- <span class="icon is-right">
15
- <a class="delete" data-action="click->toolbar#clearSearch"></a>
16
- </span>
1
+ <section class="lesli-toolbar" aria-label="<%= search_label %>">
2
+ <%= form_with(url: form_url, method: :get, local: true, class: "lesli-toolbar__form", role: "search") do %>
3
+ <div class="lesli-toolbar__search">
4
+ <%= label_tag(search_name, search_label, class: "sr-only") %>
5
+
6
+ <span class="lesli-toolbar__search-icon material-symbols" aria-hidden="true">search</span>
7
+
8
+ <%= text_field_tag(
9
+ search_name,
10
+ search_value,
11
+ placeholder: placeholder,
12
+ class: "lesli-toolbar__input",
13
+ autocomplete: "off"
14
+ ) %>
15
+
16
+ <% if searching? %>
17
+ <%= link_to(clear_url, class: "lesli-toolbar__clear", aria: { label: clear_label }, title: clear_label) do %>
18
+ <span class="material-symbols" aria-hidden="true">close</span>
19
+ <% end %>
20
+ <% end %>
17
21
  </div>
18
- <%= content %>
19
- <% if pagination %>
20
- <div class="select">
21
- <%= select_tag :pagination, options_for_select([10, 20]), class: "select" %>
22
+
23
+ <% if filters? %>
24
+ <div class="lesli-toolbar__filters">
25
+ <%= filters %>
22
26
  </div>
23
27
  <% end %>
24
- </div>
28
+
29
+ <button class="lesli-toolbar__submit" type="submit">
30
+ <span class="material-symbols" aria-hidden="true">search</span>
31
+ <span class="sr-only"><%= search_label %></span>
32
+ </button>
33
+ <% end %>
34
+
35
+ <% if actions? %>
36
+ <div class="lesli-toolbar__actions" role="group" aria-label="<%= I18n.t('lesli_view.toolbar.actions', default: 'Toolbar actions') %>">
37
+ <%= actions %>
38
+ </div>
39
+ <% end %>
25
40
  </section>
@@ -35,12 +35,49 @@ Building a better future, one line of code at a time.
35
35
  module LesliView
36
36
  module Components
37
37
  class Toolbar < ViewComponent::Base
38
- attr_reader :search_placeholder, :initial_value, :pagination
39
-
40
- def initialize(search_placeholder = "Search...", initial_value: nil, pagination: true)
38
+ renders_one :filters
39
+ renders_one :actions
40
+
41
+ attr_reader :search_placeholder, :initial_value, :url, :search_name
42
+
43
+ def initialize(search_placeholder = nil, initial_value: nil, url: nil, search_name: :search)
41
44
  @search_placeholder = search_placeholder
42
45
  @initial_value = initial_value
43
- @pagination = pagination
46
+ @url = url
47
+ @search_name = search_name
48
+ end
49
+
50
+ def form_url
51
+ url.presence || request.path
52
+ end
53
+
54
+ def search_value
55
+ return initial_value unless initial_value.nil?
56
+
57
+ request.query_parameters[search_name.to_s]
58
+ end
59
+
60
+ def searching?
61
+ search_value.present?
62
+ end
63
+
64
+ def clear_url
65
+ query = request.query_parameters.except(search_name.to_s)
66
+ return form_url if query.empty?
67
+
68
+ "#{form_url}?#{query.to_query}"
69
+ end
70
+
71
+ def placeholder
72
+ search_placeholder.presence || I18n.t("lesli_view.toolbar.placeholder", default: "Search...")
73
+ end
74
+
75
+ def search_label
76
+ I18n.t("lesli_view.toolbar.search", default: "Search")
77
+ end
78
+
79
+ def clear_label
80
+ I18n.t("lesli_view.toolbar.clear", default: "Clear search")
44
81
  end
45
82
  end
46
83
  end