mensa 0.3.2 → 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 -1
- data/Gemfile.lock +1 -17
- data/README.md +16 -42
- data/app/components/mensa/add_filter/component.html.erb +31 -0
- 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/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/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 +0 -1
- data/lib/mensa/version.rb +1 -1
- data/mensa.gemspec +2 -4
- metadata +29 -54
- 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
data/Gemfile.lock
CHANGED
|
@@ -1,14 +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
8
|
pg (>= 1.6)
|
|
9
9
|
rails (>= 7.1)
|
|
10
10
|
rubyzip (>= 1.2.2)
|
|
11
|
-
slim
|
|
12
11
|
stimulus-rails
|
|
13
12
|
tailwindcss-rails (~> 3.3)
|
|
14
13
|
textacular (>= 5)
|
|
@@ -307,9 +306,6 @@ GEM
|
|
|
307
306
|
rexml (~> 3.2, >= 3.2.5)
|
|
308
307
|
rubyzip (>= 1.2.2, < 4.0)
|
|
309
308
|
websocket (~> 1.0)
|
|
310
|
-
slim (5.2.1)
|
|
311
|
-
temple (~> 0.10.0)
|
|
312
|
-
tilt (>= 2.1.0)
|
|
313
309
|
sprockets (4.2.2)
|
|
314
310
|
concurrent-ruby (~> 1.0)
|
|
315
311
|
logger
|
|
@@ -318,14 +314,6 @@ GEM
|
|
|
318
314
|
actionpack (>= 6.1)
|
|
319
315
|
activesupport (>= 6.1)
|
|
320
316
|
sprockets (>= 3.0.0)
|
|
321
|
-
sqlite3 (2.9.4-aarch64-linux-gnu)
|
|
322
|
-
sqlite3 (2.9.4-aarch64-linux-musl)
|
|
323
|
-
sqlite3 (2.9.4-arm-linux-gnu)
|
|
324
|
-
sqlite3 (2.9.4-arm-linux-musl)
|
|
325
|
-
sqlite3 (2.9.4-arm64-darwin)
|
|
326
|
-
sqlite3 (2.9.4-x86_64-darwin)
|
|
327
|
-
sqlite3 (2.9.4-x86_64-linux-gnu)
|
|
328
|
-
sqlite3 (2.9.4-x86_64-linux-musl)
|
|
329
317
|
standard (1.54.0)
|
|
330
318
|
language_server-protocol (~> 3.17.0.2)
|
|
331
319
|
lint_roller (~> 1.0)
|
|
@@ -349,11 +337,9 @@ GEM
|
|
|
349
337
|
tailwindcss-ruby (3.4.19-arm64-darwin)
|
|
350
338
|
tailwindcss-ruby (3.4.19-x86_64-darwin)
|
|
351
339
|
tailwindcss-ruby (3.4.19-x86_64-linux)
|
|
352
|
-
temple (0.10.4)
|
|
353
340
|
textacular (5.7.0)
|
|
354
341
|
activerecord (>= 5.0)
|
|
355
342
|
thor (1.5.0)
|
|
356
|
-
tilt (2.7.0)
|
|
357
343
|
timeout (0.6.1)
|
|
358
344
|
tsort (0.2.0)
|
|
359
345
|
turbo-rails (2.0.23)
|
|
@@ -404,9 +390,7 @@ DEPENDENCIES
|
|
|
404
390
|
ruby-lsp
|
|
405
391
|
ruby-lsp-rails
|
|
406
392
|
selenium-webdriver (~> 4.17)
|
|
407
|
-
slim (~> 5.2)
|
|
408
393
|
sprockets-rails
|
|
409
|
-
sqlite3 (~> 2.8)
|
|
410
394
|
standard
|
|
411
395
|
|
|
412
396
|
BUNDLED WITH
|
data/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Mensa
|
|
2
2
|
|
|
3
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
|

