warped 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -0
- data/Gemfile.lock +2 -2
- data/README.md +104 -0
- data/app/assets/config/warped_manifest.js +2 -0
- data/app/assets/javascript/warped/controllers/filter_controller.js +76 -0
- data/app/assets/javascript/warped/controllers/filters_controller.js +21 -0
- data/app/assets/javascript/warped/index.js +2 -0
- data/app/assets/stylesheets/warped/application.css +15 -0
- data/app/assets/stylesheets/warped/base.css +23 -0
- data/app/assets/stylesheets/warped/filters.css +115 -0
- data/app/assets/stylesheets/warped/pagination.css +74 -0
- data/app/assets/stylesheets/warped/search.css +33 -0
- data/app/assets/stylesheets/warped/table.css +114 -0
- data/app/views/warped/_actions.html.erb +9 -0
- data/app/views/warped/_cell.html.erb +3 -0
- data/app/views/warped/_column.html.erb +35 -0
- data/app/views/warped/_filters.html.erb +21 -0
- data/app/views/warped/_hidden_fields.html.erb +19 -0
- data/app/views/warped/_pagination.html.erb +34 -0
- data/app/views/warped/_row.html.erb +19 -0
- data/app/views/warped/_search.html.erb +21 -0
- data/app/views/warped/_table.html.erb +52 -0
- data/app/views/warped/filters/_filter.html.erb +40 -0
- data/config/importmap.rb +3 -0
- data/docs/controllers/FILTERABLE.md +82 -3
- data/docs/controllers/views/PARTIALS.md +285 -0
- data/lib/warped/api/filter/base/value.rb +52 -0
- data/lib/warped/api/filter/base.rb +84 -0
- data/lib/warped/api/filter/boolean.rb +41 -0
- data/lib/warped/api/filter/date.rb +26 -0
- data/lib/warped/api/filter/date_time.rb +32 -0
- data/lib/warped/api/filter/decimal.rb +31 -0
- data/lib/warped/api/filter/factory.rb +38 -0
- data/lib/warped/api/filter/integer.rb +38 -0
- data/lib/warped/api/filter/string.rb +25 -0
- data/lib/warped/api/filter/time.rb +25 -0
- data/lib/warped/api/filter.rb +14 -0
- data/lib/warped/api/sort/value.rb +40 -0
- data/lib/warped/api/sort.rb +65 -0
- data/lib/warped/controllers/filterable/ui.rb +10 -32
- data/lib/warped/controllers/filterable.rb +75 -42
- data/lib/warped/controllers/pageable/ui.rb +13 -3
- data/lib/warped/controllers/pageable.rb +1 -1
- data/lib/warped/controllers/searchable/ui.rb +3 -1
- data/lib/warped/controllers/sortable/ui.rb +21 -26
- data/lib/warped/controllers/sortable.rb +53 -33
- data/lib/warped/controllers/tabulatable/ui.rb +4 -0
- data/lib/warped/controllers/tabulatable.rb +6 -9
- data/lib/warped/engine.rb +19 -0
- data/lib/warped/queries/filter.rb +3 -3
- data/lib/warped/table/action.rb +33 -0
- data/lib/warped/table/column.rb +34 -0
- data/lib/warped/version.rb +1 -1
- data/lib/warped.rb +1 -0
- data/warped.gemspec +1 -1
- metadata +44 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c1c6444d0d93fbbfa88436418e69edc9e44e228402d56becbc99bd7eae122f14
|
4
|
+
data.tar.gz: 4eeeb9b833549db2c33a67fa73627e3e55f27dda5178d9f2f377b989f64739a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 486f3074c354576fb33e04df666248b27e4b0f2dd13a6ce1256c9ec66c9ad786a158010b199b80f11761b5c5bcc83af5ee3f6c01b5a631de83da53a706d5c520
|
7
|
+
data.tar.gz: 2a3f730c41a5f42c92aac9c3e2708caf0f803f157771a25be75b524304ec1f7203056ae4f7bc40675b7c2aef779f7dce7a2e54cb6a494ded18261361eef84237
|
data/.rubocop.yml
CHANGED
@@ -21,6 +21,7 @@ Layout/LineLength:
|
|
21
21
|
Metrics/AbcSize:
|
22
22
|
Exclude:
|
23
23
|
- "lib/warped/controllers/filterable/ui.rb"
|
24
|
+
- "lib/warped/controllers/pageable/ui.rb"
|
24
25
|
- "lib/warped/controllers/sortable/ui.rb"
|
25
26
|
- "lib/warped/emails/components/**/*.rb"
|
26
27
|
- "lib/warped/queries/filter.rb"
|
@@ -34,8 +35,13 @@ Metrics/ParameterLists:
|
|
34
35
|
Exclude:
|
35
36
|
- "lib/warped/emails/components/**/*.rb"
|
36
37
|
|
38
|
+
Metrics/PerceivedComplexity:
|
39
|
+
Exclude:
|
40
|
+
- "lib/warped/controllers/pageable/ui.rb"
|
41
|
+
|
37
42
|
Metrics/CyclomaticComplexity:
|
38
43
|
Exclude:
|
44
|
+
- "lib/warped/controllers/pageable/ui.rb"
|
39
45
|
- "lib/warped/queries/filter.rb"
|
40
46
|
|
41
47
|
Metrics/MethodLength:
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -15,6 +15,34 @@ Then run the generator to create the configuration file:
|
|
15
15
|
|
16
16
|
The generator will create a file at `config/initializers/warped.rb` with the default configuration.
|
17
17
|
|
18
|
+
### Installation for rails fullstack apps
|
19
|
+
|
20
|
+
For using the views provided by the gem, your app will need to have the following:
|
21
|
+
1. [rails/importmap-rails](https://github.com/rails/importmap-rails) configured
|
22
|
+
2. [hotwired/stimulus-rails](https://github.com/hotwired/stimulus-rails) configured
|
23
|
+
|
24
|
+
Add the following to your `config/importmap.rb`:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
pin_all_from "app/javascript/controllers/warped", under: "controllers/warped"
|
28
|
+
```
|
29
|
+
|
30
|
+
> This will import all the stimulus controllers provided by the gem.
|
31
|
+
|
32
|
+
Add the following to your `app/javascript/controllers/index.js`, bellow the `eagerLoadControllersFrom("controllers", application)` line:
|
33
|
+
```javascript
|
34
|
+
eagerLoadControllersFrom("warped/controllers", application)
|
35
|
+
```
|
36
|
+
|
37
|
+
Include the css provided by the gem in your `app/views/layouts/application.html.erb`:
|
38
|
+
```erb
|
39
|
+
<%= stylesheet_link_tag "warped/base" %>
|
40
|
+
<%= stylesheet_link_tag "warped/table" %>
|
41
|
+
<%= stylesheet_link_tag "warped/search" %>
|
42
|
+
<%= stylesheet_link_tag "warped/filters" %>
|
43
|
+
<%= stylesheet_link_tag "warped/pagination" %>
|
44
|
+
```
|
45
|
+
|
18
46
|
## Usage
|
19
47
|
|
20
48
|
Warped provides utilities for making it easier to develop rails applications. The utilities are organized into modules and can be used by including the module in the class that needs the utility.
|
@@ -56,6 +84,8 @@ GET /users?name=John
|
|
56
84
|
GET /users?email=john@example.com
|
57
85
|
GET /users?created_at=2021-01-01
|
58
86
|
```
|
87
|
+
> [!TIP]
|
88
|
+
> It's highly recommended to use the type-safe filter methods provided by the gem. This prevents invalid queries from being executed on the database. See the [Filterable documentation](docs/controllers/FILTERABLE.md) for more information.
|
59
89
|
|
60
90
|
[Complete documentation for Warped::Controllers::Filterable](docs/controllers/FILTERABLE.md).
|
61
91
|
|
@@ -113,6 +143,8 @@ class UsersController < ApplicationController
|
|
113
143
|
end
|
114
144
|
end
|
115
145
|
```
|
146
|
+
> [!TIP]
|
147
|
+
> It's highly recommended to use the type-safe sort methods provided by the gem. This prevents invalid queries from being executed on the database. See the [Sortable documentation](docs/controllers/SORTABLE.md) for more information.
|
116
148
|
|
117
149
|
This will use the query parameter `sort_key` and `sort_direction` to sort the records.
|
118
150
|
- The default sort direction is `desc`.
|
@@ -204,6 +236,78 @@ Just like `paginate`, when calling the `tabulate` method in the controller actio
|
|
204
236
|
|
205
237
|
[Complete documentation for Warped::Controllers::Tabulatable](docs/controllers/TABULATABLE.md).
|
206
238
|
|
239
|
+
### Views
|
240
|
+
|
241
|
+
Warped comes with a set of partials and stimulus controllers that can be used to make development of index views easier/faster.
|
242
|
+
|
243
|
+
In order to use the views provided by the gem, warped provides ::Ui modules for each of the before menttioned concerns. `These Warped::Controllers::<ConcernName>::Ui` provide the helper methods needed by the partials in order to work.
|
244
|
+
|
245
|
+
The partials are:
|
246
|
+
- `Warped::Controllers::Filterable::Ui` -> `warped/_filters.html.erb`
|
247
|
+
- `Warped::Controllers::Searchable::Ui` -> `warped/_search.html.erb`
|
248
|
+
- `Warped::Controllers::Pageable::Ui` -> `warped/_pagination.html.erb`
|
249
|
+
- `Warped::Controllers::Tabulatable::Ui` -> `warped/_table.html.erb`
|
250
|
+
|
251
|
+
Example:
|
252
|
+
```ruby
|
253
|
+
# app/models/user.rb
|
254
|
+
class User < ApplicationRecord
|
255
|
+
# first_name :string
|
256
|
+
# last_name :string
|
257
|
+
# email :string
|
258
|
+
# created_at :datetime
|
259
|
+
|
260
|
+
scope :search, ->(query) { where('first_name LIKE ? OR last_name LIKE ? OR email LIKE ?', "%#{query}%", "%#{query}%", "%#{query}%") }
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
# app/controllers/users_controller.rb
|
265
|
+
class UsersController < ApplicationControlelr
|
266
|
+
include Warped::Controllers::Tabulatable::Ui
|
267
|
+
|
268
|
+
tabulatable_by name: { kind: :string }, email: { kind: :string }, created_at: { kind: :date_time }
|
269
|
+
|
270
|
+
def index
|
271
|
+
@users = tabulate(User.all)
|
272
|
+
end
|
273
|
+
|
274
|
+
def show
|
275
|
+
@user = User.find(params[:id])
|
276
|
+
end
|
277
|
+
|
278
|
+
def destroy
|
279
|
+
User.find(params[:id]).destroy
|
280
|
+
redirect_to users_path
|
281
|
+
end
|
282
|
+
end
|
283
|
+
```
|
284
|
+
|
285
|
+
```erb
|
286
|
+
# app/views/users/index.html.erb
|
287
|
+
<%= render "warped/table", collection: @users,
|
288
|
+
path: users_path,
|
289
|
+
columns: [
|
290
|
+
Warped::Table::Column.new(:id, 'ID'),
|
291
|
+
Warped::Table::Column.new(:full_name, 'Full Name', method: ->(user) { [user.first_name, user.last_name].compact_blank.join(" ") }),
|
292
|
+
Warped::Table::Column.new(:email),
|
293
|
+
Warped::Table::Column.new(:created_at, 'Created At', method: ->(user) { user.created_at.strftime('%Y-%m-%d') })
|
294
|
+
],
|
295
|
+
actions: [
|
296
|
+
Warped::Table::Action.new('Show', ->(user) { user_path(user) }),
|
297
|
+
Warped::Table::Action.new('Delete', ->(user) { user_path(user) }, turbo_method: :delete, turbo_confirm: 'Are you sure?')
|
298
|
+
],
|
299
|
+
turbo_action: :replace
|
300
|
+
%>
|
301
|
+
```
|
302
|
+
The code above, renders a table with:
|
303
|
+
- The columns `ID`, `Full Name`, `Email`, and `Created At`, all of which are sortable.
|
304
|
+
- The actions `Show` and `Delete` for each user. The action `Delete` will use the `DELETE` method and will ask for confirmation before executing the action.
|
305
|
+
- The filters for the `name`, `email`, and `created_at` fields.
|
306
|
+
- A search input that will search the users by the term provided.
|
307
|
+
- A pagination component that will paginate the users, showing 10 users per page by default.
|
308
|
+
- The table filtering/sorting/searching/pagination will be done using the [turbo-action=replace](https://turbo.hotwired.dev/handbook/frames#promoting-a-frame-navigation-to-a-page-visit).
|
309
|
+
|
310
|
+
[Complete documentation for Warped built-in Partials](docs/controllers/views/PARTIALS.md).
|
207
311
|
### Services
|
208
312
|
|
209
313
|
The gem provides a `Warped::Service::Base` class that can be used to create services in a rails application.
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class FilterController extends Controller {
|
4
|
+
static targets = ["relation", "relationInput", "value", "valueInput", "panel", "badgeValue"]
|
5
|
+
static outlets = ["filter"]
|
6
|
+
static classes = ["empty", "collapsed"]
|
7
|
+
|
8
|
+
|
9
|
+
clickOutside(event) {
|
10
|
+
if (!this.element.contains(event.target)) {
|
11
|
+
this.close();
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
toggle() {
|
16
|
+
this.collapsedClasses.forEach(c => this.panelTarget.classList.toggle(c))
|
17
|
+
|
18
|
+
this.filterOutlets.forEach(outlet => {
|
19
|
+
if (this != outlet) {
|
20
|
+
outlet.close();
|
21
|
+
}
|
22
|
+
})
|
23
|
+
}
|
24
|
+
|
25
|
+
close() {
|
26
|
+
this.panelTarget.classList.add(...this.collapsedClasses)
|
27
|
+
}
|
28
|
+
|
29
|
+
collapsePanel() {
|
30
|
+
this.panelTarget.classList.add(...this.collapsedClasses)
|
31
|
+
}
|
32
|
+
|
33
|
+
uncollapsePanel() {
|
34
|
+
this.panelTarget.classList.remove(...this.collapsedClasses)
|
35
|
+
}
|
36
|
+
|
37
|
+
changeRelation() {
|
38
|
+
if (this.valueInputTarget.value) {
|
39
|
+
this.relationTarget.textContent = `${this.relationInputTarget.value}:`
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
changeValue() {
|
44
|
+
const currentFilterValue = this.valueInputTarget.value.trim()
|
45
|
+
|
46
|
+
this.valueTarget.textContent = currentFilterValue
|
47
|
+
this.changeRelation()
|
48
|
+
|
49
|
+
if (currentFilterValue) {
|
50
|
+
this.badgeValueTarget.classList.remove(...this.collapsedClasses)
|
51
|
+
this.element.classList.remove(this.emptyClass)
|
52
|
+
} else {
|
53
|
+
this.relationTarget.textContent = "";
|
54
|
+
this.badgeValueTarget.classList.add(...this.collapsedClasses)
|
55
|
+
this.element.classList.add(this.emptyClass)
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
clear(submit = true) {
|
60
|
+
this.valueTarget.textContent = ""
|
61
|
+
this.valueInputTarget.value = ""
|
62
|
+
this.relationInputTarget.value = ""
|
63
|
+
this.badgeValueTarget.classList.add("hidden")
|
64
|
+
this.element.classList.add(this.emptyClass)
|
65
|
+
|
66
|
+
if (submit) {
|
67
|
+
this.form.requestSubmit();
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
get form() {
|
72
|
+
return this.element.closest("form")
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
export { FilterController }
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class FiltersController extends Controller {
|
4
|
+
static outlets = ["filter"]
|
5
|
+
|
6
|
+
clearAll(event) {
|
7
|
+
event.preventDefault();
|
8
|
+
|
9
|
+
this.filterOutlets.forEach(outlet => {
|
10
|
+
outlet.clear(false);
|
11
|
+
})
|
12
|
+
|
13
|
+
this.form.requestSubmit();
|
14
|
+
}
|
15
|
+
|
16
|
+
get form() {
|
17
|
+
return this.element;
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
export { FiltersController }
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,23 @@
|
|
1
|
+
:root {
|
2
|
+
/* colors */
|
3
|
+
--warped-base: white;
|
4
|
+
--warped-base-rgb: 255, 255, 255;
|
5
|
+
|
6
|
+
--warped-neutral: #1F2937;
|
7
|
+
--warped-neutral-rgb: 31, 41, 55;
|
8
|
+
|
9
|
+
--warped-neutral-content: #fbfbfb;
|
10
|
+
--warped-neutral-content-rgb: 251, 251, 251;
|
11
|
+
|
12
|
+
--warped-primary: #2563EB;
|
13
|
+
--warped-primary-rgb: 37, 99, 235;
|
14
|
+
|
15
|
+
--warped-primary-content: #fbfbfb;
|
16
|
+
--warped-primary-content-rgb: 251, 251, 251;
|
17
|
+
|
18
|
+
--warped-danger: #EF4444;
|
19
|
+
--warped-danger-rgb: 239, 68, 68;
|
20
|
+
|
21
|
+
/* roundness */
|
22
|
+
--warped-roundness: 0.5rem;
|
23
|
+
}
|
@@ -0,0 +1,115 @@
|
|
1
|
+
.warped-filters {
|
2
|
+
width: 100%;
|
3
|
+
display: flex;
|
4
|
+
gap: 0.5rem;
|
5
|
+
background-color: var(--warped-base);
|
6
|
+
border-radius: var(--warped-roundness);
|
7
|
+
flex-wrap: wrap
|
8
|
+
}
|
9
|
+
|
10
|
+
.warped-filters--submit {
|
11
|
+
background-color: var(--warped-primary);
|
12
|
+
color: var(--warped-primary-content);
|
13
|
+
padding: 0.5rem;
|
14
|
+
border-radius: var(--warped-roundness);
|
15
|
+
font-size: 0.875rem;
|
16
|
+
cursor: pointer;
|
17
|
+
}
|
18
|
+
|
19
|
+
.warped-filters--clear {
|
20
|
+
background-color: var(--warped-neutral);
|
21
|
+
color: var(--warped-neutral-content);
|
22
|
+
padding: 0.5rem;
|
23
|
+
border-radius: var(--warped-roundness);
|
24
|
+
font-size: 0.875rem;
|
25
|
+
cursor: pointer;
|
26
|
+
}
|
27
|
+
|
28
|
+
.warped-filters--filter {
|
29
|
+
display: flex;
|
30
|
+
gap: 0.25rem;
|
31
|
+
height: 100%;
|
32
|
+
border-radius: var(--warped-roundness);
|
33
|
+
background-color: var(--warped-base);
|
34
|
+
padding-left: 0.5rem;
|
35
|
+
padding-right: 0.5rem;
|
36
|
+
padding-top: 0.25rem;
|
37
|
+
padding-bottom: 0.25rem;
|
38
|
+
position: relative;
|
39
|
+
border: 1px solid var(--warped-neutral);
|
40
|
+
align-items: center;
|
41
|
+
}
|
42
|
+
|
43
|
+
.warped-filters--filter-inactive {
|
44
|
+
border: 1px dashed var(--warped-neutral);
|
45
|
+
}
|
46
|
+
|
47
|
+
.warped-filters--filter--label {
|
48
|
+
font-size: 0.875rem;
|
49
|
+
color: var(--warped-neutral);
|
50
|
+
font-weight: 500;
|
51
|
+
}
|
52
|
+
|
53
|
+
.warped-filters--filter--icon {
|
54
|
+
height: 1.5rem;
|
55
|
+
width: 1.5rem;
|
56
|
+
color: var(--warped-neutral);
|
57
|
+
cursor: pointer;
|
58
|
+
}
|
59
|
+
|
60
|
+
.warped-filters--filter--value {
|
61
|
+
display: flex;
|
62
|
+
transition: all 200ms;
|
63
|
+
color: var(--warped-neutral);
|
64
|
+
}
|
65
|
+
|
66
|
+
.warped-filters--filter--panel-collapsed {
|
67
|
+
visibility: hidden;
|
68
|
+
opacity: 0;
|
69
|
+
}
|
70
|
+
|
71
|
+
.warped-filters--filter--value--values {
|
72
|
+
font-size: 0.875rem;
|
73
|
+
color: var(--warped-neutral);
|
74
|
+
font-weight: 600;
|
75
|
+
}
|
76
|
+
|
77
|
+
.warped-filters--filter--panel {
|
78
|
+
transition: all 200ms;
|
79
|
+
width: max-content;
|
80
|
+
position: absolute;
|
81
|
+
top: 2.5rem;
|
82
|
+
left: 0;
|
83
|
+
background-color: var(--warped-base);
|
84
|
+
border-radius: var(--warped-roundness);
|
85
|
+
padding: 0.5rem;
|
86
|
+
display: flex;
|
87
|
+
z-index: 40;
|
88
|
+
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
|
89
|
+
}
|
90
|
+
|
91
|
+
.warped-filters--filter--panel--remove {
|
92
|
+
cursor: pointer;
|
93
|
+
color: var(--warped-danger);
|
94
|
+
width: 2rem;
|
95
|
+
stroke-width: 2;
|
96
|
+
}
|
97
|
+
|
98
|
+
.warped-filters--filter--panel--select {
|
99
|
+
width: max-content;
|
100
|
+
padding-left: 0.5rem;
|
101
|
+
padding-top: 0.25rem;
|
102
|
+
padding-bottom: 0.25rem;
|
103
|
+
border-top-left-radius: var(--warped-roundness);
|
104
|
+
border-bottom-left-radius: var(--warped-roundness);
|
105
|
+
border-right: 1px solid var(--warped-neutral);
|
106
|
+
}
|
107
|
+
|
108
|
+
.warped-filters--filter--panel--input {
|
109
|
+
width: max-content;
|
110
|
+
padding-top: 0.25rem;
|
111
|
+
padding-bottom: 0.25rem;
|
112
|
+
border-top-right-radius: var(--warped-roundness);
|
113
|
+
border-bottom-right-radius: var(--warped-roundness);
|
114
|
+
border-left: 0;
|
115
|
+
}
|
@@ -0,0 +1,74 @@
|
|
1
|
+
.warped--pagination {
|
2
|
+
display: flex;
|
3
|
+
flex-wrap: wrap;
|
4
|
+
justify-content: center;
|
5
|
+
width: 100%;
|
6
|
+
height: 2.5rem;
|
7
|
+
}
|
8
|
+
|
9
|
+
.warped--pagination--btn {
|
10
|
+
background-color: var(--warped-neutral);
|
11
|
+
color: var(--warped-neutral-content);
|
12
|
+
padding: 0.25rem 0.5rem;
|
13
|
+
display: flex;
|
14
|
+
justify-content: center;
|
15
|
+
align-items: center;
|
16
|
+
min-width: 2.5rem;
|
17
|
+
}
|
18
|
+
|
19
|
+
.warped--pagination--btn button {
|
20
|
+
height: 100%;
|
21
|
+
width: 100%;
|
22
|
+
}
|
23
|
+
|
24
|
+
.warped--pagination--btn:not(:last-child) {
|
25
|
+
border-right: 1px solid var(--warped-neutral-content);
|
26
|
+
}
|
27
|
+
|
28
|
+
.warped--pagination--btn:first-child {
|
29
|
+
border-top-left-radius: var(--warped-roundness);
|
30
|
+
border-bottom-left-radius: var(--warped-roundness);
|
31
|
+
width: 7rem;
|
32
|
+
}
|
33
|
+
|
34
|
+
.warped--pagination--btn:last-child {
|
35
|
+
border-top-right-radius: var(--warped-roundness);
|
36
|
+
border-bottom-right-radius: var(--warped-roundness);
|
37
|
+
width: 7rem;
|
38
|
+
}
|
39
|
+
|
40
|
+
.warped--pagination--btn:hover {
|
41
|
+
background-color: var(--warped-primary);
|
42
|
+
color: var(--warped-primary-content);
|
43
|
+
}
|
44
|
+
|
45
|
+
.warped--pagination--btn-disabled {
|
46
|
+
background-color: var(--warped-neutral);
|
47
|
+
color: var(--warped-neutral-content);
|
48
|
+
padding: 0.25rem 0.5rem;
|
49
|
+
cursor: not-allowed;
|
50
|
+
}
|
51
|
+
|
52
|
+
.warped--pagination--btn-disabled:hover {
|
53
|
+
background-color: var(--warped-neutral);
|
54
|
+
}
|
55
|
+
|
56
|
+
.warped--pagination--btn-active {
|
57
|
+
background-color: var(--warped-primary);
|
58
|
+
color: var(--warped-primary-content);
|
59
|
+
padding: 0.25rem 0.5rem;
|
60
|
+
cursor: pointer;
|
61
|
+
}
|
62
|
+
|
63
|
+
.warped--pagination--btn-inactive {
|
64
|
+
background-color: var(--warped-neutral);
|
65
|
+
color: var(--warped-neutral-content);
|
66
|
+
padding: 0.25rem 0.5rem;
|
67
|
+
cursor: pointer;
|
68
|
+
}
|
69
|
+
|
70
|
+
.warped--pagination--btn-gap:hover {
|
71
|
+
background-color: var(--warped-neutral);
|
72
|
+
color: var(--warped-neutral-content);
|
73
|
+
cursor: not-allowed;
|
74
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
.warped-searchbar {
|
2
|
+
display: flex;
|
3
|
+
width: 100%;
|
4
|
+
background-color: white;
|
5
|
+
border-radius: var(--warped-roundness);
|
6
|
+
}
|
7
|
+
|
8
|
+
.warped-searchbar--button {
|
9
|
+
background-color: var(--warped-neutral);
|
10
|
+
color: var(--warped-neutral-content);
|
11
|
+
stroke: var(--warped-neutral-content);
|
12
|
+
padding-left: 1rem;
|
13
|
+
padding-right: 1rem;
|
14
|
+
padding-top: 0.5rem;
|
15
|
+
padding-bottom: 0.5rem;
|
16
|
+
border-top-left-radius: var(--warped-roundness);
|
17
|
+
border-bottom-left-radius: var(--warped-roundness);
|
18
|
+
}
|
19
|
+
|
20
|
+
.warped-searchbar--button svg {
|
21
|
+
height: 1.25rem;
|
22
|
+
width: 1.25rem;
|
23
|
+
}
|
24
|
+
|
25
|
+
.warped-searchbar--input {
|
26
|
+
flex-grow: 1;
|
27
|
+
border-top: 1px solid var(--warped-neutral);
|
28
|
+
border-right: 1px solid var(--warped-neutral);
|
29
|
+
border-bottom: 1px solid var(--warped-neutral);
|
30
|
+
border-top-right-radius: var(--warped-roundness);
|
31
|
+
border-bottom-right-radius: var(--warped-roundness);
|
32
|
+
padding-left: 0.5rem;
|
33
|
+
}
|
@@ -0,0 +1,114 @@
|
|
1
|
+
/* This file contains all the styles necessary for Warped::Table to work */
|
2
|
+
.warped-table {
|
3
|
+
display: flex;
|
4
|
+
flex-direction: column;
|
5
|
+
width: 100%;
|
6
|
+
row-gap: 0.5rem;
|
7
|
+
background-color: var(--warped-base);
|
8
|
+
border-radius: var(--warped-roundness);
|
9
|
+
padding: 0.5rem;
|
10
|
+
}
|
11
|
+
|
12
|
+
.warped-table--container {
|
13
|
+
overflow-x: auto;
|
14
|
+
}
|
15
|
+
|
16
|
+
.warped-table--controls {
|
17
|
+
display: flex;
|
18
|
+
flex-direction: column;
|
19
|
+
row-gap: 0.5rem;
|
20
|
+
}
|
21
|
+
|
22
|
+
.warped-table--table {
|
23
|
+
width: 100%;
|
24
|
+
border-collapse: collapse;
|
25
|
+
border-spacing: 0;
|
26
|
+
display: table;
|
27
|
+
}
|
28
|
+
|
29
|
+
.warped-table--table--header {
|
30
|
+
display: table-header-group;
|
31
|
+
background-color: var(--warped-neutral);
|
32
|
+
}
|
33
|
+
|
34
|
+
.warped-table--table--header .warped-table--table--row .warped-table--table--cell {
|
35
|
+
font-weight: 500;
|
36
|
+
color: var(--warped-neutral-content);
|
37
|
+
border-right: 1px solid var(--warped-neutral-content);
|
38
|
+
padding: 0 0.25rem;
|
39
|
+
stroke: var(--warped-neutral-content);
|
40
|
+
stroke-width: 0.5;
|
41
|
+
}
|
42
|
+
|
43
|
+
.warped-table--table--header .warped-table--table--row .warped-table--table--cell form {
|
44
|
+
width: 100%;
|
45
|
+
}
|
46
|
+
|
47
|
+
.warped-table--table--header .warped-table--table--row .warped-table--table--cell form button {
|
48
|
+
display: flex;
|
49
|
+
gap: 0.25rem;
|
50
|
+
width: 100%;
|
51
|
+
}
|
52
|
+
|
53
|
+
.warped-table--table--header .warped-table--table--row .warped-table--table--cell form button svg {
|
54
|
+
width: 1.25rem;
|
55
|
+
height: 1.25rem;
|
56
|
+
margin-top: auto;
|
57
|
+
margin-bottom: auto;
|
58
|
+
}
|
59
|
+
|
60
|
+
.warped-table--table--header .warped-table--table--row .warped-table--table--cell:last-child {
|
61
|
+
border-right: none;
|
62
|
+
}
|
63
|
+
|
64
|
+
.warped-table--table--row {
|
65
|
+
display: table-row;
|
66
|
+
}
|
67
|
+
|
68
|
+
.warped-table--table--row:not(:last-child) {
|
69
|
+
border-bottom: 1px solid var(--warped-neutral);
|
70
|
+
}
|
71
|
+
|
72
|
+
.warped-table--table--cell {
|
73
|
+
display: table-cell;
|
74
|
+
padding: 0.25rem;
|
75
|
+
vertical-align: middle;
|
76
|
+
}
|
77
|
+
|
78
|
+
.warped-table--table--row-group {
|
79
|
+
display: table-row-group;
|
80
|
+
}
|
81
|
+
|
82
|
+
.warped-table--table--row-group>*+* {
|
83
|
+
border-top: 1px solid var(--warped-neutral);
|
84
|
+
}
|
85
|
+
|
86
|
+
.warped-table--table--row-group .warped-table--table--empty-row:only-child {
|
87
|
+
display: table-row;
|
88
|
+
}
|
89
|
+
|
90
|
+
.warped-table--table--row-group .warped-table--table--row:nth-child(odd) {
|
91
|
+
background-color: rgba(var(--warped-neutral-rgb), 0.1);
|
92
|
+
}
|
93
|
+
|
94
|
+
.warped-table--table--row-group .warped-table--table--empty-row {
|
95
|
+
display: none;
|
96
|
+
position: relative;
|
97
|
+
height: 3rem;
|
98
|
+
}
|
99
|
+
|
100
|
+
.warped-table--table--row-group .warped-table--table--empty-row .warped-table--table--cell {
|
101
|
+
position: absolute;
|
102
|
+
width: 100%;
|
103
|
+
height: 100%;
|
104
|
+
text-align: center;
|
105
|
+
font-weight: 500;
|
106
|
+
padding-top: 0.5rem;
|
107
|
+
}
|
108
|
+
|
109
|
+
.warped-table--action {
|
110
|
+
padding: 0.25rem 0.5rem;
|
111
|
+
border-radius: var(--warped-roundness);
|
112
|
+
background-color: var(--warped-neutral);
|
113
|
+
color: var(--warped-neutral-content);
|
114
|
+
}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%# locals: (resource:, actions:[])%>
|
2
|
+
|
3
|
+
<div class="warped-table--table--cell">
|
4
|
+
<% actions.each do |action| %>
|
5
|
+
<% opts = action.options.deep_dup %>
|
6
|
+
<% action_class = "warped-table--action #{opts.delete(:class)}" %>
|
7
|
+
<%= link_to (action.name(resource)), action.path(resource), class: action_class, **opts %>
|
8
|
+
<% end %>
|
9
|
+
</div>
|