warped 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +25 -0
  3. data/Gemfile +0 -2
  4. data/Gemfile.lock +25 -19
  5. data/README.md +116 -270
  6. data/app/assets/config/warped_manifest.js +2 -0
  7. data/app/assets/javascript/warped/controllers/filter_controller.js +76 -0
  8. data/app/assets/javascript/warped/controllers/filters_controller.js +21 -0
  9. data/app/assets/javascript/warped/index.js +2 -0
  10. data/app/assets/stylesheets/warped/application.css +15 -0
  11. data/app/assets/stylesheets/warped/base.css +23 -0
  12. data/app/assets/stylesheets/warped/filters.css +115 -0
  13. data/app/assets/stylesheets/warped/pagination.css +74 -0
  14. data/app/assets/stylesheets/warped/search.css +33 -0
  15. data/app/assets/stylesheets/warped/table.css +114 -0
  16. data/app/views/warped/_actions.html.erb +9 -0
  17. data/app/views/warped/_cell.html.erb +3 -0
  18. data/app/views/warped/_column.html.erb +35 -0
  19. data/app/views/warped/_filters.html.erb +21 -0
  20. data/app/views/warped/_hidden_fields.html.erb +19 -0
  21. data/app/views/warped/_pagination.html.erb +34 -0
  22. data/app/views/warped/_row.html.erb +19 -0
  23. data/app/views/warped/_search.html.erb +21 -0
  24. data/app/views/warped/_table.html.erb +52 -0
  25. data/app/views/warped/filters/_filter.html.erb +40 -0
  26. data/config/importmap.rb +3 -0
  27. data/docs/controllers/FILTERABLE.md +193 -0
  28. data/docs/controllers/PAGEABLE.md +70 -0
  29. data/docs/controllers/README.md +8 -0
  30. data/docs/controllers/SEARCHABLE.md +95 -0
  31. data/docs/controllers/SORTABLE.md +94 -0
  32. data/docs/controllers/TABULATABLE.md +28 -0
  33. data/docs/controllers/views/PARTIALS.md +285 -0
  34. data/docs/jobs/README.md +22 -0
  35. data/docs/services/README.md +81 -0
  36. data/lib/generators/warped/install_generator.rb +1 -1
  37. data/lib/warped/api/filter/base/value.rb +52 -0
  38. data/lib/warped/api/filter/base.rb +84 -0
  39. data/lib/warped/api/filter/boolean.rb +41 -0
  40. data/lib/warped/api/filter/date.rb +26 -0
  41. data/lib/warped/api/filter/date_time.rb +32 -0
  42. data/lib/warped/api/filter/decimal.rb +31 -0
  43. data/lib/warped/api/filter/factory.rb +38 -0
  44. data/lib/warped/api/filter/integer.rb +38 -0
  45. data/lib/warped/api/filter/string.rb +25 -0
  46. data/lib/warped/api/filter/time.rb +25 -0
  47. data/lib/warped/api/filter.rb +14 -0
  48. data/lib/warped/api/sort/value.rb +40 -0
  49. data/lib/warped/api/sort.rb +65 -0
  50. data/lib/warped/controllers/filterable/ui.rb +46 -0
  51. data/lib/warped/controllers/filterable.rb +79 -42
  52. data/lib/warped/controllers/pageable/ui.rb +70 -0
  53. data/lib/warped/controllers/pageable.rb +11 -11
  54. data/lib/warped/controllers/searchable/ui.rb +37 -0
  55. data/lib/warped/controllers/searchable.rb +2 -0
  56. data/lib/warped/controllers/sortable/ui.rb +53 -0
  57. data/lib/warped/controllers/sortable.rb +53 -33
  58. data/lib/warped/controllers/tabulatable/ui.rb +54 -0
  59. data/lib/warped/controllers/tabulatable.rb +13 -27
  60. data/lib/warped/emails/components/align.rb +21 -0
  61. data/lib/warped/emails/components/base.rb +116 -0
  62. data/lib/warped/emails/components/button.rb +58 -0
  63. data/lib/warped/emails/components/divider.rb +15 -0
  64. data/lib/warped/emails/components/heading.rb +65 -0
  65. data/lib/warped/emails/components/layouts/columns.rb +36 -0
  66. data/lib/warped/emails/components/layouts/cta.rb +38 -0
  67. data/lib/warped/emails/components/layouts/main.rb +34 -0
  68. data/lib/warped/emails/components/link.rb +36 -0
  69. data/lib/warped/emails/components/spacer.rb +15 -0
  70. data/lib/warped/emails/components/stepper.rb +104 -0
  71. data/lib/warped/emails/components/table.rb +37 -0
  72. data/lib/warped/emails/components/text.rb +67 -0
  73. data/lib/warped/emails/helpers.rb +26 -0
  74. data/lib/warped/emails/slottable.rb +61 -0
  75. data/lib/warped/emails/styleable.rb +160 -0
  76. data/lib/warped/engine.rb +19 -0
  77. data/lib/warped/queries/filter.rb +32 -12
  78. data/lib/warped/table/action.rb +33 -0
  79. data/lib/warped/table/column.rb +34 -0
  80. data/lib/warped/version.rb +1 -1
  81. data/lib/warped.rb +2 -0
  82. data/warped.gemspec +1 -1
  83. metadata +73 -7
  84. data/lib/warped/emails/.keep +0 -0
