mensa 0.3.1 → 0.3.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -2
  3. data/Gemfile.lock +2 -34
  4. data/README.md +18 -44
  5. data/app/components/mensa/add_filter/component.html.erb +31 -0
  6. data/app/components/mensa/add_filter/component_controller.js +0 -1
  7. data/app/components/mensa/application_component.rb +0 -1
  8. data/app/components/mensa/cell/component.html.erb +1 -0
  9. data/app/components/mensa/column_customizer/component.html.erb +31 -0
  10. data/app/components/mensa/control_bar/component.html.erb +56 -0
  11. data/app/components/mensa/empty_state/component.html.erb +10 -0
  12. data/app/components/mensa/filter_pill/component.html.erb +20 -0
  13. data/app/components/mensa/filter_pill_list/component.html.erb +30 -0
  14. data/app/components/mensa/header/component.html.erb +12 -0
  15. data/app/components/mensa/row_action/component.html.erb +9 -0
  16. data/app/components/mensa/search/component.html.erb +26 -0
  17. data/app/components/mensa/selection/component_controller.js +1 -1
  18. data/app/components/mensa/table/component.html.erb +25 -0
  19. data/app/components/mensa/table_row/component.html.erb +18 -0
  20. data/app/components/mensa/view/component.html.erb +51 -0
  21. data/app/components/mensa/views/component.html.erb +74 -0
  22. data/app/helpers/mensa/application_helper.rb +15 -2
  23. data/app/javascript/mensa/controllers/index.js +2 -2
  24. data/app/javascript/mensa/controllers/link_controller.js +44 -0
  25. data/app/tables/mensa/base.rb +0 -6
  26. data/app/tables/mensa/column.rb +1 -10
  27. data/app/tables/mensa/row.rb +1 -1
  28. data/app/views/mensa/exports/_badge.html.erb +6 -0
  29. data/app/views/mensa/exports/_dialog.html.erb +52 -0
  30. data/app/views/mensa/exports/_list.html.erb +32 -0
  31. data/app/views/mensa/tables/filters/show.turbo_stream.erb +58 -0
  32. data/app/views/mensa/tables/show.html.erb +5 -0
  33. data/app/views/mensa/tables/show.turbo_stream.erb +7 -0
  34. data/app/views/mensa/tables/views/create.turbo_stream.erb +11 -0
  35. data/app/views/mensa/tables/views/destroy.turbo_stream.erb +11 -0
  36. data/app/views/mensa/tables/views/update.turbo_stream.erb +11 -0
  37. data/lib/generators/mensa/table_generator.rb +15 -0
  38. data/lib/generators/mensa/templates/config/initializers/mensa.rb +4 -5
  39. data/lib/generators/mensa/templates/table.rb.tt +18 -0
  40. data/lib/mensa/engine.rb +1 -2
  41. data/lib/mensa/version.rb +1 -1
  42. data/mensa.gemspec +3 -4
  43. metadata +43 -53
  44. data/app/components/mensa/add_filter/component.html.slim +0 -14
  45. data/app/components/mensa/cell/component.html.slim +0 -1
  46. data/app/components/mensa/column_customizer/component.html.slim +0 -14
  47. data/app/components/mensa/control_bar/component.html.slim +0 -43
  48. data/app/components/mensa/empty_state/component.html.slim +0 -7
  49. data/app/components/mensa/filter_pill/component.html.slim +0 -9
  50. data/app/components/mensa/filter_pill_list/component.html.slim +0 -11
  51. data/app/components/mensa/header/component.html.slim +0 -8
  52. data/app/components/mensa/row_action/component.html.slim +0 -6
  53. data/app/components/mensa/search/component.html.slim +0 -21
  54. data/app/components/mensa/table/component.html.slim +0 -10
  55. data/app/components/mensa/table_row/component.html.slim +0 -11
  56. data/app/components/mensa/view/component.html.slim +0 -30
  57. data/app/components/mensa/views/component.html.slim +0 -52
  58. data/app/views/mensa/exports/_badge.html.slim +0 -5
  59. data/app/views/mensa/exports/_dialog.html.slim +0 -42
  60. data/app/views/mensa/exports/_list.html.slim +0 -29
  61. data/app/views/mensa/tables/filters/show.turbo_stream.slim +0 -36
  62. data/app/views/mensa/tables/show.html.slim +0 -4
  63. data/app/views/mensa/tables/show.turbo_stream.slim +0 -5
  64. data/app/views/mensa/tables/views/create.turbo_stream.slim +0 -11
  65. data/app/views/mensa/tables/views/destroy.turbo_stream.slim +0 -11
  66. data/app/views/mensa/tables/views/update.turbo_stream.slim +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd1d2a591fc88aa9498cf300d61b0c490f648c72ca8a9b827ae8e2c2c54bd8a1
