mensa 0.2.4 → 0.2.6
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/.devcontainer/Dockerfile +6 -2
- data/.devcontainer/compose.yaml +1 -1
- data/.devcontainer/devcontainer.json +31 -29
- data/.devcontainer/postCreate.sh +8 -0
- data/.devcontainer/postStart.sh +9 -0
- data/.gitignore +3 -1
- data/.zed/tasks.json +12 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +155 -153
- data/Procfile +1 -1
- data/README.md +95 -60
- data/app/assets/stylesheets/mensa/application.css +14 -11
- data/app/components/mensa/add_filter/component.css +110 -5
- data/app/components/mensa/add_filter/component.html.slim +10 -12
- data/app/components/mensa/add_filter/component.rb +8 -2
- data/app/components/mensa/add_filter/component_controller.js +697 -85
- data/app/components/mensa/cell/component.css +9 -0
- data/app/components/mensa/column_customizer/component.css +40 -0
- data/app/components/mensa/column_customizer/component.html.slim +14 -0
- data/app/components/mensa/column_customizer/component.rb +13 -0
- data/app/components/mensa/column_customizer/component_controller.js +383 -0
- data/app/components/mensa/control_bar/component.css +127 -4
- data/app/components/mensa/control_bar/component.html.slim +41 -14
- data/app/components/mensa/control_bar/component.rb +2 -6
- data/app/components/mensa/empty_state/component.css +20 -0
- data/app/components/mensa/empty_state/component.html.slim +7 -0
- data/app/components/mensa/empty_state/component.rb +18 -0
- data/app/components/mensa/filter_pill/component.css +23 -0
- data/app/components/mensa/filter_pill/component.html.slim +9 -0
- data/app/components/mensa/filter_pill/component.rb +24 -0
- data/app/components/mensa/filter_pill/component_controller.js +52 -0
- data/app/components/mensa/filter_pill_list/component.css +63 -0
- data/app/components/mensa/filter_pill_list/component.html.slim +11 -0
- data/app/components/mensa/{filter_list → filter_pill_list}/component.rb +1 -1
- data/app/components/mensa/filter_pill_list/component_controller.js +749 -0
- data/app/components/mensa/header/component.css +41 -43
- data/app/components/mensa/header/component.html.slim +7 -7
- data/app/components/mensa/header/component.rb +1 -1
- data/app/components/mensa/row_action/component.html.slim +2 -2
- data/app/components/mensa/row_action/component.rb +1 -1
- data/app/components/mensa/search/component.css +68 -9
- data/app/components/mensa/search/component.html.slim +19 -15
- data/app/components/mensa/search/component.rb +1 -1
- data/app/components/mensa/search/component_controller.js +39 -49
- data/app/components/mensa/selection/component_controller.js +147 -0
- data/app/components/mensa/table/component.css +28 -0
- data/app/components/mensa/table/component.html.slim +9 -6
- data/app/components/mensa/table/component.rb +1 -0
- data/app/components/mensa/table/component_controller.js +524 -76
- data/app/components/mensa/table_row/component.css +6 -0
- data/app/components/mensa/table_row/component.html.slim +8 -3
- data/app/components/mensa/table_row/component.rb +1 -1
- data/app/components/mensa/view/component.css +97 -29
- data/app/components/mensa/view/component.html.slim +23 -10
- data/app/components/mensa/view/component.rb +5 -0
- data/app/components/mensa/views/component.css +106 -13
- data/app/components/mensa/views/component.html.slim +51 -17
- data/app/components/mensa/views/component_controller.js +245 -20
- data/app/controllers/mensa/application_controller.rb +1 -1
- data/app/controllers/mensa/tables/batch_actions_controller.rb +24 -0
- data/app/controllers/mensa/tables/exports_controller.rb +96 -0
- data/app/controllers/mensa/tables/filters_controller.rb +6 -2
- data/app/controllers/mensa/tables/views_controller.rb +108 -0
- data/app/controllers/mensa/tables_controller.rb +5 -14
- data/app/helpers/mensa/application_helper.rb +4 -1
- data/app/javascript/mensa/application.js +2 -2
- data/app/javascript/mensa/controllers/application_controller.js +5 -21
- data/app/javascript/mensa/controllers/index.js +16 -7
- data/app/jobs/mensa/export_job.rb +77 -85
- data/app/models/mensa/export.rb +93 -0
- data/app/tables/mensa/action.rb +3 -1
- data/app/tables/mensa/base.rb +103 -17
- data/app/tables/mensa/batch_action.rb +27 -0
- data/app/tables/mensa/cell.rb +21 -6
- data/app/tables/mensa/column.rb +30 -25
- data/app/tables/mensa/config/action_dsl.rb +1 -1
- data/app/tables/mensa/config/batch_dsl.rb +13 -0
- data/app/tables/mensa/config/column_dsl.rb +1 -0
- data/app/tables/mensa/config/dsl_logic.rb +8 -4
- data/app/tables/mensa/config/filter_dsl.rb +4 -1
- data/app/tables/mensa/config/render_dsl.rb +1 -1
- data/app/tables/mensa/config/table_dsl.rb +14 -4
- data/app/tables/mensa/config/view_dsl.rb +2 -0
- data/app/tables/mensa/config_readers.rb +34 -3
- data/app/tables/mensa/filter.rb +94 -14
- data/app/tables/mensa/row.rb +1 -1
- data/app/tables/mensa/scope.rb +25 -13
- data/app/views/mensa/exports/_badge.html.slim +5 -0
- data/app/views/mensa/exports/_dialog.html.slim +42 -0
- data/app/views/mensa/exports/_list.html.slim +29 -0
- data/app/views/mensa/tables/filters/show.turbo_stream.slim +34 -6
- data/app/views/mensa/tables/show.html.slim +2 -0
- data/app/views/mensa/tables/show.turbo_stream.slim +1 -1
- data/app/views/mensa/tables/views/create.turbo_stream.slim +11 -0
- data/app/views/mensa/tables/views/destroy.turbo_stream.slim +11 -0
- data/app/views/mensa/tables/views/update.turbo_stream.slim +11 -0
- data/bin/setup +1 -1
- data/config/locales/en.yml +45 -1
- data/config/locales/nl.yml +46 -1
- data/config/routes.rb +7 -0
- data/db/migrate/20260604120000_create_mensa_exports.rb +25 -0
- data/docs/columns.png +0 -0
- data/docs/export.png +0 -0
- data/docs/filters.png +0 -0
- data/docs/table.png +0 -0
- data/lib/generators/mensa/tailwind_config_generator.rb +3 -3
- data/lib/generators/mensa/templates/config/initializers/mensa.rb +1 -1
- data/lib/mensa/configuration.rb +35 -15
- data/lib/mensa/engine.rb +15 -10
- data/lib/mensa/version.rb +1 -1
- data/lib/mensa.rb +2 -2
- data/lib/tasks/mensa_tasks.rake +1 -1
- data/mensa.gemspec +3 -2
- data/mise.toml +8 -0
- data/package-lock.json +0 -7
- metadata +60 -15
- data/app/components/mensa/filter/component_controller.js +0 -12
- data/app/components/mensa/filter_list/component.css +0 -14
- data/app/components/mensa/filter_list/component.html.slim +0 -14
- data/app/components/mensa/filter_list/component_controller.js +0 -14
- /data/{rubocop.yml → .rubocop.yml} +0 -0
data/README.md
CHANGED
|
@@ -4,6 +4,8 @@ Fast and awesome tables, with pagination, sorting, filtering and custom views.
|
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|