@@ -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>
@@ -0,0 +1,35 @@
1
+ <%# locals: (path:, column:, turbo_action:) %>
2
+
3
+ <div class="warped-table--table--cell">
4
+ <% if controller.class.include?(Warped::Controllers::Sortable::Ui) && sorted? && sortable_field?(column.parameter_name) %>
5
+ <% html_form_options = {}.tap do |hash| %>
6
+ <% hash.merge!(filter_url_params) if try(:filtered?) %>
7
+ <% hash.merge!(paginate_url_params) if try(:sorted?) %>
8
+ <% hash.merge!(search_url_params) if try(:searched?) %>
9
+ <% end %>
10
+
11
+ <% column_sorted = sorted_field?(column.parameter_name) %>
12
+ <% sort_direction = column_sorted ? current_action_sort_value.opposite_direction : default_sort_direction %>
13
+
14
+ <%= button_to(path, { "data-turbo-action" => turbo_action, method: :get, params: html_form_options.merge(sort_key: column.parameter_name, sort_direction: sort_direction) }) do %>
15
+ <% if column_sorted %>
16
+ <% if current_action_sort_value.asc? %>
17
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
18
+ <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5" />
19
+ </svg>
20
+ <% else %>
21
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
22
+ <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
23
+ </svg>
24
+ <% end %>
25
+ <% else %>
26
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
27
+ <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 15 12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
28
+ </svg>
29
+ <% end %>
30
+ <span> <%= column.display_name %> </span>
31
+ <% end %>
32
+ <% else %>
33
+ <%= column.display_name %>
34
+ <% end %>
35
+ </div>
@@ -0,0 +1,21 @@
1
+ <%# locals: (path:, turbo_action:, **options) %>
2
+
3
+ <% opts = options.deep_dup %>
4
+
5
+ <% data = opts.delete(:data) { {} } %>
6
+ <% data.merge!(controller: "filters #{data[:controller]}", filters_filter_outlet: ".warped-filters--filter", turbo_action:) %>
7
+
8
+ <% html = opts.extract!(:html) %>
9
+ <% html_class = opts.delete(:class) %>
10
+ <% html.merge!(class: "warped-filters #{html_class}") %>
11
+
12
+ <%= form_with url: path, method: :get, html:, data:, **opts do |f| %>
13
+ <%= f.submit "Filter", class: "warped-filters--submit" %>
14
+ <%= f.button "Clear", type: :reset, class: "warped-filters--clear", data: { action: "filters#clearAll" } %>
15
+
16
+ <% current_action_filters.each do |filter| %>
17
+ <%= render "warped/filters/filter", form: f, filter: filter %>
18
+ <% end %>
19
+
20
+ <%= render "warped/hidden_fields", form: f, modules: %i[sortable searchable] %>
21
+ <% end %>
@@ -0,0 +1,19 @@
1
+ <%# locals: (form:, modules:) %>
2
+
3
+ <% Array.wrap(modules).each do |module_name| %>
4
+ <% if module_name.to_sym == :searchable && try(:searched?) %>
5
+ <%= form.hidden_field search_param, value: search_term %>
6
+ <% elsif module_name.to_sym == :filterable && try(:filtered?) %>
7
+ <% filter_url_params.each do |key, value| %>
8
+ <%= form.hidden_field key, value: %>
9
+ <% end %>
10
+ <% elsif module_name.to_sym == :sortable && try(:sorted?) %>
11
+ <% sort_url_params.each do |key, value| %>
12
+ <%= form.hidden_field key, value: %>
13
+ <% end %>
14
+ <% elsif module_name.to_sym == :pageable && try(:paginated?) %>
15
+ <% paginate_url_params.each do |key, value| %>
16
+ <%= form.hidden_field key, value: %>
17
+ <% end %>
18
+ <% end %>
19
+ <% end %>
@@ -0,0 +1,34 @@
1
+ <%# locals: (path:, turbo_action:, **options) %>
2
+
3
+ <% opts = options.deep_dup %>
4
+ <% pagination_class = "warped--pagination #{opts.delete(:class)}" %>
5
+
6
+ <% uri = URI(path) %>
7
+
8
+ <% html_form_options = {}.tap do |hash| %>
9
+ <% hash.merge!(sort_url_params) if try(:sorted?) %>
10
+ <% hash.merge!(filter_url_params) if try(:filtered?) %>
11
+ <% hash.merge!(search_url_params) if try(:searched?) %>
12
+ <% end %>
13
+
14
+ <%= tag.nav(class: pagination_class, role: "navigation", **opts) do %>
15
+ <% if pagination[:prev_page] -%> <%= button_to("Previous", path, { "data-turbo-action" => turbo_action, method: :get, form_class: "warped--pagination--btn", params: html_form_options.merge(page: pagination[:prev_page]) }) %>
16
+ <% else -%> <span class="warped--pagination--btn warped--pagination--btn-disabled" disabled>Previous</span>
17
+ <% end -%>
18
+
19
+ <% pagination[:series].each do |item| -%>
20
+ <% if item.is_a?(Integer) -%> <%= button_to(item, path, { "data-turbo-action" => turbo_action, method: :get, form_class: "warped--pagination--btn warped--pagination--btn-inactive", params: html_form_options.merge(page: item) }) %>
21
+ <% elsif item.is_a?(String) -%> <span class="warped--pagination--btn warped--pagination--btn-active"><%= item %></span>
22
+ <% elsif item == :gap %>
23
+ <span class="warped--pagination--btn warped--pagination--btn-gap">
24
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
25
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM12.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM18.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z" />
26
+ </svg>
27
+ </span>
28
+ <% end -%>
29
+ <% end -%>
30
+
31
+ <% if pagination[:next_page] -%> <%= button_to("Next", path, { "data-turbo-action" => turbo_action, method: :get, form_class: "warped--pagination--btn", params: html_form_options.merge(page: pagination[:next_page]) }) %>
32
+ <% else -%> <span class=" warped--pagination--btn warped--pagination--btn-disabled" disabled>Next</span>
33
+ <% end -%>
34
+ <% end %>
@@ -0,0 +1,19 @@
1
+ <%# locals: (resource:, columns:, path: nil, actions: nil) -%>
2
+
3
+ <% element_tag = path.present? ? :a : :div %>
4
+
5
+ <%= content_tag element_tag, **{ class: "warped-table--table--row" }.merge!(href: path).compact_blank do %>
6
+ <% if (block = yield).present? %>
7
+ <%= block %>
8
+ <% else %>
9
+ <% columns.each do |column| %>
10
+ <%= render "warped/cell" do %>
11
+ <%= column.content_for(resource) %>
12
+ <% end %>
13
+ <% end %>
14
+
15
+ <% if actions.present? %>
16
+ <%= render "warped/actions", actions:, resource: %>
17
+ <% end %>
18
+ <% end %>
19
+ <% end %>
@@ -0,0 +1,21 @@
1
+ <%# locals: (path:, turbo_action:, **options) %>
2
+
3
+ <% opts = options.deep_dup %>
4
+
5
+ <% html = opts.extract!(:html) %>
6
+ <% html.merge!(class: "warped-searchbar #{opts.delete(:class)}") %>
7
+
8
+ <% data = opts.delete(:data) { {} } %>
9
+ <% data.merge!(turbo_action:) %>
10
+
11
+ <%= form_with url: path, method: :get, data:, html:, **opts do |f| %>
12
+ <%= f.button type: :submit, class: "warped-searchbar--button", name: nil do %>
13
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
14
+ <path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
15
+ </svg>
16
+ <% end %>
17
+
18
+ <%= f.text_field search_param, autocomplete: "off", value: search_term, class: "warped-searchbar--input" %>
19
+
20
+ <%= render "warped/hidden_fields", form: f, modules: %i[pageable sortable filterable] %>
21
+ <% end %>