4
- data.tar.gz: b332b0aed27c3d6589083ad09032a3b7e326e2e17c54848024ce97628854b76d
3
+ metadata.gz: 4370665a3982e3464306fd155b455aef8c62d2fda14c474771042a30af3038cb
4
+ data.tar.gz: 91c50234915ffeb886a6569575d33fa7b85eb453f1da8991e075ad46fe5101ac
5
5
  SHA512:
6
- metadata.gz: 51543cc8be3d7ef4afd908b2130398ec4e35da6555d96d011c7046c054dedc08ab0d4c4460222c1058ef39268665169ccab8d5db9860c4ab2dffb1ab3e672d72
7
- data.tar.gz: 4f26ad72658d38d06b7806ce6012982ba0392acdb7567f8beeb61f6201b6f6bdd86e1ed9a374bb92f6faa9318f3b5bf3b8565730e46627eefcfaf3b54edff851
6
+ metadata.gz: 3cbde51c093adcccaad8b2814b6c436a8be160e6fa68bab0cc2463c22560d325017f736577add7ffb2b3e4974e2df1abce81ca3a89e83ea3bb9e77d42d65800a
7
+ data.tar.gz: 9691484b7695d915849eebbc204f8ca22c560d38928c5e0d78285b4eb1f7b140f29aad0498d455549e359f97590e9659f5e1d2a049b4b3a17ffc2c10bc9c5397
data/Gemfile CHANGED
@@ -7,11 +7,10 @@ gemspec
7
7
 
8
8
  gem "puma"
9
9
  gem "sprockets-rails"
10
- gem "satis", "~> 2"
11
10
  gem "pry"
12
11
  gem "capybara", "~> 3.40"
13
12
  gem "selenium-webdriver", "~> 4.17"
14
- gem "slim", "~> 5.2"
13
+
15
14
  gem "debug"
16
15
  gem "overmind", require: false
17
16
  gem "rack-mini-profiler"
data/Gemfile.lock CHANGED
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mensa (0.3.0)
4
+ mensa (0.3.2)
5
5
  csv
6
6
  importmap-rails
7
7
  pagy (>= 43)
8
+ pg (>= 1.6)
8
9
  rails (>= 7.1)
9
10
  rubyzip (>= 1.2.2)
10
- slim
11
11
  stimulus-rails
12
12
  tailwindcss-rails (~> 3.3)
13
13
  textacular (>= 5)
@@ -96,7 +96,6 @@ GEM
96
96
  ast (2.4.3)
97
97
  base64 (0.3.0)
98
98
  bigdecimal (4.1.2)
99
- browser (6.2.0)
100
99
  builder (3.3.0)
101
100
  capybara (3.40.0)
102
101
  addressable
@@ -116,7 +115,6 @@ GEM
116
115
  debug (1.11.1)
117
116
  irb (~> 1.10)
118
117
  reline (>= 0.3.8)
119
- diffy (3.4.4)
120
118
  drb (2.2.3)
121
119
  erb (6.0.4)
122
120
  erubi (1.13.1)
@@ -135,10 +133,6 @@ GEM
135
133
  rdoc (>= 4.0.0)
136
134
  reline (>= 0.4.2)
137
135
  json (2.19.8)
138
- jsonb_accessor (1.4.2)
139
- activerecord (>= 6.1)
140
- activesupport (>= 6.1)
141
- pg (>= 0.18.1)
142
136
  language_server-protocol (3.17.0.5)
143
137
  lint_roller (1.1.0)
144
138
  logger (1.7.0)
@@ -305,16 +299,6 @@ GEM
305
299
  ruby-lsp (>= 0.26.0, < 0.27.0)
306
300
  ruby-progressbar (1.13.0)
307
301
  rubyzip (3.3.1)
308
- satis (2.3.5)
309
- browser
310
- diffy
311
- importmap-rails
312
- jsonb_accessor (~> 1.4)
313
- rails (>= 6)
314
- stimulus-rails
315
- tailwindcss-rails
316
- turbo-rails
317
- view_component
318
302
  securerandom (0.4.1)
319
303
  selenium-webdriver (4.44.0)
320
304
  base64 (~> 0.2)
@@ -322,9 +306,6 @@ GEM
322
306
  rexml (~> 3.2, >= 3.2.5)
323
307
  rubyzip (>= 1.2.2, < 4.0)
324
308
  websocket (~> 1.0)
325
- slim (5.2.1)
326
- temple (~> 0.10.0)
327
- tilt (>= 2.1.0)
328
309
  sprockets (4.2.2)
329
310
  concurrent-ruby (~> 1.0)
330
311
  logger
@@ -333,14 +314,6 @@ GEM
333
314
  actionpack (>= 6.1)
334
315
  activesupport (>= 6.1)
335
316
  sprockets (>= 3.0.0)