|
|
7
|
+

|
|
8
|
+

|
|
7
9
|
|
|
8
10
|
Features:
|
|
9
11
|
|
|
@@ -13,84 +15,105 @@ Features:
|
|
|
13
15
|
- [x] tables without headers (and without most of the above)
|
|
14
16
|
- [x] filtering of multiple columns
|
|
15
17
|
- [X] Hide filter icon in case there are no filters
|
|
18
|
+
- [X] column ordering
|
|
19
|
+
- [X] editing of existing filters
|
|
20
|
+
- [X] view selection and exports per view
|
|
21
|
+
- [X] multiple selection of rows and batch processing
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
- [ ]
|
|
19
|
-
- [ ]
|
|
20
|
-
- [ ] view selection and exports per view
|
|
21
|
-
- [ ] multiple selection of rows
|
|
23
|
+
Todo/Fixme:
|
|
24
|
+
- [ ] exports can be mailed - daily/weekly/monthly/quarterly/bi-yearly/yearly (time configurable)
|
|
25
|
+
- [ ] Search only works on table text columns
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
Nice to haves:
|
|
24
28
|
|
|
25
29
|
- [ ] group by
|
|
26
30
|
- [ ] sum/max/min
|
|
27
31
|
- [ ] tables backed by arrays (of ActiveModel)
|
|
28
32
|
|
|
29
|
-
Todo/Fixme:
|
|
30
|
-
|
|
31
|
-
- [ ] Search only works on table text columns
|
|
32
|
-
|
|
33
33
|
## Usage
|
|
34
34
|
|
|
35
35
|
Add tables in your app/tables folder, inheriting from ApplicationTable.
|
|
36
36
|
This in turn should inherit from Mensa::Base.
|
|
37
37
|
|
|
38
|
+
You can give columns an arbitrary name, it can match the database column, translations will be taken from `activerecord.attributes.<model>.<column>`:
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
en:
|
|
42
|
+
activerecord:
|
|
43
|
+
attributes:
|
|
44
|
+
user:
|
|
45
|
+
name: Full name
|
|
46
|
+
```
|
|
47
|
+
|
|
38
48
|
```ruby
|
|
39
49
|
class UserTable < ApplicationTable
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
scope -> { where(name: ...) }
|
|
54
|
-
end
|
|
50
|
+
model User # implicit from name
|
|
51
|
+
|
|
52
|
+
order name: :desc
|
|
53
|
+
|
|
54
|
+
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: ...) }
|
|
55
63
|
end
|
|
64
|
+
operators [:is, :isnt] # Optional, mensa tries to deduce this from the column information
|
|
65
|
+
end
|
|
56
66
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
column(:nr_of_roles) do
|
|
68
|
+
attribute "roles_count" # We use a database column here
|
|
69
|
+
end
|
|
60
70
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
# You can add one or more actions to a row
|
|
72
|
+
action :delete do
|
|
73
|
+
title "Delete row"
|
|
74
|
+
link { |user| user_path(user) }
|
|
75
|
+
icon "fa-regular fa-trash"
|
|
76
|
+
link_attributes data: {"turbo-confirm": "Are you sure you want to delete the user?", "turbo-method": :delete}
|
|
77
|
+
show ->(user) { true }
|
|
78
|
+
end
|
|
69
79
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
link { |user| edit_user_path(user) }
|
|
81
|
+
supports_views true # This table supports custom views
|
|
82
|
+
show_header true
|
|
83
|
+
view_columns_ordering false # Disabled for now
|
|
84
|
+
|
|
85
|
+
# Add system views
|
|
86
|
+
# Mensa will always create a systemview (:default) with name 'All' showing all records.
|
|
87
|
+
# If you want to rename it, for example because you don't show all records in your default scope, add it and give it a name like below.
|
|
88
|
+
view :default do
|
|
89
|
+
name "Default"
|
|
90
|
+
description "Some descriptive text"
|
|
91
|
+
end
|
|
92
|
+
view :concept do
|
|
93
|
+
name "Concept"
|
|
94
|
+
filter :state do
|
|
95
|
+
operator :is
|
|
96
|
+
value "concept"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
batch :confirm do
|
|
101
|
+
description "Confirm users"
|
|
102
|
+
process do |records|
|
|
103
|
+
ConfirmUsersJob.perform_later(records.to_a)
|
|
84
104
|
end
|
|
85
105
|
end
|
|
106
|
+
|
|
107
|
+
scope do
|
|
108
|
+
User.all
|
|
109
|
+
end
|
|
86
110
|
end
|
|
87
111
|
```
|
|
88
112
|
|
|
89
|
-
Currently mensa depends on satis, but that is something we might remove in a future version.
|
|
90
113
|
You can show your tables on the page using the following:
|
|
91
114
|
|
|
92
115
|
```slim
|
|
93
|
-
=
|
|
116
|
+
= table :users
|
|
94
117
|
```
|
|
95
118
|
|
|
96
119
|
#### Custom views
|
|
@@ -99,17 +122,16 @@ Custom views are views not defined by the developer (SystemViews) but by the end
|
|
|
99
122
|
|
|
100
123
|
Initial support for custom-views is there, but pretty rudimentary:
|
|
101
124
|
|
|
102
|
-
`Mensa::TableView.create(table: "users", name: "Guests",
|
|
125
|
+
`Mensa::TableView.create(table: "users", name: "Guests", config: {filters: {role: {value: "guest"}}})`
|
|
103
126
|
|
|
104
127
|
### Fast
|
|
105
128
|
|
|
106
|
-
Mensa selects only the data it needs, based on the columns. Sometimes it needs additional columns to do it's work, but you don't want them displayed.
|
|
107
|
-
This can be done by adding `internal true` to the column definition, or shorter: use `internal` instead
|
|
129
|
+
Mensa selects only the data it needs, based on the columns. Sometimes it needs additional columns to do it's work, but you don't want them displayed. This can be done by adding `internal true` to the column definition, or shorter: use `internal` instead. If your table scope joins an association, Mensa also auto-adds the foreign key column as internal when it is needed.
|
|
108
130
|
|
|
109
131
|
```ruby
|
|
110
|
-
internal :born_on
|
|
132
|
+
internal :born_on
|
|
111
133
|
column :age do
|
|
112
|
-
attribute "EXTRACT(YEAR FROM AGE(born_on))::int"
|
|
134
|
+
attribute "EXTRACT(YEAR FROM AGE(born_on))::int" # here born_on is used internally, so we ned to select is
|
|
113
135
|
end
|
|
114
136
|
```
|
|
115
137
|
|
|
@@ -124,26 +146,33 @@ end
|
|
|
124
146
|
export BUNDLE_RUBYGEMS__PKG__GITHUB__COM=ghp_xxxxxxw
|
|
125
147
|
export RBENV_VERSION=$(cat .ruby-version)
|
|
126
148
|
```
|
|
149
|
+
|
|
127
150
|
- Run `direnv allow`
|
|
151
|
+
|
|
152
|
+
If you use devcontainers:
|
|
128
153
|
- Open with Visual Studio Code (or with any other editor) and reopen in container.
|
|
129
154
|
- Run `bin/overmind s`
|
|
130
155
|
|
|
156
|
+
If you're not using devcontainers:
|
|
157
|
+
- Run `bin/overmind s`
|
|
158
|
+
|
|
131
159
|
### Docs
|
|
132
160
|
|
|
133
161
|
Using the following in your view will render Mensa::Table::Component
|
|
134
162
|
```slim
|
|
135
|
-
=
|
|
163
|
+
= table :users
|
|
136
164
|
```
|
|
137
165
|
|
|
138
166
|
The Mensa::Table::Component will render:
|
|
139
167
|
- Mensa::Search::Component
|
|
140
|
-
- Mensa::
|
|
168
|
+
- Mensa::FilterPillList::Component
|
|
169
|
+
- Mensa::FilterPill::Component
|
|
170
|
+
- Mensa::AddFilter::Component
|
|
141
171
|
- Mensa::Views::Component
|
|
142
172
|
- renders a views list
|
|
143
173
|
- Mensa::ControlBar::Component
|
|
144
174
|
- search icon
|
|
145
175
|
- filter icon
|
|
146
|
-
- condensed toggle
|
|
147
176
|
- export icon
|
|
148
177
|
- turbo-frame with the actual table you see (which is rendered by Mensa::View::Component)
|
|
149
178
|
|
|
@@ -173,6 +202,12 @@ Always use `bundle` to install the gem. Next use the install generator to instal
|
|
|
173
202
|
$ bin/rails g mensa:install
|
|
174
203
|
```
|
|
175
204
|
|
|
205
|
+
### Exports
|
|
206
|
+
|
|
207
|
+
Exporting is built into the table's control bar. Clicking the export button opens
|
|
208
|
+
a dialog that lists the user's previous downloads and lets them request a new
|
|
209
|
+
export (scope and CSV format).
|
|
210
|
+
|
|
176
211
|
## Contributing
|
|
177
212
|
|
|
178
213
|
```
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
@import
|
|
2
|
-
@import
|
|
3
|
-
@import
|
|
4
|
-
@import
|
|
5
|
-
@import
|
|
6
|
-
@import
|
|
7
|
-
@import
|
|
8
|
-
@import
|
|
9
|
-
@import
|
|
10
|
-
@import
|
|
11
|
-
@import
|
|
1
|
+
@import "../../../components/mensa/add_filter/component.css";
|
|
2
|
+
@import "../../../components/mensa/column_customizer/component.css";
|
|
3
|
+
@import "../../../components/mensa/cell/component.css";
|
|
4
|
+
@import "../../../components/mensa/control_bar/component.css";
|
|
5
|
+
@import "../../../components/mensa/empty_state/component.css";
|
|
6
|
+
@import "../../../components/mensa/filter_pill/component.css";
|
|
7
|
+
@import "../../../components/mensa/filter_pill_list/component.css";
|
|
8
|
+
@import "../../../components/mensa/header/component.css";
|
|
9
|
+
@import "../../../components/mensa/row_action/component.css";
|
|
10
|
+
@import "../../../components/mensa/search/component.css";
|
|
11
|
+
@import "../../../components/mensa/table/component.css";
|
|
12
|
+
@import "../../../components/mensa/table_row/component.css";
|
|
13
|
+
@import "../../../components/mensa/view/component.css";
|
|
14
|
+
@import "../../../components/mensa/views/component.css";
|
|
@@ -1,13 +1,118 @@
|
|
|
1
|
-
.
|
|
1
|
+
.mensa-table {
|
|
2
2
|
&__add_filter {
|
|
3
|
-
@apply relative;
|
|
3
|
+
@apply relative inline-flex items-center;
|
|
4
|
+
|
|
5
|
+
&__trigger {
|
|
6
|
+
/* Hidden by default; shown via sibling selector when filter pills exist */
|
|
7
|
+
@apply hidden items-center justify-center w-5 h-5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 cursor-pointer transition-colors text-sm;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
&__enter-hint {
|
|
11
|
+
display: none;
|
|
12
|
+
@apply text-xs text-gray-400 bg-gray-100 dark:bg-gray-700 rounded px-1.5 py-0.5 whitespace-nowrap;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
&__checkbox {
|
|
16
|
+
@apply h-4 w-4 flex-none rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 inline-block;
|
|
17
|
+
|
|
18
|
+
&--checked {
|
|
19
|
+
@apply bg-gray-900 border-gray-900 dark:bg-white dark:border-white;
|
|
20
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='white'%3E%3Cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3E%3C/svg%3E");
|
|
21
|
+
background-size: 100% 100%;
|
|
22
|
+
background-repeat: no-repeat;
|
|
23
|
+
background-position: center;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
4
26
|
|
|
5
27
|
&__popover_container {
|
|
6
|
-
@apply p-
|
|
28
|
+
@apply p-4 fixed z-50 w-72 rounded-lg bg-white dark:bg-gray-800 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm;
|
|
29
|
+
|
|
30
|
+
&__heading {
|
|
31
|
+
@apply font-semibold text-gray-900 dark:text-gray-100 mb-2;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
input&__input {
|
|
35
|
+
@apply w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 text-sm text-gray-900 dark:text-gray-100 shadow-sm placeholder:text-gray-400 focus:border-primary-500 focus:ring-2 focus:ring-primary-500 focus:outline-none;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&__separator {
|
|
39
|
+
@apply border-t border-gray-200 dark:border-gray-600 my-3 -mx-4;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
&__values {
|
|
43
|
+
@apply mb-2 max-h-[20rem] overflow-y-scroll;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&__value {
|
|
47
|
+
@apply flex items-center gap-2 py-1.5 px-2 rounded-md cursor-pointer text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 select-none;
|
|
48
|
+
|
|
49
|
+
&__check {
|
|
50
|
+
@apply w-4 flex-none inline-flex items-center justify-center text-xs;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
&__operators {
|
|
55
|
+
@apply mb-2;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
&__operator {
|
|
59
|
+
@apply flex items-center gap-2 py-1.5 rounded-md cursor-pointer text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 select-none;
|
|
60
|
+
|
|
61
|
+
&__check {
|
|
62
|
+
@apply w-4 flex-none inline-flex items-center justify-center text-xs;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
7
65
|
|
|
8
66
|
&__clear {
|
|
9
|
-
@apply text-gray-
|
|
67
|
+
@apply block mt-3 text-gray-500 dark:text-gray-400 no-underline hover:text-gray-700 dark:hover:text-gray-200 cursor-pointer;
|
|
10
68
|
}
|
|
11
69
|
}
|
|
12
70
|
}
|
|
13
|
-
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Show enter hint on hover or keyboard highlight — covers column list and value popover */
|
|
74
|
+
.mensa-table__add_filter li:hover .mensa-table__add_filter__enter-hint,
|
|
75
|
+
.mensa-table__add_filter li.highlighted .mensa-table__add_filter__enter-hint {
|
|
76
|
+
display: inline-flex;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Keyboard highlight background — mirrors the hover style */
|
|
80
|
+
.mensa-table__add_filter li.highlighted {
|
|
81
|
+
@apply bg-gray-100 dark:bg-gray-700;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Show + when user-added (non-view-filter) committed pills are present.
|
|
85
|
+
User pills are always visible, so no ancestor visibility check needed. */
|
|
86
|
+
.mensa-table__search-bar__pills-area:has(
|
|
87
|
+
.mensa-filter-pill:not([data-view-filter="true"]):not(
|
|
88
|
+
.mensa-filter-pill--pending
|
|
89
|
+
)
|
|
90
|
+
):not(:has(.mensa-filter-pill--pending))
|
|
91
|
+
.mensa-table__add_filter
|
|
92
|
+
.mensa-table__add_filter__trigger {
|
|
93
|
+
@apply inline-flex;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Show + when view-defined filter pills are present AND currently visible
|
|
97
|
+
(eye toggle on). Hidden view filters must not reveal the + button. */
|
|
98
|
+
.mensa-table:not(.mensa-table--view-filters-hidden)
|
|
99
|
+
.mensa-table__search-bar__pills-area:has(
|
|
100
|
+
.mensa-filter-pill[data-view-filter="true"]
|
|
101
|
+
):not(:has(.mensa-filter-pill--pending))
|
|
102
|
+
.mensa-table__add_filter
|
|
103
|
+
.mensa-table__add_filter__trigger {
|
|
104
|
+
@apply inline-flex;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* While a filter is being configured, collapse the add-filter wrapper so it
|
|
108
|
+
doesn't create an extra flex gap between the pending pill and the search input.
|
|
109
|
+
The value popover inside is position:fixed so display:contents doesn't affect it. */
|
|
110
|
+
.mensa-table__search-bar__pills-area:has(.mensa-filter-pill--pending)
|
|
111
|
+
.mensa-table__add_filter {
|
|
112
|
+
display: contents;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Pending pill while the value popover is open — rounded on both sides (no remove button) */
|
|
116
|
+
.mensa-filter-pill--pending .mensa-filter-pill__chip {
|
|
117
|
+
@apply rounded-md opacity-70;
|
|
118
|
+
}
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
.
|
|
2
|
-
button.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
span.pointer-events-none.absolute.inset-y-0.right-0.flex.items-center.pr-2
|
|
6
|
-
div class=Mensa.config.icons[:filters_add_filter]
|
|
7
|
-
|
|
8
|
-
ul.hidden.absolute.z-10.mt-1.max-h-60.w-64.overflow-auto.rounded-md.bg-white.dark:bg-gray-800.py-1.text-base.shadow-lg.ring-1.ring-black.ring-opacity-5.focus:outline-none.sm:text-sm data-mensa-add-filter-target="filterList"
|
|
1
|
+
.mensa-table__add_filter data-controller="mensa-add-filter" data-mensa-add-filter-mensa-filter-pill-list-outlet="#mensa-filter-pill-list-#{table.table_id}" data-mensa-add-filter-operator-labels-value=operator_labels.to_json
|
|
2
|
+
button.mensa-table__add_filter__trigger[type="button" title=t("mensa.add_filter.add", default: "Add filter") data-action="mensa-add-filter#openAllColumns"]
|
|
3
|
+
i.fa-solid.fa-circle-plus
|
|
4
|
+
ul.hidden.fixed.z-50.max-h-96.overflow-auto.rounded-lg.bg-white.dark:bg-gray-800.p-2.text-base.shadow-lg.ring-1.ring-black.ring-opacity-5.focus:outline-none.sm:text-sm data-mensa-add-filter-target="filterList"
|
|
9
5
|
- table.columns.select(&:filter?).each do |column|
|
|
10
|
-
li
|
|
11
|
-
.label.font-normal.block.truncate
|
|
6
|
+
li.text-gray-900.dark:text-gray-300.hover:bg-gray-100.dark:hover:bg-gray-700.relative.cursor-default.select-none.rounded-md.py-2.px-3.flex.items-center.gap-2 data-mensa-add-filter-target="filterListItem" data-action="click->mensa-add-filter#selectColumn mouseenter->mensa-add-filter#columnItemHovered" data-filter-column-name=column.name
|
|
7
|
+
.label.font-normal.block.truncate.flex-1
|
|
12
8
|
= column.human_name
|
|
13
|
-
.check.hidden.text-primary-600.
|
|
9
|
+
.check.hidden.text-primary-600.flex.items-center
|
|
14
10
|
.fal.fa-check
|
|
11
|
+
span.mensa-table__add_filter__enter-hint
|
|
12
|
+
| ↵ Enter
|
|
15
13
|
|
|
16
|
-
div.hidden.
|
|
14
|
+
div.hidden.mensa-table__add_filter__popover_container data-mensa-add-filter-target="valuePopover" id="mensa-filter-pill-value-#{SecureRandom.base36}"
|
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
module Mensa
|
|
4
4
|
module AddFilter
|
|
5
|
-
class Component <
|
|
5
|
+
class Component < Mensa::ApplicationComponent
|
|
6
6
|
attr_reader :table
|
|
7
7
|
|
|
8
8
|
def initialize(table:)
|
|
9
9
|
@table = table
|
|
10
10
|
end
|
|
11
|
+
|
|
12
|
+
def operator_labels
|
|
13
|
+
Mensa::Filter.OPERATORS.to_h do |name, label, _requires_value|
|
|
14
|
+
[name, label]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
11
17
|
end
|
|
12
18
|
end
|
|
13
|
-
end
|
|
19
|
+
end
|