okonomi_ui_kit 0.1.3 → 0.1.5
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/app/assets/builds/okonomi_ui_kit/application.tailwind.css +21 -39
- data/app/helpers/okonomi_ui_kit/form_builder.rb +176 -158
- data/app/helpers/okonomi_ui_kit/theme.rb +35 -0
- data/app/javascript/okonomi_ui_kit/controllers/file_input_controller.js +13 -0
- data/app/javascript/okonomi_ui_kit/controllers/flash_controller.js +31 -0
- data/app/javascript/okonomi_ui_kit/controllers/form_field_visibility_controller.js +27 -0
- data/app/javascript/okonomi_ui_kit/controllers/upload_controller.js +55 -0
- data/app/views/okonomi/forms/tailwind/_checkbox_label.html.erb +2 -2
- data/app/views/okonomi/forms/tailwind/_field.html.erb +1 -1
- data/config/importmap.rb +5 -0
- data/lib/okonomi_ui_kit/engine.rb +2 -1
- data/lib/okonomi_ui_kit/version.rb +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6dcaf7cc3b2377bc772e7a10b1aa077bf440743ef6c13e999eb9b5372c42ca7
|
4
|
+
data.tar.gz: f5c7b796716c8c823029a03fc76ad293356ee58d2e9af5b18b8d299100402b6d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34f34becaab28a8ad277861856eceb3ee874544c869ae52ca5c4a2aa35ad0f34d21a5484f5d4a1eb9fd5b45d6056ee996fa69fc90b3f038bc0a06999adf70d0b
|
7
|
+
data.tar.gz: 9bbad8f7dbb9748765976e84a494250b3695c167b5578b7e9077bc963df622c6990b512372a2b7bc1fb7056d7bfc1f1afdcccf3a759262ce45aa459bd7b1a249
|
@@ -128,6 +128,7 @@
|
|
128
128
|
--default-font-family: var(--font-sans);
|
129
129
|
--default-mono-font-family: var(--font-mono);
|
130
130
|
--color-default-50: var(--color-gray-50);
|
131
|
+
--color-default-300: var(--color-gray-300);
|
131
132
|
--color-default-500: var(--color-gray-500);
|
132
133
|
--color-default-600: var(--color-gray-600);
|
133
134
|
--color-default-700: var(--color-gray-700);
|
@@ -144,7 +145,6 @@
|
|
144
145
|
--color-success-700: var(--color-green-700);
|
145
146
|
--color-danger-50: var(--color-red-50);
|
146
147
|
--color-danger-100: var(--color-red-100);
|
147
|
-
--color-danger-300: var(--color-red-300);
|
148
148
|
--color-danger-400: var(--color-red-400);
|
149
149
|
--color-danger-600: var(--color-red-600);
|
150
150
|
--color-danger-700: var(--color-red-700);
|
@@ -615,6 +615,9 @@
|
|
615
615
|
.cursor-pointer {
|
616
616
|
cursor: pointer;
|
617
617
|
}
|
618
|
+
.appearance-none {
|
619
|
+
appearance: none;
|
620
|
+
}
|
618
621
|
.grid-cols-1 {
|
619
622
|
grid-template-columns: repeat(1, minmax(0, 1fr));
|
620
623
|
}
|
@@ -753,6 +756,12 @@
|
|
753
756
|
border-color: var(--color-gray-300);
|
754
757
|
}
|
755
758
|
}
|
759
|
+
.self-center {
|
760
|
+
align-self: center;
|
761
|
+
}
|
762
|
+
.justify-self-end {
|
763
|
+
justify-self: flex-end;
|
764
|
+
}
|
756
765
|
.truncate {
|
757
766
|
overflow: hidden;
|
758
767
|
text-overflow: ellipsis;
|
@@ -1138,6 +1147,9 @@
|
|
1138
1147
|
.text-blue-900 {
|
1139
1148
|
color: var(--color-blue-900);
|
1140
1149
|
}
|
1150
|
+
.text-danger-400 {
|
1151
|
+
color: var(--color-danger-400);
|
1152
|
+
}
|
1141
1153
|
.text-danger-600 {
|
1142
1154
|
color: var(--color-danger-600);
|
1143
1155
|
}
|
@@ -1262,9 +1274,6 @@
|
|
1262
1274
|
--tw-ring-color: color-mix(in oklab, var(--color-black) 5%, transparent);
|
1263
1275
|
}
|
1264
1276
|
}
|
1265
|
-
.ring-danger-300 {
|
1266
|
-
--tw-ring-color: var(--color-danger-300);
|
1267
|
-
}
|
1268
1277
|
.ring-danger-400 {
|
1269
1278
|
--tw-ring-color: var(--color-danger-400);
|
1270
1279
|
}
|
@@ -1287,11 +1296,8 @@
|
|
1287
1296
|
outline-style: var(--tw-outline-style);
|
1288
1297
|
outline-width: 1px;
|
1289
1298
|
}
|
1290
|
-
|
1291
|
-
outline-
|
1292
|
-
}
|
1293
|
-
.outline-gray-300 {
|
1294
|
-
outline-color: var(--color-gray-300);
|
1299
|
+
.outline-default-300 {
|
1300
|
+
outline-color: var(--color-default-300);
|
1295
1301
|
}
|
1296
1302
|
.filter {
|
1297
1303
|
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
@@ -1331,9 +1337,6 @@
|
|
1331
1337
|
--tw-ease: var(--ease-out);
|
1332
1338
|
transition-timing-function: var(--ease-out);
|
1333
1339
|
}
|
1334
|
-
.ring-inset {
|
1335
|
-
--tw-ring-inset: inset;
|
1336
|
-
}
|
1337
1340
|
.group-hover\:border-primary-600 {
|
1338
1341
|
&:is(:where(.group):hover *) {
|
1339
1342
|
@media (hover: hover) {
|
@@ -1368,17 +1371,6 @@
|
|
1368
1371
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
1369
1372
|
}
|
1370
1373
|
}
|
1371
|
-
.focus-within\:ring-2 {
|
1372
|
-
&:focus-within {
|
1373
|
-
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
1374
|
-
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
1375
|
-
}
|
1376
|
-
}
|
1377
|
-
.focus-within\:ring-danger-400 {
|
1378
|
-
&:focus-within {
|
1379
|
-
--tw-ring-color: var(--color-danger-400);
|
1380
|
-
}
|
1381
|
-
}
|
1382
1374
|
.focus-within\:ring-gray-400 {
|
1383
1375
|
&:focus-within {
|
1384
1376
|
--tw-ring-color: var(--color-gray-400);
|
@@ -1662,22 +1654,6 @@
|
|
1662
1654
|
}
|
1663
1655
|
}
|
1664
1656
|
}
|
1665
|
-
.focus\:outline-2 {
|
1666
|
-
&:focus {
|
1667
|
-
outline-style: var(--tw-outline-style);
|
1668
|
-
outline-width: 2px;
|
1669
|
-
}
|
1670
|
-
}
|
1671
|
-
.focus\:-outline-offset-2 {
|
1672
|
-
&:focus {
|
1673
|
-
outline-offset: calc(2px * -1);
|
1674
|
-
}
|
1675
|
-
}
|
1676
|
-
.focus\:outline-primary-600 {
|
1677
|
-
&:focus {
|
1678
|
-
outline-color: var(--color-primary-600);
|
1679
|
-
}
|
1680
|
-
}
|
1681
1657
|
.focus\:outline-none {
|
1682
1658
|
&:focus {
|
1683
1659
|
--tw-outline-style: none;
|
@@ -1751,6 +1727,12 @@
|
|
1751
1727
|
display: none;
|
1752
1728
|
}
|
1753
1729
|
}
|
1730
|
+
.sm\:size-4 {
|
1731
|
+
@media (width >= 40rem) {
|
1732
|
+
width: calc(var(--spacing) * 4);
|
1733
|
+
height: calc(var(--spacing) * 4);
|
1734
|
+
}
|
1735
|
+
}
|
1754
1736
|
.sm\:grid-cols-3 {
|
1755
1737
|
@media (width >= 40rem) {
|
1756
1738
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
@@ -1,191 +1,209 @@
|
|
1
1
|
module OkonomiUiKit
|
2
2
|
class FormBuilder < ActionView::Helpers::FormBuilder
|
3
|
-
|
3
|
+
delegate :tag, :content_tag, :safe_join, to: :@template
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def field(field_id = nil, options = {}, &block)
|
10
|
-
@template.render('okonomi/forms/tailwind/field', field_id:, options:, form: self, &block)
|
11
|
-
end
|
12
|
-
|
13
|
-
def upload_field(method, options = {})
|
14
|
-
@template.render('okonomi/forms/tailwind/upload_field', method:, options:, form: self)
|
15
|
-
end
|
16
|
-
|
17
|
-
def text_field(method, options = {})
|
18
|
-
css = input_field_classes(method, options, focus_ring: 'focus-within:ring-0.5')
|
19
|
-
super(method, { autocomplete: "off" }.merge(options).merge(class: css))
|
20
|
-
end
|
5
|
+
def ui
|
6
|
+
@template.ui
|
7
|
+
end
|
21
8
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
9
|
+
def field_set(options = {}, &block)
|
10
|
+
@template.render('okonomi/forms/tailwind/field_set', options:, form: self, &block)
|
11
|
+
end
|
26
12
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
13
|
+
def field(field_id = nil, options = {}, &block)
|
14
|
+
@template.render('okonomi/forms/tailwind/field', field_id:, options:, form: self, &block)
|
15
|
+
end
|
31
16
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
17
|
+
def upload_field(method, options = {})
|
18
|
+
@template.render('okonomi/forms/tailwind/upload_field', method:, options:, form: self)
|
19
|
+
end
|
36
20
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
21
|
+
def text_field(method, options = {})
|
22
|
+
css = input_field_classes(method, :text, options)
|
23
|
+
super(method, { autocomplete: "off" }.merge(options).merge(class: css))
|
24
|
+
end
|
41
25
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
26
|
+
def email_field(method, options = {})
|
27
|
+
css = input_field_classes(method, :email, options)
|
28
|
+
super(method, options.merge(class: css))
|
29
|
+
end
|
46
30
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
31
|
+
def url_field(method, options = {})
|
32
|
+
css = input_field_classes(method, :url, options)
|
33
|
+
super(method, options.merge(class: css))
|
34
|
+
end
|
51
35
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
36
|
+
def password_field(method, options = {})
|
37
|
+
css = input_field_classes(method, :password, options)
|
38
|
+
super(method, options.merge(class: css))
|
39
|
+
end
|
56
40
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
41
|
+
def number_field(method, options = {})
|
42
|
+
css = input_field_classes(method, :number, options)
|
43
|
+
super(method, options.merge(class: css))
|
44
|
+
end
|
61
45
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
46
|
+
def telephone_field(method, options = {})
|
47
|
+
css = input_field_classes(method, :telephone_field, options)
|
48
|
+
super(method, options.merge(class: css))
|
49
|
+
end
|
66
50
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
51
|
+
def search_field(method, options = {})
|
52
|
+
css = input_field_classes(method, :search, options)
|
53
|
+
super(method, options.merge(class: css))
|
54
|
+
end
|
71
55
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
'bg-danger-100 text-danger-600 ring-1 ring-inset ring-danger-300 focus-within:ring-2 focus-within:ring-danger-400',
|
77
|
-
select_class_default_state),
|
78
|
-
html_options[:class]
|
79
|
-
].compact.join(' ').split(' ').uniq
|
80
|
-
super(method, choices, options, html_options.merge(class: css), &block)
|
81
|
-
end
|
56
|
+
def date_field(method, options = {})
|
57
|
+
css = input_field_classes(method, :date, options)
|
58
|
+
super(method, options.merge(class: css))
|
59
|
+
end
|
82
60
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
'bg-danger-100 text-danger-600 ring-1 ring-inset ring-danger-300 focus-within:ring-2 focus-within:ring-danger-400',
|
88
|
-
select_class_default_state),
|
89
|
-
html_options[:class]
|
90
|
-
].compact.join(' ').split(' ').uniq
|
91
|
-
super(method, collection, value_method, text_method, options, html_options.merge(class: css))
|
92
|
-
end
|
61
|
+
def datetime_local_field(method, options = {})
|
62
|
+
css = input_field_classes(method, :datetime_local, options)
|
63
|
+
super(method, options.merge(class: css))
|
64
|
+
end
|
93
65
|
|
94
|
-
|
95
|
-
|
96
|
-
|
66
|
+
def time_field(method, options = {})
|
67
|
+
css = input_field_classes(method, :time, options)
|
68
|
+
super(method, options.merge(class: css))
|
69
|
+
end
|
97
70
|
|
98
|
-
|
99
|
-
|
100
|
-
|
71
|
+
def text_area(method, options = {})
|
72
|
+
css = input_field_classes(method, :textarea, options, include_disabled: false)
|
73
|
+
super(method, options.merge(class: css))
|
74
|
+
end
|
101
75
|
|
102
|
-
|
103
|
-
|
104
|
-
|
76
|
+
def select(method, choices = nil, options = {}, html_options = {}, &block)
|
77
|
+
css = [
|
78
|
+
ui.get_theme.dig(:components, :select, :root),
|
79
|
+
when_errors(
|
80
|
+
method,
|
81
|
+
ui.get_theme.dig(:components, :select, :error),
|
82
|
+
ui.get_theme.dig(:components, :select, :valid)
|
83
|
+
),
|
84
|
+
html_options[:class]
|
85
|
+
].compact.join(' ').split(' ').uniq
|
86
|
+
|
87
|
+
select_html = super(method, choices, options, html_options.merge(class: css), &block)
|
88
|
+
icon_html = @template.svg_icon(
|
89
|
+
ui.get_theme.dig(:components, :select, :icon, :file),
|
90
|
+
class: ui.get_theme.dig(:components, :select, :icon, :class)
|
91
|
+
)
|
92
|
+
|
93
|
+
@template.content_tag(:div, class: ui.get_theme.dig(:components, :select, :wrapper)) do
|
94
|
+
@template.concat(select_html)
|
95
|
+
@template.concat(icon_html)
|
96
|
+
end
|
97
|
+
end
|
105
98
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
99
|
+
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
|
100
|
+
css = [
|
101
|
+
ui.get_theme.dig(:components, :select, :root),
|
102
|
+
when_errors(
|
103
|
+
method,
|
104
|
+
ui.get_theme.dig(:components, :select, :error),
|
105
|
+
ui.get_theme.dig(:components, :select, :valid)
|
106
|
+
),
|
107
|
+
html_options[:class]
|
108
|
+
].compact.join(' ').split(' ').uniq
|
109
|
+
|
110
|
+
select_html = super(method, collection, value_method, text_method, options, html_options.merge(class: css))
|
111
|
+
icon_html = @template.svg_icon(
|
112
|
+
ui.get_theme.dig(:components, :select, :icon, :file),
|
113
|
+
class: ui.get_theme.dig(:components, :select, :icon, :class)
|
114
|
+
)
|
115
|
+
|
116
|
+
@template.content_tag(:div, class: ui.get_theme.dig(:components, :select, :wrapper)) do
|
117
|
+
@template.concat(select_html)
|
118
|
+
@template.concat(icon_html)
|
119
|
+
end
|
120
|
+
end
|
110
121
|
|
111
|
-
|
112
|
-
|
113
|
-
|
122
|
+
def multi_select(method, **options)
|
123
|
+
@template.render(
|
124
|
+
partial: 'okonomi/forms/tailwind/multi_select',
|
125
|
+
locals: {
|
126
|
+
form: self,
|
127
|
+
method: method,
|
128
|
+
options: options
|
129
|
+
}
|
130
|
+
)
|
131
|
+
end
|
114
132
|
|
115
|
-
|
116
|
-
|
117
|
-
|
133
|
+
def label(method, text = nil, options = {}, &block)
|
134
|
+
base_classes = ui.get_theme.dig(:components, :label, :root)
|
135
|
+
super(method, text, merge_class(options, base_classes), &block)
|
136
|
+
end
|
118
137
|
|
119
|
-
|
120
|
-
|
121
|
-
|
138
|
+
def submit(value = nil, options = {})
|
139
|
+
variant = options.delete(:variant) || 'contained'
|
140
|
+
color = options.delete(:color) || 'primary'
|
122
141
|
|
123
|
-
|
124
|
-
|
125
|
-
@template.concat check_box(
|
126
|
-
method,
|
127
|
-
{
|
128
|
-
class: 'cursor-pointer h-4 w-4 rounded-sm border-gray-300 text-primary-600 focus:ring-0 focus:ring-primary-600'
|
129
|
-
}.merge(options || {}),
|
130
|
-
checked_value,
|
131
|
-
unchecked_value
|
132
|
-
)
|
133
|
-
@template.concat @template.render('okonomi/forms/tailwind/checkbox_label', method:, options:, form: self)
|
142
|
+
base_classes = ui.button_class(variant:, color:)
|
143
|
+
super(value, merge_class(options, base_classes))
|
134
144
|
end
|
135
|
-
end
|
136
145
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
@template.tag.div(
|
151
|
-
class: "hidden",
|
152
|
-
data: {
|
153
|
-
controller: "form-field-visibility",
|
154
|
-
"form-field-visibility-field-id-value": field_id,
|
155
|
-
"form-field-visibility-equals-value": equals
|
156
|
-
},
|
157
|
-
&block
|
158
|
-
)
|
159
|
-
end
|
146
|
+
def check_box_with_label(method, options = {}, checked_value = true, unchecked_value = false)
|
147
|
+
@template.content_tag(:div, class: ui.get_theme.dig(:components, :checkbox, :wrapper)) do
|
148
|
+
@template.concat check_box(
|
149
|
+
method,
|
150
|
+
{
|
151
|
+
class: ui.get_theme.dig(:components, :checkbox, :input, :root)
|
152
|
+
}.merge(options || {}),
|
153
|
+
checked_value,
|
154
|
+
unchecked_value
|
155
|
+
)
|
156
|
+
@template.concat @template.render('okonomi/forms/tailwind/checkbox_label', method:, options:, form: self)
|
157
|
+
end
|
158
|
+
end
|
160
159
|
|
161
|
-
|
160
|
+
def show_if(field:, equals:, &block)
|
161
|
+
field_id = "#{object_name}_#{field}"
|
162
|
+
@template.tag.div(
|
163
|
+
class: "hidden",
|
164
|
+
data: {
|
165
|
+
controller: "form-field-visibility",
|
166
|
+
"form-field-visibility-field-id-value": field_id,
|
167
|
+
"form-field-visibility-equals-value": equals
|
168
|
+
},
|
169
|
+
&block
|
170
|
+
)
|
171
|
+
end
|
162
172
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
173
|
+
private
|
174
|
+
|
175
|
+
def input_field_classes(method, type, options, include_disabled: true)
|
176
|
+
css_classes = [
|
177
|
+
ui.get_theme.dig(:components, :input, :types, type, :root) || ui.get_theme.dig(:components, :input, :types, :text, :root),
|
178
|
+
when_errors(
|
179
|
+
method,
|
180
|
+
ui.get_theme.dig(:components, :input, :types, type, :error) || ui.get_theme.dig(:components, :input, :types, :text, :error),
|
181
|
+
ui.get_theme.dig(:components, :input, :types, type, :valid) || ui.get_theme.dig(:components, :input, :types, :text, :valid)
|
182
|
+
),
|
183
|
+
options[:class]
|
184
|
+
]
|
185
|
+
|
186
|
+
if include_disabled
|
187
|
+
css_classes << (
|
188
|
+
ui.get_theme.dig(:components, :input, :types, type, :disabled) || ui.get_theme.dig(:components, :input, :types, :text, :disabled)
|
189
|
+
)
|
190
|
+
end
|
191
|
+
|
192
|
+
css_classes.compact.join(' ').split(' ').uniq
|
172
193
|
end
|
173
|
-
|
174
|
-
css_classes.compact.join(' ').split(' ').uniq
|
175
|
-
end
|
176
194
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
195
|
+
def when_errors(method, value, default_value = nil)
|
196
|
+
key = method.to_s.gsub('_id', '').to_sym
|
197
|
+
if object.errors.include?(key) || object.errors.include?(method)
|
198
|
+
value
|
199
|
+
else
|
200
|
+
default_value
|
201
|
+
end
|
183
202
|
end
|
184
|
-
end
|
185
203
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
204
|
+
def merge_class(options, new_class)
|
205
|
+
options[:class] = [options[:class], new_class].compact.join(" ")
|
206
|
+
options
|
207
|
+
end
|
190
208
|
end
|
191
209
|
end
|
@@ -63,6 +63,41 @@ module OkonomiUiKit
|
|
63
63
|
info: "text-info-600 hover:underline"
|
64
64
|
}
|
65
65
|
}
|
66
|
+
},
|
67
|
+
input: {
|
68
|
+
types: {
|
69
|
+
text: {
|
70
|
+
root: "w-full border-0 px-3 py-2 rounded-md ring-1 focus:outline-none focus-within:ring-1",
|
71
|
+
error: "bg-danger-100 text-danger-400 ring-danger-400 focus:ring-danger-600",
|
72
|
+
valid: "text-default-700 ring-gray-300 focus-within:ring-gray-400",
|
73
|
+
disabled: "disabled:bg-gray-50 disabled:cursor-not-allowed"
|
74
|
+
}
|
75
|
+
}
|
76
|
+
},
|
77
|
+
select: {
|
78
|
+
root: "col-start-1 row-start-1 w-full appearance-none rounded-md py-2 pr-8 pl-3 text-base outline-1 focus:outline-none sm:text-sm/6",
|
79
|
+
error: "bg-danger-100 text-danger-400 ring-1 ring-danger-400",
|
80
|
+
valid: "bg-white outline-default-300 text-default-700",
|
81
|
+
wrapper: "grid grid-cols-1",
|
82
|
+
icon: {
|
83
|
+
file: 'heroicons/solid/chevron-down',
|
84
|
+
class: "pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-gray-500 sm:size-4"
|
85
|
+
}
|
86
|
+
},
|
87
|
+
label: {
|
88
|
+
root: "block text-sm/6 font-medium text-default-700"
|
89
|
+
},
|
90
|
+
checkbox: {
|
91
|
+
wrapper: "flex gap-4 items-center",
|
92
|
+
input: {
|
93
|
+
root: "cursor-pointer size-5 rounded-sm border-gray-300 text-primary-600 focus:ring-0 focus:ring-primary-600"
|
94
|
+
},
|
95
|
+
label: {
|
96
|
+
root: "cursor-pointer font-medium text-gray-700"
|
97
|
+
},
|
98
|
+
hint: {
|
99
|
+
root: "cursor-pointer text-sm text-gray-400"
|
100
|
+
}
|
66
101
|
}
|
67
102
|
}
|
68
103
|
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// app/javascript/controllers/file_input_controller.js
|
2
|
+
import { Controller } from "@hotwired/stimulus"
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
connect() {
|
6
|
+
this.input = this.element
|
7
|
+
this.label = document.getElementById("file-name")
|
8
|
+
|
9
|
+
this.input.addEventListener("change", () => {
|
10
|
+
this.label.textContent = this.input.files[0]?.name || "No file chosen"
|
11
|
+
})
|
12
|
+
}
|
13
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["message"]
|
5
|
+
static values = { autoDismiss: Number }
|
6
|
+
|
7
|
+
connect() {
|
8
|
+
if (this.hasAutoDismissValue && this.autoDismissValue > 0) {
|
9
|
+
setTimeout(() => {
|
10
|
+
this.dismiss()
|
11
|
+
}, this.autoDismissValue)
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
dismiss() {
|
16
|
+
this.element.style.transition = 'opacity 0.3s ease-out, transform 0.3s ease-out'
|
17
|
+
this.element.style.opacity = '0'
|
18
|
+
this.element.style.transform = 'translateX(100%)'
|
19
|
+
|
20
|
+
setTimeout(() => {
|
21
|
+
if (this.element.parentNode) {
|
22
|
+
this.element.remove()
|
23
|
+
}
|
24
|
+
}, 300)
|
25
|
+
}
|
26
|
+
|
27
|
+
close(event) {
|
28
|
+
event.preventDefault()
|
29
|
+
this.dismiss()
|
30
|
+
}
|
31
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static values = { fieldId: String, equals: String }
|
5
|
+
|
6
|
+
connect() {
|
7
|
+
console.log("FormFieldVisibilityController connected")
|
8
|
+
this.field = document.getElementById(this.fieldIdValue)
|
9
|
+
if (this.field) {
|
10
|
+
this.toggle()
|
11
|
+
this.field.addEventListener("change", this.toggle.bind(this))
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
toggle() {
|
16
|
+
let currentValue
|
17
|
+
|
18
|
+
if (this.field.type === "checkbox") {
|
19
|
+
currentValue = this.field.checked ? this.field.value : null
|
20
|
+
} else {
|
21
|
+
currentValue = this.field.value
|
22
|
+
}
|
23
|
+
|
24
|
+
const shouldShow = currentValue === this.equalsValue
|
25
|
+
this.element.classList.toggle("hidden", !shouldShow)
|
26
|
+
}
|
27
|
+
}
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["input", "filename", "dropzone", "preview", "destroyInput"]
|
5
|
+
|
6
|
+
connect() {
|
7
|
+
this.dropzoneTarget.addEventListener("dragover", (e) => {
|
8
|
+
e.preventDefault()
|
9
|
+
this.dropzoneTarget.classList.add("bg-gray-100")
|
10
|
+
})
|
11
|
+
|
12
|
+
this.dropzoneTarget.addEventListener("dragleave", () => {
|
13
|
+
this.dropzoneTarget.classList.remove("bg-gray-100")
|
14
|
+
})
|
15
|
+
|
16
|
+
this.dropzoneTarget.addEventListener("drop", (e) => {
|
17
|
+
e.preventDefault()
|
18
|
+
this.dropzoneTarget.classList.remove("bg-gray-100")
|
19
|
+
const files = e.dataTransfer.files
|
20
|
+
if (files.length > 0) {
|
21
|
+
this.inputTarget.files = files
|
22
|
+
this.updatePreview()
|
23
|
+
}
|
24
|
+
})
|
25
|
+
}
|
26
|
+
|
27
|
+
updatePreview() {
|
28
|
+
const file = this.inputTarget.files[0]
|
29
|
+
this.filenameTarget.textContent = file ? file.name : "No file selected"
|
30
|
+
this.destroyInputTarget.value = "0"
|
31
|
+
|
32
|
+
if (file && file.type.startsWith("image/")) {
|
33
|
+
const reader = new FileReader()
|
34
|
+
reader.onload = (e) => {
|
35
|
+
this.previewTarget.innerHTML = `<img src="${e.target.result}" class="max-h-32 rounded" />`
|
36
|
+
}
|
37
|
+
reader.readAsDataURL(file)
|
38
|
+
} else if (file) {
|
39
|
+
this.previewTarget.innerHTML = `
|
40
|
+
<svg class="size-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
41
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16v-4m0 0V7m0 5H3m4 0h4" />
|
42
|
+
</svg>`
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
clear() {
|
47
|
+
this.inputTarget.value = ""
|
48
|
+
this.filenameTarget.textContent = "No file selected"
|
49
|
+
this.previewTarget.innerHTML = `
|
50
|
+
<svg class="size-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
51
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16v-4m0 0V7m0 5H3m4 0h4" />
|
52
|
+
</svg>`
|
53
|
+
this.destroyInputTarget.value = "1"
|
54
|
+
}
|
55
|
+
}
|
@@ -3,7 +3,7 @@
|
|
3
3
|
label_options[:for] = options[:id] if options[:id]
|
4
4
|
%>
|
5
5
|
<%= form.label(method, label_options) do %>
|
6
|
-
<div class="
|
6
|
+
<div class="<%= form.ui.get_theme.dig(:components, :checkbox, :label, :root) %>">
|
7
7
|
<% if options[:label] %>
|
8
8
|
<%= options[:label] %>
|
9
9
|
<% else %>
|
@@ -11,7 +11,7 @@
|
|
11
11
|
<% end %>
|
12
12
|
</div>
|
13
13
|
<% if options[:hint] %>
|
14
|
-
<div class="
|
14
|
+
<div class="<%= form.ui.get_theme.dig(:components, :checkbox, :hint, :root) %>">
|
15
15
|
<%= t("activerecord.hints.#{form.object.class.name.underscore}.#{method}") %>
|
16
16
|
</div>
|
17
17
|
<% end %>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<div class="w-full flex flex-col gap-2">
|
2
2
|
<div class="flex justify-between items-center" x-data="{ open: false }">
|
3
3
|
<% if field_id %>
|
4
|
-
<%= form.label field_id, t("activerecord.attributes.#{form.object_name}.#{field_id}")
|
4
|
+
<%= form.label field_id, t("activerecord.attributes.#{form.object_name}.#{field_id}") %>
|
5
5
|
<% end %>
|
6
6
|
<% if options[:hint] %>
|
7
7
|
<div class="relative">
|
data/config/importmap.rb
CHANGED
@@ -1,2 +1,7 @@
|
|
1
|
+
pin "application", to: "okonomi_ui_kit/application.js", preload: true
|
2
|
+
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
|
3
|
+
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
|
4
|
+
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
|
5
|
+
|
1
6
|
pin "okonomi_ui_kit", to: "okonomi_ui_kit/application.js", preload: true
|
2
7
|
pin_all_from OkonomiUiKit::Engine.root.join("app/javascript/okonomi_ui_kit/controllers"), under: "controllers", to: "okonomi_ui_kit/controllers"
|
@@ -29,7 +29,8 @@ module OkonomiUiKit
|
|
29
29
|
include OkonomiUiKit::PageBuilderHelper
|
30
30
|
include OkonomiUiKit::TableHelper
|
31
31
|
include OkonomiUiKit::UiHelper
|
32
|
-
|
32
|
+
|
33
|
+
ActionView::Base.field_error_proc = ->(html_tag, _instance) { html_tag.html_safe }
|
33
34
|
end
|
34
35
|
end
|
35
36
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: okonomi_ui_kit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Okonomi GmbH
|
@@ -1369,6 +1369,10 @@ files:
|
|
1369
1369
|
- app/helpers/okonomi_ui_kit/ui_helper.rb
|
1370
1370
|
- app/javascript/okonomi_ui_kit/application.js
|
1371
1371
|
- app/javascript/okonomi_ui_kit/controllers/dropdown_controller.js
|
1372
|
+
- app/javascript/okonomi_ui_kit/controllers/file_input_controller.js
|
1373
|
+
- app/javascript/okonomi_ui_kit/controllers/flash_controller.js
|
1374
|
+
- app/javascript/okonomi_ui_kit/controllers/form_field_visibility_controller.js
|
1375
|
+
- app/javascript/okonomi_ui_kit/controllers/upload_controller.js
|
1372
1376
|
- app/javascript/okonomi_ui_kit_manifest.js
|
1373
1377
|
- app/jobs/okonomi_ui_kit/application_job.rb
|
1374
1378
|
- app/mailers/okonomi_ui_kit/application_mailer.rb
|