warped 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -0
  3. data/Gemfile.lock +2 -2
  4. data/README.md +104 -0
  5. data/app/assets/config/warped_manifest.js +2 -0
  6. data/app/assets/javascript/warped/controllers/filter_controller.js +76 -0
  7. data/app/assets/javascript/warped/controllers/filters_controller.js +21 -0
  8. data/app/assets/javascript/warped/index.js +2 -0
  9. data/app/assets/stylesheets/warped/application.css +15 -0
  10. data/app/assets/stylesheets/warped/base.css +23 -0
  11. data/app/assets/stylesheets/warped/filters.css +115 -0
  12. data/app/assets/stylesheets/warped/pagination.css +74 -0
  13. data/app/assets/stylesheets/warped/search.css +33 -0
  14. data/app/assets/stylesheets/warped/table.css +114 -0
  15. data/app/views/warped/_actions.html.erb +9 -0
  16. data/app/views/warped/_cell.html.erb +3 -0
  17. data/app/views/warped/_column.html.erb +35 -0
  18. data/app/views/warped/_filters.html.erb +21 -0
  19. data/app/views/warped/_hidden_fields.html.erb +19 -0
  20. data/app/views/warped/_pagination.html.erb +34 -0
  21. data/app/views/warped/_row.html.erb +19 -0
  22. data/app/views/warped/_search.html.erb +21 -0
  23. data/app/views/warped/_table.html.erb +52 -0
  24. data/app/views/warped/filters/_filter.html.erb +40 -0
  25. data/config/importmap.rb +3 -0
  26. data/docs/controllers/FILTERABLE.md +82 -3
  27. data/docs/controllers/views/PARTIALS.md +285 -0
  28. data/lib/warped/api/filter/base/value.rb +52 -0
  29. data/lib/warped/api/filter/base.rb +84 -0
  30. data/lib/warped/api/filter/boolean.rb +41 -0
  31. data/lib/warped/api/filter/date.rb +26 -0
  32. data/lib/warped/api/filter/date_time.rb +32 -0
  33. data/lib/warped/api/filter/decimal.rb +31 -0
  34. data/lib/warped/api/filter/factory.rb +38 -0
  35. data/lib/warped/api/filter/integer.rb +38 -0
  36. data/lib/warped/api/filter/string.rb +25 -0
  37. data/lib/warped/api/filter/time.rb +25 -0
  38. data/lib/warped/api/filter.rb +14 -0
  39. data/lib/warped/api/sort/value.rb +40 -0
  40. data/lib/warped/api/sort.rb +65 -0
  41. data/lib/warped/controllers/filterable/ui.rb +10 -32
  42. data/lib/warped/controllers/filterable.rb +75 -42
  43. data/lib/warped/controllers/pageable/ui.rb +13 -3
  44. data/lib/warped/controllers/pageable.rb +1 -1
  45. data/lib/warped/controllers/searchable/ui.rb +3 -1
  46. data/lib/warped/controllers/sortable/ui.rb +21 -26
  47. data/lib/warped/controllers/sortable.rb +53 -33
  48. data/lib/warped/controllers/tabulatable/ui.rb +4 -0
  49. data/lib/warped/controllers/tabulatable.rb +6 -9
  50. data/lib/warped/engine.rb +19 -0
  51. data/lib/warped/queries/filter.rb +3 -3
  52. data/lib/warped/table/action.rb +33 -0
  53. data/lib/warped/table/column.rb +34 -0
  54. data/lib/warped/version.rb +1 -1
  55. data/lib/warped.rb +1 -0
  56. data/warped.gemspec +1 -1
  57. metadata +44 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 01d84d855e451182cddf0b6d9e38d922e0dfd40c5ee05752496759d5344b042b
4
- data.tar.gz: 9d0ddb61f7af15597abb5e0a12f11cbb602f86b2f52763288f4be797f2d41509
3
+ metadata.gz: c1c6444d0d93fbbfa88436418e69edc9e44e228402d56becbc99bd7eae122f14
4
+ data.tar.gz: 4eeeb9b833549db2c33a67fa73627e3e55f27dda5178d9f2f377b989f64739a0
5
5
  SHA512:
6
- metadata.gz: 4b9d8acfdfd217f5755800657688ce6e2c5f7ddab564a8b658c6a94ee38d8b19ae1dc5f416ea3996d103015cf09793734beeaa63b1388356347b5354eb65da10
7
- data.tar.gz: '0407936f782cdc2dbb0f210b43a7204a6758866e13bd1eb6772b3410e7063f49909dc3ed60d3cfd081ece0dfdccc10a6ced6e4f295d407f4795ea9f4bbe00b85'
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
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- warped (0.2.0)
5
- rails (>= 6.0, < 8.0)
4
+ warped (1.0.0)
5
+ rails (>= 7.1, <= 8.0)
6
6
  zeitwerk (>= 2.4)
7
7
 
8
8
  GEM
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,2 @@
1
+ //= link_directory ../stylesheets/warped .css
2
+ //= link_directory ../stylesheets/warped .js
@@ -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,2 @@
1
+ import FilterController from "warped/controllers/filter_controller";
2
+ import FiltersController from "warped/controllers/filters_controller";
@@ -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>
@@ -0,0 +1,3 @@
1
+ <div class="warped-table--table--cell">
2
+ <%= yield %>
3
+ </div>