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.
- checksums.yaml +4 -4
- data/Gemfile +1 -2
- data/Gemfile.lock +2 -34
- data/README.md +18 -44
- data/app/components/mensa/add_filter/component.html.erb +31 -0
- data/app/components/mensa/add_filter/component_controller.js +0 -1
- data/app/components/mensa/application_component.rb +0 -1
- data/app/components/mensa/cell/component.html.erb +1 -0
- data/app/components/mensa/column_customizer/component.html.erb +31 -0
- data/app/components/mensa/control_bar/component.html.erb +56 -0
- data/app/components/mensa/empty_state/component.html.erb +10 -0
- data/app/components/mensa/filter_pill/component.html.erb +20 -0
- data/app/components/mensa/filter_pill_list/component.html.erb +30 -0
- data/app/components/mensa/header/component.html.erb +12 -0
- data/app/components/mensa/row_action/component.html.erb +9 -0
- data/app/components/mensa/search/component.html.erb +26 -0
- data/app/components/mensa/selection/component_controller.js +1 -1
- data/app/components/mensa/table/component.html.erb +25 -0
- data/app/components/mensa/table_row/component.html.erb +18 -0
- data/app/components/mensa/view/component.html.erb +51 -0
- data/app/components/mensa/views/component.html.erb +74 -0
- data/app/helpers/mensa/application_helper.rb +15 -2
- data/app/javascript/mensa/controllers/index.js +2 -2
- data/app/javascript/mensa/controllers/link_controller.js +44 -0
- data/app/tables/mensa/base.rb +0 -6
- data/app/tables/mensa/column.rb +1 -10
- data/app/tables/mensa/row.rb +1 -1
- data/app/views/mensa/exports/_badge.html.erb +6 -0
- data/app/views/mensa/exports/_dialog.html.erb +52 -0
- data/app/views/mensa/exports/_list.html.erb +32 -0
- data/app/views/mensa/tables/filters/show.turbo_stream.erb +58 -0
- data/app/views/mensa/tables/show.html.erb +5 -0
- data/app/views/mensa/tables/show.turbo_stream.erb +7 -0
- data/app/views/mensa/tables/views/create.turbo_stream.erb +11 -0
- data/app/views/mensa/tables/views/destroy.turbo_stream.erb +11 -0
- data/app/views/mensa/tables/views/update.turbo_stream.erb +11 -0
- data/lib/generators/mensa/table_generator.rb +15 -0
- data/lib/generators/mensa/templates/config/initializers/mensa.rb +4 -5
- data/lib/generators/mensa/templates/table.rb.tt +18 -0
- data/lib/mensa/engine.rb +1 -2
- data/lib/mensa/version.rb +1 -1
- data/mensa.gemspec +3 -4
- metadata +43 -53
- data/app/components/mensa/add_filter/component.html.slim +0 -14
- data/app/components/mensa/cell/component.html.slim +0 -1
- data/app/components/mensa/column_customizer/component.html.slim +0 -14
- data/app/components/mensa/control_bar/component.html.slim +0 -43
- data/app/components/mensa/empty_state/component.html.slim +0 -7
- data/app/components/mensa/filter_pill/component.html.slim +0 -9
- data/app/components/mensa/filter_pill_list/component.html.slim +0 -11
- data/app/components/mensa/header/component.html.slim +0 -8
- data/app/components/mensa/row_action/component.html.slim +0 -6
- data/app/components/mensa/search/component.html.slim +0 -21
- data/app/components/mensa/table/component.html.slim +0 -10
- data/app/components/mensa/table_row/component.html.slim +0 -11
- data/app/components/mensa/view/component.html.slim +0 -30
- data/app/components/mensa/views/component.html.slim +0 -52
- data/app/views/mensa/exports/_badge.html.slim +0 -5
- data/app/views/mensa/exports/_dialog.html.slim +0 -42
- data/app/views/mensa/exports/_list.html.slim +0 -29
- data/app/views/mensa/tables/filters/show.turbo_stream.slim +0 -36
- data/app/views/mensa/tables/show.html.slim +0 -4
- data/app/views/mensa/tables/show.turbo_stream.slim +0 -5
- data/app/views/mensa/tables/views/create.turbo_stream.slim +0 -11
- data/app/views/mensa/tables/views/destroy.turbo_stream.slim +0 -11
- 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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4370665a3982e3464306fd155b455aef8c62d2fda14c474771042a30af3038cb
|
|
4
|
+
data.tar.gz: 91c50234915ffeb886a6569575d33fa7b85eb453f1da8991e075ad46fe5101ac
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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.
|
|
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
|

|
|
6
7
|

|
|
@@ -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
|
-
|
|
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
|
-
```
|
|
116
|
-
|
|
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
|
|
132
|
+
- Setup your direnv, add the following to your `mise.toml`:
|
|
144
133
|
|
|
145
134
|
```
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
```
|
|
163
|
-
|
|
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
|
-
$
|
|
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>
|
|
@@ -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
|
|
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>
|