336
- sqlite3 (2.9.4-aarch64-linux-gnu)
337
- sqlite3 (2.9.4-aarch64-linux-musl)
338
- sqlite3 (2.9.4-arm-linux-gnu)
339
- sqlite3 (2.9.4-arm-linux-musl)
340
- sqlite3 (2.9.4-arm64-darwin)
341
- sqlite3 (2.9.4-x86_64-darwin)
342
- sqlite3 (2.9.4-x86_64-linux-gnu)
343
- sqlite3 (2.9.4-x86_64-linux-musl)
344
317
  standard (1.54.0)
345
318
  language_server-protocol (~> 3.17.0.2)
346
319
  lint_roller (~> 1.0)
@@ -364,11 +337,9 @@ GEM
364
337
  tailwindcss-ruby (3.4.19-arm64-darwin)
365
338
  tailwindcss-ruby (3.4.19-x86_64-darwin)
366
339
  tailwindcss-ruby (3.4.19-x86_64-linux)
367
- temple (0.10.4)
368
340
  textacular (5.7.0)
369
341
  activerecord (>= 5.0)
370
342
  thor (1.5.0)
371
- tilt (2.7.0)
372
343
  timeout (0.6.1)
373
344
  tsort (0.2.0)
374
345
  turbo-rails (2.0.23)
@@ -418,11 +389,8 @@ DEPENDENCIES
418
389
  rubocop-rails
419
390
  ruby-lsp
420
391
  ruby-lsp-rails
421
- satis (~> 2)
422
392
  selenium-webdriver (~> 4.17)
423
- slim (~> 5.2)
424
393
  sprockets-rails
425
- sqlite3 (~> 2.8)
426
394
  standard
427
395
 
428
396
  BUNDLED WITH
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Mensa
2
2
 
3
- Fast and awesome tables, with pagination, sorting, filtering and custom views.
3
+ Fast and awesome tables, with pagination, sorting, filtering, batch processing, column hiding, column ordering and custom views.
4
+ Due to search, it only works with postgresql at the moment.
4
5
 
5
6
  ![table](./docs/table.png)
6
7
  ![filters](./docs/filters.png)
@@ -12,13 +13,13 @@ Features:
12
13
  - [x] very fast
13
14
  - [x] row-links
14
15
  - [x] sorting
15
- - [x] tables without headers (and without most of the above)
16
16
  - [x] filtering of multiple columns
17
17
  - [X] Hide filter icon in case there are no filters
18
18
  - [X] column ordering
19
19
  - [X] editing of existing filters
20
20
  - [X] view selection and exports per view
21
21
  - [X] multiple selection of rows and batch processing
22
+ - [x] tables without headers (and without most of the above)
22
23
 
23
24
  Todo/Fixme:
24
25
  - [ ] exports can be mailed - daily/weekly/monthly/quarterly/bi-yearly/yearly (time configurable)
@@ -52,16 +53,7 @@ class UserTable < ApplicationTable
52
53
  order name: :desc
53
54
 
54
55
  column(:name) do
55
- attribute :name # Optional, we can deduct this from the column name
56
- sortable true
57
- sanitize true
58
- internal false
59
- visible true
60
- filter do
61
- collection -> { }
62
- scope -> { where(name: ...) }
63
- end
64
- operators [:is, :isnt] # Optional, mensa tries to deduce this from the column information
56
+ filter
65
57
  end
66
58
 
67
59
  column(:nr_of_roles) do
@@ -112,17 +104,14 @@ end
112
104
 
113
105
  You can show your tables on the page using the following:
114
106
 
115
- ```slim
116
- = table :users
107
+ ```erb
108
+ <%= table :users %>
117
109
  ```
118
110
 
119
111
  #### Custom views
120
112
 
121
113
  Custom views are views not defined by the developer (SystemViews) but by the end-user by adding/removing filters.
122
-
123
- Initial support for custom-views is there, but pretty rudimentary:
124
-
125
- `Mensa::TableView.create(table: "users", name: "Guests", config: {filters: {role: {value: "guest"}}})`
114
+ When you enable these, they are stored in the database and can be used across sessions, by the user who created them.
126
115
 
127
116
  ### Fast
128
117
 
@@ -140,42 +129,27 @@ end
140
129
  ### Coding
141
130
 
142
131
  - Checkout this repo
143
- - Setup your direnv, add the following to your `.envrc`:
132
+ - Setup your direnv, add the following to your `mise.toml`:
144
133
 
145
134
  ```