|
|
@@ -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>
|
|
@@ -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 %>
|
|
@@ -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>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<% current_view = table.all_views.find { |v| v.id == table.table_view&.id } || table.all_views.first %>
|
|
2
|
+
<% current_view_name = current_view&.name || t(".all", default: "All") %>
|
|
3
|
+
<% current_view_id = current_view&.id || "" %>
|
|
4
|
+
|
|
5
|
+
<div class="mensa-table__views"
|
|
6
|
+
id="mensa-views-<%= table.table_id %>"
|
|
7
|
+
data-mensa-table-target="views"
|
|
8
|
+
data-controller="mensa-views"
|
|
9
|
+
data-mensa-views-mensa-filter-pill-list-outlet="#mensa-filter-pill-list-<%= table.table_id %>"
|
|
10
|
+
data-mensa-views-table-id-value="<%= table.table_id %>"
|
|
11
|
+
data-mensa-views-views-url-value="<%= helpers.mensa.table_views_path(table.name) %>">
|
|
12
|
+
<button class="mensa-table__views__trigger" type="button" data-action="mensa-views#toggleDropdown" data-mensa-views-target="trigger" aria-haspopup="listbox">
|
|
13
|
+
<span class="mensa-table__views__trigger-label" data-mensa-views-target="triggerLabel"><%= current_view_name %></span>
|
|
14
|
+
<i class="fa-solid fa-sort mensa-table__views__trigger-icon"></i>
|
|
15
|
+
</button>
|
|
16
|
+
|
|
17
|
+
<div class="mensa-table__views__dropdown hidden" data-mensa-views-target="dropdown">
|
|
18
|
+
<ul role="listbox">
|
|
19
|
+
<% table.all_views.each do |view| %>
|
|
20
|
+
<% is_selected = view.id == table.table_view&.id || (view.id == :default && table.table_view.blank?) %>
|
|
21
|
+
<% is_user_view = view.id.is_a?(String) && view.id.match?(/\A[0-9a-f-]{32,}\z/i) %>
|
|
22
|
+
<li class="mensa-table__views__option" role="option" data-view-id="<%= view.id %>" data-view-name="<%= view.name %>" data-mensa-views-target="view">
|
|
23
|
+
<button class="mensa-table__views__option-btn" type="button" data-action="mensa-views#select" data-view-id="<%= view.id %>">
|
|
24
|
+
<i class="mensa-table__views__option-check fa-solid fa-check<%= " invisible" unless is_selected %>"></i>
|
|
25
|
+
<span><%= view.name %></span>
|
|
26
|
+
</button>
|
|
27
|
+
<% if is_user_view %>
|
|
28
|
+
<button class="mensa-table__views__option-menu" type="button" data-action="mensa-views#toggleSubmenu" data-view-id="<%= view.id %>" title="View options">
|
|
29
|
+
<i class="fa-solid fa-ellipsis"></i>
|
|
30
|
+
</button>
|
|
31
|
+
<% else %>
|
|
32
|
+
<div class="mensa-table__views__option-system">
|
|
33
|
+
<i class="fa-solid fa-ban"></i>
|
|
34
|
+
</div>
|
|
35
|
+
<% end %>
|
|
36
|
+
</li>
|
|
37
|
+
<% end %>
|
|
38
|
+
</ul>
|
|
39
|
+
|
|
40
|
+
<div class="mensa-table__views__submenu hidden" data-mensa-views-target="submenu">
|
|
41
|
+
<button class="mensa-table__views__submenu-item" type="button" data-action="mensa-views#renameView">
|
|
42
|
+
<i class="fa-solid fa-pencil"></i>
|
|
43
|
+
<%= t(".rename_view", default: "Rename view") %>
|
|
44
|
+
</button>
|
|
45
|
+
<button class="mensa-table__views__submenu-item" type="button" data-action="mensa-views#duplicateView">
|
|
46
|
+
<i class="fa-solid fa-copy"></i>
|
|
47
|
+
<%= t(".duplicate_view", default: "Duplicate view") %>
|
|
48
|
+
</button>
|
|
49
|
+
<button class="mensa-table__views__submenu-item mensa-table__views__submenu-item--danger" type="button" data-action="mensa-views#deleteView">
|
|
50
|
+
<i class="fa-solid fa-trash"></i>
|
|
51
|
+
<%= t(".delete_view", default: "Delete view") %>
|
|
52
|
+
</button>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<dialog class="mensa-table__views__rename-dialog" data-mensa-views-target="renameDialog" data-action="click->mensa-views#renameDialogBackdrop">
|
|
57
|
+
<form class="mensa-table__views__rename-form" data-action="submit->mensa-views#confirmRename">
|
|
58
|
+
<input type="hidden" data-mensa-views-target="renameViewId">
|
|
59
|
+
<h3 class="mensa-table__views__rename-title"><%= t(".rename_view", default: "Rename view") %></h3>
|
|
60
|
+
<label class="mensa-table__views__rename-label">
|
|
61
|
+
<%= t(".view_name", default: "Name") %>
|
|
62
|
+
<input class="mensa-table__views__rename-input" type="text" required placeholder="<%= t(".view_name_placeholder", default: "View name") %>" data-mensa-views-target="renameInput">
|
|
63
|
+
</label>
|
|
64
|
+
<div class="mensa-table__views__rename-actions">
|
|
65
|
+
<button class="mensa-table__views__rename-btn mensa-table__views__rename-btn--secondary" type="button" data-action="mensa-views#cancelRename">
|
|
66
|
+
<%= t(".cancel", default: "Cancel") %>
|
|
67
|
+
</button>
|
|
68
|
+
<button class="mensa-table__views__rename-btn mensa-table__views__rename-btn--primary" type="submit">
|
|
69
|
+
<%= t(".rename", default: "Rename") %>
|
|
70
|
+
</button>
|
|
71
|
+
</div>
|
|
72
|
+
</form>
|
|
73
|
+
</dialog>
|
|
74
|
+
</div>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<% count = Mensa::Export.completed_count(table_name, user) %>
|
|
2
|
+
<span id="<%= Mensa::Export.badge_dom_id(table_name, user) %>"
|
|
3
|
+
class="mensa-table__control_bar__badge <%= 'hidden' unless count.positive? %>"
|
|
4
|
+
aria-hidden="<%= count.positive? ? 'false' : 'true' %>">
|
|
5
|
+
<%= count %>
|
|
6
|
+
</span>
|