146
- export BUNDLE_RUBYGEMS__PKG__GITHUB__COM=ghp_xxxxxxw
147
- export RBENV_VERSION=$(cat .ruby-version)
135
+ [tools]
136
+ node = "24"
137
+ ruby = "3.4.7"
138
+
139
+ [env]
140
+ RUBY_VERSION="3.4.7"
148
141
  ```
149
142
 
150
143
  - Run `direnv allow`
151
-
152
- If you use devcontainers:
153
- - Open with Visual Studio Code (or with any other editor) and reopen in container.
154
- - Run `bin/overmind s`
155
-
156
- If you're not using devcontainers:
157
- - Run `bin/overmind s`
144
+ - Run `overmind s`
158
145
 
159
146
  ### Docs
160
147
 
161
148
  Using the following in your view will render Mensa::Table::Component
162
- ```slim
163
- = table :users
149
+ ```erb
150
+ <%= table :users %>
164
151
  ```
165
152
 
166
- The Mensa::Table::Component will render:
167
- - Mensa::Search::Component
168
- - Mensa::FilterPillList::Component
169
- - Mensa::FilterPill::Component
170
- - Mensa::AddFilter::Component
171
- - Mensa::Views::Component
172
- - renders a views list
173
- - Mensa::ControlBar::Component
174
- - search icon
175
- - filter icon
176
- - export icon
177
- - turbo-frame with the actual table you see (which is rendered by Mensa::View::Component)
178
-
179
153
  ## Installation
180
154
 
181
155
  Add this line to your application's Gemfile:
@@ -199,7 +173,7 @@ $ gem install mensa
199
173
  Always use `bundle` to install the gem. Next use the install generator to install migrations, add an initializer and do other setup:
200
174
 
201
175
  ```bash
202
- $ bin/rails g mensa:install
176
+ $ rails g mensa:install
203
177
  ```
204
178
 
205
179
  ### Exports
@@ -0,0 +1,31 @@
1
+ <div class="mensa-table__add_filter"
2
+ data-controller="mensa-add-filter"
3
+ data-mensa-add-filter-mensa-filter-pill-list-outlet="#mensa-filter-pill-list-<%= table.table_id %>"
4
+ data-mensa-add-filter-operator-labels-value="<%= operator_labels.to_json %>">
5
+ <button class="mensa-table__add_filter__trigger"
6
+ type="button"
7
+ title="<%= t("mensa.add_filter.add", default: "Add filter") %>"
8
+ data-action="mensa-add-filter#openAllColumns">
9
+ <i class="fa-solid fa-circle-plus"></i>
10
+ </button>
11
+
12
+ <ul class="hidden fixed z-50 max-h-96 overflow-auto rounded-lg bg-white p-2 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-800 sm:text-sm"
13
+ data-mensa-add-filter-target="filterList">
14
+ <% table.columns.select(&:filter?).each do |column| %>
15
+ <li class="text-gray-900 hover:bg-gray-100 relative flex cursor-default select-none items-center gap-2 rounded-md px-3 py-2 dark:text-gray-300 dark:hover:bg-gray-700"
16
+ data-mensa-add-filter-target="filterListItem"
17
+ data-action="click->mensa-add-filter#selectColumn mouseenter->mensa-add-filter#columnItemHovered"
18
+ data-filter-column-name="<%= column.name %>">
19
+ <div class="label block flex-1 truncate font-normal"><%= column.human_name %></div>
20
+ <div class="check hidden flex items-center text-primary-600">
21
+ <div class="fal fa-check"></div>
22
+ </div>
23
+ <span class="mensa-table__add_filter__enter-hint">↵ Enter</span>
24
+ </li>
25
+ <% end %>
26
+ </ul>
27
+
28
+ <div class="hidden mensa-table__add_filter__popover_container"
29
+ data-mensa-add-filter-target="valuePopover"
30
+ id="mensa-filter-pill-value-<%= SecureRandom.base36 %>"></div>
31
+ </div>
@@ -1,5 +1,4 @@
1
1
  import ApplicationController from "mensa/controllers/application_controller";
2
- // import { debounce } from '@entdec/satis'
3
2
  import { get } from "@rails/request.js";
4
3
 
5
4
  // Survives the turbo-stream re-render that destroys both the filter-pill-list
@@ -4,7 +4,6 @@ module Mensa
4
4
  class ApplicationComponent < ViewComponent::Base
5
5
  include ViewComponent::Slotable
6
6
  include ActionView::Helpers::TranslationHelper
7
- include Satis::Concerns::ContextualTranslations
8
7
  include Mensa::ApplicationHelper
9
8
 
10
9
  attr_accessor :original_view_context
@@ -0,0 +1 @@
1
+ <td><%= cell.render(:html) %></td>
@@ -0,0 +1,31 @@
1
+ <div class="relative"
2
+ data-controller="mensa-column-customizer"
3
+ data-mensa-column-customizer-table-name-value="<%= table.name %>"
4
+ data-mensa-column-customizer-turbo-frame-id-value="<%= table.table_id %>"
5
+ data-mensa-column-customizer-mensa-table-outlet="#table-<%= table.table_id %>">
6
+ <button class="mensa-table__control_bar__button" type="button" data-action="click->mensa-column-customizer#toggle">
7
+ <i class="<%= Mensa.config.icons[:control_bar_edit] %>"></i>
8
+ </button>
9
+ <div class="mensa-table__column_customizer__popover hidden" data-mensa-column-customizer-target="popover">
10
+ <p class="mensa-table__column_customizer__heading">Columns</p>
11
+ <ul>
12
+ <% table.columns.reject(&:internal?).each do |column| %>
13
+ <li class="mensa-table__column_customizer__row"
14
+ data-mensa-column-customizer-target="columnRow"
15
+ data-column-name="<%= column.name %>"
16
+ data-visible="<%= column.visible?.to_s %>"
17
+ draggable="true"
18
+ data-action="dragstart->mensa-column-customizer#dragStart dragover->mensa-column-customizer#dragOver drop->mensa-column-customizer#drop dragend->mensa-column-customizer#dragEnd">
19
+ <i class="mensa-table__column_customizer__handle fa-solid fa-grip-vertical"></i>
20
+ <span class="mensa-table__column_customizer__name<%= !column.visible? ? " mensa-table__column_customizer__name--hidden" : "" %>">
21
+ <%= column.human_name %>
22
+ </span>
23
+ <button class="mensa-table__column_customizer__visibility" type="button" data-action="click->mensa-column-customizer#toggleVisibility">
24
+ <i class="fa-solid fa-eye"></i>
25
+ <i class="fa-solid fa-eye-slash"></i>
26
+ </button>
27
+ </li>
28
+ <% end %>
29
+ </ul>
30
+ </div>
31
+ </div>
@@ -0,0 +1,56 @@
1
+ <div class="mensa-table__control_bar" data-mensa-table-target="controlBar">
2
+ <div class="flex items-center gap-1 py-0">
3
+ <% if view_columns_ordering? %>
4
+ <%= render Mensa::ColumnCustomizer::Component.new(table: table) %>
5
+ <% end %>
6
+
7
+ <div class="mensa-table__save-reset hidden" data-mensa-table-target="saveResetButtons">
8
+ <button class="mensa-table__control_bar__button" type="button" title="<%= t(".reset", default: "Reset") %>" data-action="mensa-table#cancelFiltersAndSearch">
9
+ <i class="fa-solid fa-rotate-left"></i>
10
+ </button>
11
+ <% if table.current_user && table.supports_custom_views? %>
12
+ <div class="relative">
13
+ <button class="mensa-table__control_bar__button hidden" type="button" data-mensa-table-target="saveSimple" data-action="mensa-table#saveAsNewView">
14
+ <%= t(".save", default: "Save") %>
15
+ </button>
16
+ <button class="mensa-table__control_bar__button hidden" type="button" data-mensa-table-target="saveSplit" data-action="mensa-table#toggleSaveDropdown">
17
+ <%= t(".save", default: "Save") %>
18
+ <i class="fa-solid fa-chevron-down text-xs"></i>
19
+ </button>
20
+ <ul class="mensa-table__control_bar__save-dropdown hidden" data-mensa-table-target="saveDropdown">
21
+ <li>
22
+ <button class="mensa-table__control_bar__save-dropdown-item" type="button" data-action="mensa-table#updateCurrentViewAction">
23
+ <%= t(".update_view", default: "Update view") %>
24
+ </button>
25
+ </li>
26
+ <li>
27
+ <button class="mensa-table__control_bar__save-dropdown-item" type="button" data-action="mensa-table#saveAsNewView">
28
+ <%= t(".save_as_new_view", default: "Save as new view") %>
29
+ </button>
30
+ </li>
31
+ </ul>
32
+ </div>
33
+ <% else %>
34
+ <button class="mensa-table__control_bar__button" type="button" data-action="mensa-table#saveAsNewView">
35
+ <%= t(".save", default: "Save") %>
36
+ </button>
37
+ <% end %>
38
+ </div>
39
+
40
+ <button class="mensa-table__control_bar__button hidden" type="button" data-mensa-table-target="eyeButton" data-action="click->mensa-table#toggleViewFilters">
41
+ <i class="fa-solid fa-eye"></i>
42
+ </button>
43
+
44
+ <% if table.exportable? %>
45
+ <button class="mensa-table__control_bar__button relative" type="button" data-action="mensa-table#export">
46
+ <i data-mensa-table-target="exportIcon" class="<%= Mensa.config.icons[:control_bar_export] %>"></i>
47
+ <%= helpers.render partial: "mensa/exports/badge", locals: {table_name: table.name, user: table.current_user} %>
48
+ </button>
49
+ <% end %>
50
+ </div>
51
+
52
+ <% if table.exportable? %>
53
+ <%= helpers.turbo_stream_from Mensa::Export.stream_name(table.name, table.current_user) %>
54
+ <%= helpers.render partial: "mensa/exports/dialog", locals: {table: table} %>
55
+ <% end %>
56
+ </div>
@@ -0,0 +1,10 @@
1
+ <div class="mensa-empty-state">
2
+ <div class="mensa-empty-state__icon">
3
+ <i class="<%= Mensa.config.icons[:search] %>"></i>
4
+ </div>
5
+ <h3 class="mensa-empty-state__title"><%= t("mensa.empty_state.title", model: model_name_plural) %></h3>
6
+ <p class="mensa-empty-state__subtitle"><%= t("mensa.empty_state.subtitle") %></p>
7
+ <button class="mensa-empty-state__button" type="button" data-action="click->mensa-table#cancelFiltersAndSearch">
8
+ <%= t("mensa.empty_state.clear_button") %>
9
+ </button>
10
+ </div>
@@ -0,0 +1,20 @@
1
+ <% formatted_value = filter.value.is_a?(Array) ? filter.value.join(", ") : filter.value %>
2
+ <div class="mensa-filter-pill"
3
+ data-controller="mensa-filter-pill"
4
+ data-mensa-filter-pill-mensa-filter-pill-list-outlet="#mensa-filter-pill-list-<%= filter.table.table_id %>"
5
+ data-mensa-filter-pill-column-name-value="<%= filter.column.name %>"
6
+ data-mensa-filter-pill-value-value="<%= filter.operator_with_value? ? (filter.value.is_a?(Array) ? filter.value.to_json : filter.value) : nil %>"
7
+ data-mensa-filter-pill-operator-value="<%= filter.operator %>"
8
+ data-mensa-filter-pill-operator-without-value-value="<%= "true" unless filter.operator_with_value? %>"
9
+ data-view-filter="<%= "true" if view_filter? %>">
10
+ <button class="mensa-filter-pill__chip" type="button" data-action="click->mensa-filter-pill#edit">
11
+ <span class="mensa-filter-pill__column"><%= filter.column.human_name %></span>
12
+ <span class="mensa-filter-pill__operator"><%= filter.operator_label %></span>
13
+ <% if formatted_value.present? && filter.operator_with_value? %>
14
+ <span class="mensa-filter-pill__value"><%= formatted_value %></span>
15
+ <% end %>
16
+ </button>
17
+ <button class="mensa-filter-pill__remove" type="button" title="Remove filter" data-action="click->mensa-filter-pill#remove">
18
+ <i class="fa-solid fa-xmark"></i>
19
+ </button>
20
+ </div>
@@ -0,0 +1,30 @@
1
+ <% has_filterable_columns = table.columns.any?(&:filter?) %>
2
+ <div class="mensa-table__search-bar"
3
+ id="mensa-filter-pill-list-<%= table.table_id %>"
4
+ data-controller="mensa-filter-pill-list"
5
+ data-mensa-table-target="filterList"
6
+ data-mensa-filter-pill-list-table-name-value="<%= table.name %>"
7
+ data-mensa-filter-pill-list-mensa-table-outlet=".mensa-table#table-<%= table.table_id %>"
8
+ data-mensa-filter-pill-list-mensa-filter-pill-outlet=".mensa-filter-pill"
9
+ data-mensa-filter-pill-list-mensa-add-filter-outlet="[data-controller=mensa-add-filter]">
10
+ <div class="mensa-table__search-bar__pills-area">
11
+ <%= render Mensa::FilterPill::Component.with_collection(table.active_filters) %>
12
+ <% if has_filterable_columns %>
13
+ <%= render Mensa::AddFilter::Component.new(table: table) %>
14
+ <% end %>
15
+ <div class="mensa-table__search-bar__input-wrapper">
16
+ <input class="mensa-table__search-bar__input"
17
+ type="text"
18
+ name="search_query"
19
+ autocomplete="off"
20
+ placeholder="<%= t(has_filterable_columns ? ".search" : ".search_only") %>"
21
+ data-mensa-filter-pill-list-target="searchInput"
22
+ data-action="input->mensa-filter-pill-list#monitorSearch keydown.enter->mensa-filter-pill-list#search keydown.esc->mensa-filter-pill-list#resetSearch focus->mensa-filter-pill-list#searchFocused keydown.down->mensa-filter-pill-list#navigateDown keydown.up->mensa-filter-pill-list#navigateUp"
23
+ value="<%= params[:query] %>">
24
+ <i class="mensa-table__search-bar__search-icon <%= Mensa.config.icons[:search] %>"></i>
25
+ </div>
26
+ </div>
27
+ <button class="mensa-table__search-bar__clear hidden" type="button" data-mensa-filter-pill-list-target="resetSearchButton" data-action="mensa-filter-pill-list#resetSearch">
28
+ <i class="fas fa-xmark"></i>
29
+ </button>
30
+ </div>
@@ -0,0 +1,12 @@
1
+ <th>
2
+ <% if column.sortable? %>
3
+ <%= link_to table.path(order: {column.name => column.next_sort_direction}, turbo_frame_id: table.table_id), "data-turbo-frame": "_self", class: "order cursor-pointer" do %>
4
+ <span><%= column.human_name %></span>
5
+ <i class="<%= Mensa.config.icons["order_indicator#{column.sort_direction.to_s.present? ? "_#{column.sort_direction}" : ""}".to_sym] %>"></i>
6
+ <% end %>
7
+ <% else %>
8
+ <div class="container">
9
+ <div class="title"><%= column.human_name %></div>
10
+ </div>
11
+ <% end %>
12
+ </th>
@@ -0,0 +1,9 @@
1
+ <% if action.show.call(row.record) %>
2
+ <%= link_to(action.link ? table.original_view_context.instance_exec(row.record, &action.link) : "", {class: "action", title: action.title}.merge(action.link_attributes || {})) do %>
3
+ <% if action.icon %>
4
+ <i class="fa <%= action.icon %>"></i>
5
+ <% else %>
6
+ <%= name %>
7
+ <% end %>
8
+ <% end %>
9
+ <% end %>
@@ -0,0 +1,26 @@
1
+ <% if table.current_user %>
2
+ <dialog class="mensa-table__save-view-dialog" data-mensa-table-target="saveViewDialog" data-action="click->mensa-table#saveViewDialogBackdrop">
3
+ <form class="mensa-table__save-view-dialog__form" data-action="submit->mensa-table#confirmSaveView">
4
+ <header class="mensa-table__save-view-dialog__header">
5
+ <h2 class="mensa-table__save-view-dialog__title"><%= t(".save_view_title", default: "Save view") %></h2>
6
+ <p class="mensa-table__save-view-dialog__subtitle"><%= t(".save_view_subtitle", default: "Save the current filters, ordering and search as a reusable view.") %></p>
7
+ </header>
8
+ <label class="mensa-table__save-view-dialog__field">
9
+ <span class="mensa-table__save-view-dialog__label"><%= t(".view_name", default: "Name") %></span>
10
+ <input class="mensa-table__save-view-dialog__input" type="text" required placeholder="<%= t(".view_name_placeholder", default: "e.g. Active customers") %>" data-mensa-table-target="saveViewName">
11
+ </label>
12
+ <label class="mensa-table__save-view-dialog__field">
13
+ <span class="mensa-table__save-view-dialog__label"><%= t(".view_description", default: "Description") %></span>
14
+ <textarea class="mensa-table__save-view-dialog__textarea" rows="3" placeholder="<%= t(".view_description_placeholder", default: "Optional notes about this view") %>" data-mensa-table-target="saveViewDescription"></textarea>
15
+ </label>
16
+ <div class="mensa-table__save-view-dialog__actions">
17
+ <button class="mensa-table__save-view-dialog__button mensa-table__save-view-dialog__button--secondary" type="button" data-action="mensa-table#cancelSaveView">
18
+ <%= t(".cancel") %>
19
+ </button>
20
+ <button class="mensa-table__save-view-dialog__button mensa-table__save-view-dialog__button--primary" type="submit">
21
+ <%= t(".save") %>
22
+ </button>
23
+ </div>
24
+ </form>
25
+ </dialog>
26
+ <% end %>
@@ -60,7 +60,7 @@ export default class SelectionController extends ApplicationController {
60
60
  this.updateSelectionState();
61
61
  }
62
62
 
63
- // Stops click events from bubbling up to the row's satis-link handler,
63
+ // Stops click events from bubbling up to the row's mensa-link handler,
64
64
  // preventing accidental navigation when the user clicks a checkbox cell.
65
65
  stopPropagation(event) {
66
66
  event.stopPropagation();
@@ -0,0 +1,25 @@
1
+ <div class="mensa-table"
2
+ id="table-<%= table.table_id %>"
3
+ data-mensa-table-supports-views-value="<%= table.supports_views? %>"
4
+ data-mensa-table-table-url-value="<%= helpers.mensa.table_url(table.name, {turbo_frame_id: table.table_id}.merge(params)) %>"
5
+ data-mensa-table-save-view-url-value="<%= helpers.mensa.table_views_path(table.name) %>"
6
+ data-mensa-table-views-url-value="<%= helpers.mensa.table_views_path(table.name) %>"
7
+ data-mensa-table-exports-url-value="<%= helpers.mensa.table_exports_path(table.name) %>"
8
+ data-controller="mensa-table"
9
+ data-mensa-table-mensa-filter-pill-outlet="[data-controller='mensa-filter-pill']"
10
+ data-mensa-table-mensa-filter-pill-list-outlet="#mensa-filter-pill-list-<%= table.table_id %>"
11
+ data-mensa-table-mensa-column-customizer-outlet="[data-controller='mensa-column-customizer']">
12
+ <div class="mensa-table__toolbar">
13
+ <div class="mensa-table__search-container">
14
+ <% if table.supports_views? && table.show_header? %>
15
+ <%= render Mensa::Views::Component.new(table: table) %>
16
+ <% end %>
17
+ <div class="flex min-w-0 flex-1" id="filters-<%= table.table_id %>">
18
+ <%= render Mensa::FilterPillList::Component.new(table: table) %>
19
+ </div>
20
+ </div>
21
+ <%= render Mensa::ControlBar::Component.new(table: table) %>
22
+ </div>
23
+ <%= render Mensa::Search::Component.new(table: table) %>
24
+ <turbo-frame id="<%= table.table_id %>" target="_top" data-mensa-table-target="turboFrame" data-turbo-permanent=""></turbo-frame>
25
+ </div>
@@ -0,0 +1,18 @@
1
+ <%= content_tag :tr, **row.link_attributes do %>
2
+ <% if table.batch_actions? %>
3
+ <td class="mensa-table__checkbox-col" data-action="click->mensa-selection#stopPropagation">
4
+ <input class="mensa-table__select-all" type="checkbox" value="<%= row.record.id %>" data-action="change->mensa-selection#toggleRow" data-mensa-selection-target="rowCheckbox">
5
+ </td>
6
+ <% end %>
7
+ <% if table.actions? && Mensa.config.row_actions_position == :front %>
8
+ <td class="actions">
9
+ <%= render(Mensa::RowAction::Component.with_collection(table.actions, row: row, table: table)) %>
10
+ </td>
11
+ <% end %>
12
+ <%= render(Mensa::Cell::Component.with_collection(table.display_columns, row: row)) %>
13
+ <% if table.actions? && Mensa.config.row_actions_position == :back %>
14
+ <td class="actions">
15
+ <%= render(Mensa::RowAction::Component.with_collection(table.actions, row: row, table: table)) %>
16
+ </td>
17
+ <% end %>
18
+ <% end %>
@@ -0,0 +1,51 @@
1
+ <div class="overflow-y-auto relative"
2
+ data-mensa-table-target="view"
3
+ data-controller="mensa-selection"
4
+ data-mensa-selection-batch-url-value="<%= table.batch_actions? ? helpers.mensa.table_batch_actions_path(table.name) : "" %>">
5
+ <% if table.batch_actions? %>
6
+ <div class="mensa-batch-bar hidden" data-mensa-selection-target="batchBar">
7
+ <div class="mensa-batch-bar__content">
8
+ <input class="mensa-table__select-all" type="checkbox" data-action="click->mensa-selection#deselectAll" data-mensa-selection-target="batchAllCheckbox">
9
+ <span class="mensa-batch-bar__count" data-mensa-selection-target="selectedCount"></span>
10
+ <% table.batch_actions.each do |batch_action| %>
11
+ <button class="mensa-batch-bar__button" type="button" data-action="click->mensa-selection#executeBatch" data-mensa-selection-batch-action-param="<%= batch_action.name %>">
12
+ <%= batch_action.title %>
13
+ </button>
14
+ <% end %>
15
+ </div>
16
+ </div>
17
+ <% end %>
18
+ <table class="w-full">
19
+ <% if table.show_header? %>
20
+ <thead>
21
+ <tr>
22
+ <% if table.batch_actions? %>
23
+ <th class="mensa-table__checkbox-col">
24
+ <div class="container">
25
+ <input class="mensa-table__select-all" type="checkbox" data-action="change->mensa-selection#toggleAll" data-mensa-selection-target="headerCheckbox">
26
+ </div>
27
+ </th>
28
+ <% end %>
29
+ <% if table.actions? && Mensa.config.row_actions_position == :front %>
30
+ <th>Actions</th>
31
+ <% end %>
32
+ <%= render(Mensa::Header::Component.with_collection(table.display_columns, table: table)) %>
33
+ <% if table.actions? && Mensa.config.row_actions_position == :back %>
34
+ <th>Actions</th>
35
+ <% end %>
36
+ </tr>
37
+ </thead>
38
+ <% end %>
39
+ <tbody>
40
+ <%= render(Mensa::TableRow::Component.with_collection(table.rows, table: table)) %>
41
+ </tbody>
42
+ </table>
43
+ <% if table.pagy_details&.count == 0 %>
44
+ <%= render Mensa::EmptyState::Component.new(table: table) %>
45
+ <% elsif table.pagy_details&.last > 1 %>
46
+ <div class="paging">
47
+ <span><%= t("mensa.paging.info", model: model_name_plural, from: table.pagy_details.from, to: table.pagy_details.to, count: table.pagy_details.count) %></span>
48
+ <%== table.pagy_details.series_nav(anchor_string: 'data-turbo-frame="_self"') %>
49
+ </div>
50
+ <% end %>
51
+ </div>