stimulus_plumbers_tailwind 0.3.3 → 0.4.0
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/CHANGELOG.md +35 -0
- data/lib/stimulus_plumbers/themes/tailwind/avatar.rb +16 -8
- data/lib/stimulus_plumbers/themes/tailwind/button/group.rb +36 -0
- data/lib/stimulus_plumbers/themes/tailwind/button.rb +103 -57
- data/lib/stimulus_plumbers/themes/tailwind/calendar.rb +49 -18
- data/lib/stimulus_plumbers/themes/tailwind/card.rb +40 -7
- data/lib/stimulus_plumbers/themes/tailwind/combobox.rb +37 -11
- data/lib/stimulus_plumbers/themes/tailwind/control.rb +17 -0
- data/lib/stimulus_plumbers/themes/tailwind/form/field.rb +172 -0
- data/lib/stimulus_plumbers/themes/tailwind/form/input.rb +154 -0
- data/lib/stimulus_plumbers/themes/tailwind/form.rb +11 -103
- data/lib/stimulus_plumbers/themes/tailwind/icon.rb +3 -2
- data/lib/stimulus_plumbers/themes/tailwind/layout.rb +19 -1
- data/lib/stimulus_plumbers/themes/tailwind/link.rb +85 -0
- data/lib/stimulus_plumbers/themes/tailwind/list.rb +82 -0
- data/lib/stimulus_plumbers/themes/tailwind_theme.rb +11 -2
- data/lib/stimulus_plumbers_tailwind/version.rb +1 -1
- metadata +7 -2
- data/lib/stimulus_plumbers/themes/tailwind/action_list.rb +0 -33
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Themes
|
|
5
|
+
module Tailwind
|
|
6
|
+
module Form
|
|
7
|
+
module Field
|
|
8
|
+
LABEL = %w[text-(length:--sp-text-sm) font-medium text-(--sp-color-fg)].freeze
|
|
9
|
+
REQUIRED_MARK = %w[text-(--sp-color-error) ml-(--sp-space-0-5)].freeze
|
|
10
|
+
HINT = %w[text-(length:--sp-text-xs) text-(--sp-color-muted-fg)].freeze
|
|
11
|
+
ERROR_TEXT = %w[text-(length:--sp-text-xs) text-(--sp-color-error)].freeze
|
|
12
|
+
|
|
13
|
+
FLOATING_INPUT_BASE = %w[
|
|
14
|
+
peer w-full text-(length:--sp-text-sm) text-(--sp-color-fg) appearance-none
|
|
15
|
+
focus:outline-none focus:ring-0
|
|
16
|
+
].freeze
|
|
17
|
+
FLOATING_INPUT_TYPES = {
|
|
18
|
+
filled: %w[
|
|
19
|
+
rounded-t-(--sp-radius-md) px-(--sp-space-2-5) pb-(--sp-space-2-5) pt-(--sp-space-5)
|
|
20
|
+
bg-(--sp-color-bg-muted) border-0 border-b-2
|
|
21
|
+
].freeze,
|
|
22
|
+
outlined: %w[
|
|
23
|
+
px-(--sp-space-2-5) pb-(--sp-space-2-5) pt-(--sp-space-4)
|
|
24
|
+
bg-transparent rounded-(--sp-radius-md) border
|
|
25
|
+
].freeze,
|
|
26
|
+
standard: %w[
|
|
27
|
+
py-(--sp-space-2-5) px-0
|
|
28
|
+
bg-transparent border-0 border-b-2
|
|
29
|
+
].freeze
|
|
30
|
+
}.freeze
|
|
31
|
+
FLOATING_INPUT_ERROR = %w[border-(--sp-color-error)].freeze
|
|
32
|
+
FLOATING_INPUT_DEFAULT = %w[border-(--sp-color-muted-fg) focus:border-(--sp-color-primary)].freeze
|
|
33
|
+
|
|
34
|
+
FLOATING_GROUP_TYPES = {
|
|
35
|
+
filled: %w[relative].freeze,
|
|
36
|
+
outlined: %w[relative].freeze,
|
|
37
|
+
standard: %w[relative z-0].freeze
|
|
38
|
+
}.freeze
|
|
39
|
+
|
|
40
|
+
FLOATING_LABEL_BASE = %w[
|
|
41
|
+
absolute text-(length:--sp-text-sm) text-(--sp-color-muted-fg)
|
|
42
|
+
duration-300 transform origin-[0]
|
|
43
|
+
].freeze
|
|
44
|
+
FLOATING_LABEL_FOCUS = %w[peer-focus:text-(--sp-color-primary)].freeze
|
|
45
|
+
FLOATING_LABEL_ERROR = %w[text-(--sp-color-error)].freeze
|
|
46
|
+
FLOATING_LABEL_TYPES = {
|
|
47
|
+
filled: %w[
|
|
48
|
+
-translate-y-(--sp-space-4) scale-75 top-(--sp-space-4) z-10 start-(--sp-space-2-5)
|
|
49
|
+
peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0
|
|
50
|
+
peer-focus:scale-75 peer-focus:-translate-y-(--sp-space-4)
|
|
51
|
+
rtl:peer-focus:translate-x-1/4 rtl:peer-focus:left-auto
|
|
52
|
+
].freeze,
|
|
53
|
+
outlined: %w[
|
|
54
|
+
-translate-y-(--sp-space-4) scale-75 top-(--sp-space-2) z-10 start-1
|
|
55
|
+
bg-(--sp-color-bg) px-(--sp-space-2) peer-focus:px-(--sp-space-2)
|
|
56
|
+
peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:top-1/2
|
|
57
|
+
peer-focus:top-(--sp-space-2) peer-focus:scale-75 peer-focus:-translate-y-(--sp-space-4)
|
|
58
|
+
rtl:peer-focus:translate-x-1/4 rtl:peer-focus:left-auto
|
|
59
|
+
].freeze,
|
|
60
|
+
standard: %w[
|
|
61
|
+
-translate-y-(--sp-space-6) scale-75 top-(--sp-space-3) -z-10 start-0
|
|
62
|
+
peer-focus:start-0
|
|
63
|
+
peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0
|
|
64
|
+
peer-focus:scale-75 peer-focus:-translate-y-(--sp-space-6)
|
|
65
|
+
rtl:peer-focus:translate-x-1/4 rtl:peer-focus:left-auto
|
|
66
|
+
].freeze
|
|
67
|
+
}.freeze
|
|
68
|
+
|
|
69
|
+
CHECKBOX_LABEL_TYPES = {
|
|
70
|
+
default: %w[
|
|
71
|
+
flex items-center gap-(--sp-space-2) cursor-pointer py-(--sp-space-0-5) select-none
|
|
72
|
+
text-(length:--sp-text-sm) text-(--sp-color-fg)
|
|
73
|
+
].freeze,
|
|
74
|
+
button: %w[
|
|
75
|
+
flex items-center gap-(--sp-space-3) flex-1 p-(--sp-space-4) cursor-pointer select-none
|
|
76
|
+
text-(length:--sp-text-sm) text-(--sp-color-muted-fg)
|
|
77
|
+
bg-(--sp-color-bg) border border-(--sp-color-border) rounded-(--sp-radius-md) shadow-(--sp-shadow-xs)
|
|
78
|
+
hover:bg-(--sp-color-muted)
|
|
79
|
+
].freeze,
|
|
80
|
+
card: %w[
|
|
81
|
+
flex justify-between items-start flex-1 p-(--sp-space-4) cursor-pointer select-none
|
|
82
|
+
text-(length:--sp-text-sm) text-(--sp-color-muted-fg)
|
|
83
|
+
bg-(--sp-color-bg) border border-(--sp-color-border) rounded-(--sp-radius-md) shadow-(--sp-shadow-xs)
|
|
84
|
+
hover:bg-(--sp-color-muted) hover:border-(--sp-color-border-strong) hover:text-(--sp-color-fg)
|
|
85
|
+
has-[:checked]:border-(--card-ring) has-[:checked]:bg-(--card-ring)/10
|
|
86
|
+
has-[:checked]:text-(--sp-color-fg) has-[:checked]:hover:bg-(--card-ring)/15
|
|
87
|
+
].freeze
|
|
88
|
+
}.freeze
|
|
89
|
+
|
|
90
|
+
RADIO_LABEL_TYPES = {
|
|
91
|
+
default: %w[
|
|
92
|
+
flex items-center gap-(--sp-space-2) cursor-pointer py-(--sp-space-0-5) select-none
|
|
93
|
+
text-(length:--sp-text-sm) text-(--sp-color-fg)
|
|
94
|
+
].freeze,
|
|
95
|
+
button: %w[
|
|
96
|
+
inline-flex items-center justify-between flex-1 p-(--sp-space-4) cursor-pointer select-none
|
|
97
|
+
text-(length:--sp-text-sm) text-(--sp-color-muted-fg)
|
|
98
|
+
bg-(--sp-color-bg) border border-(--sp-color-border) rounded-(--sp-radius-md)
|
|
99
|
+
hover:bg-(--sp-color-muted)
|
|
100
|
+
peer-checked:border-(--card-ring) peer-checked:bg-(--card-ring)/10
|
|
101
|
+
peer-checked:text-(--sp-color-fg) peer-checked:hover:bg-(--card-ring)/15
|
|
102
|
+
].freeze,
|
|
103
|
+
card: %w[
|
|
104
|
+
flex items-start flex-1 p-(--sp-space-4) cursor-pointer select-none
|
|
105
|
+
text-(length:--sp-text-sm) text-(--sp-color-muted-fg)
|
|
106
|
+
bg-(--sp-color-bg) border border-(--sp-color-border) rounded-(--sp-radius-md) shadow-(--sp-shadow-xs)
|
|
107
|
+
hover:bg-(--sp-color-muted) hover:border-(--sp-color-border-strong) hover:text-(--sp-color-fg)
|
|
108
|
+
peer-checked:border-(--card-ring) peer-checked:bg-(--card-ring)/10
|
|
109
|
+
peer-checked:text-(--sp-color-fg) peer-checked:hover:bg-(--card-ring)/15
|
|
110
|
+
].freeze
|
|
111
|
+
}.freeze
|
|
112
|
+
|
|
113
|
+
CHOICE_ITEMS_LAYOUT = {
|
|
114
|
+
stacked: %w[flex flex-col gap-(--sp-space-1)].freeze,
|
|
115
|
+
inline: %w[flex flex-row flex-wrap gap-x-(--sp-space-4) gap-y-(--sp-space-1)].freeze
|
|
116
|
+
}.freeze
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
def form_field_floating_classes(type: nil, error: false)
|
|
121
|
+
{
|
|
122
|
+
classes: klasses(
|
|
123
|
+
*FLOATING_INPUT_BASE,
|
|
124
|
+
*FLOATING_INPUT_TYPES.fetch(type, []),
|
|
125
|
+
*(error ? FLOATING_INPUT_ERROR : FLOATING_INPUT_DEFAULT)
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def form_field_floating_group_classes(type: nil)
|
|
131
|
+
{ classes: klasses(*FLOATING_GROUP_TYPES.fetch(type, [])) }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def form_field_floating_label_classes(type: nil, error: false)
|
|
135
|
+
color = error ? FLOATING_LABEL_ERROR : FLOATING_LABEL_FOCUS
|
|
136
|
+
{ classes: klasses(*FLOATING_LABEL_BASE, *FLOATING_LABEL_TYPES.fetch(type, []), *color) }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def form_field_label_classes(hidden: false, **)
|
|
140
|
+
{ classes: klasses(*LABEL, hidden ? "sr-only" : nil) }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def form_field_required_mark_classes
|
|
144
|
+
{ classes: klasses(*REQUIRED_MARK) }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def form_field_hint_classes
|
|
148
|
+
{ classes: klasses(*HINT) }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def form_field_error_classes
|
|
152
|
+
{ classes: klasses(*ERROR_TEXT) }
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def form_field_choice_items_classes(layout: :stacked)
|
|
156
|
+
{ classes: klasses(*CHOICE_ITEMS_LAYOUT.fetch(layout)) }
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def form_field_checkbox_label_classes(type: :default, variant: :default)
|
|
160
|
+
card_color = type == :card ? Card::VARIANTS.fetch(variant, Card::VARIANTS[:tertiary]) : []
|
|
161
|
+
{ classes: klasses(*CHECKBOX_LABEL_TYPES.fetch(type), *card_color) }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def form_field_radio_label_classes(type: :default, variant: :default)
|
|
165
|
+
card_color = %i[button card].include?(type) ? Card::VARIANTS.fetch(variant, Card::VARIANTS[:tertiary]) : []
|
|
166
|
+
{ classes: klasses(*RADIO_LABEL_TYPES.fetch(type), *card_color) }
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Themes
|
|
5
|
+
module Tailwind
|
|
6
|
+
module Form
|
|
7
|
+
module Input
|
|
8
|
+
INPUT_BASE = %w[
|
|
9
|
+
w-full rounded-(--sp-radius-md) border px-(--sp-space-3) py-(--sp-space-2) text-(length:--sp-text-sm)
|
|
10
|
+
text-(--sp-color-fg) bg-(--sp-color-bg) focus:outline-none focus:ring-2 focus:ring-offset-0
|
|
11
|
+
].freeze
|
|
12
|
+
INPUT_ERROR = %w[border-(--sp-color-error) focus:ring-(--sp-color-error)].freeze
|
|
13
|
+
INPUT_DEFAULT = %w[border-(--sp-color-muted-fg) focus:ring-(--sp-focus-ring-color)].freeze
|
|
14
|
+
|
|
15
|
+
CHECKBOX_TYPES = {
|
|
16
|
+
default: %w[
|
|
17
|
+
size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0
|
|
18
|
+
border border-(--sp-color-border) bg-(--sp-color-muted)
|
|
19
|
+
focus:ring-2 focus:ring-(--sp-focus-ring-color) focus:outline-none
|
|
20
|
+
disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer
|
|
21
|
+
].freeze,
|
|
22
|
+
button: %w[
|
|
23
|
+
size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0
|
|
24
|
+
border border-(--sp-color-border) bg-(--sp-color-muted)
|
|
25
|
+
focus:ring-2 focus:ring-(--sp-focus-ring-color) focus:outline-none
|
|
26
|
+
disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer
|
|
27
|
+
].freeze,
|
|
28
|
+
card: %w[
|
|
29
|
+
size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0
|
|
30
|
+
border border-(--sp-color-border) bg-(--sp-color-muted)
|
|
31
|
+
checked:border-(--card-ring)
|
|
32
|
+
focus:ring-2 focus:ring-(--card-ring) focus:outline-none
|
|
33
|
+
disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer
|
|
34
|
+
].freeze
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
RADIO_TYPES = {
|
|
38
|
+
default: %w[
|
|
39
|
+
size-(--sp-control-size) rounded-full shrink-0
|
|
40
|
+
[accent-color:var(--sp-color-primary)] cursor-pointer
|
|
41
|
+
focus:ring-2 focus:ring-(--sp-focus-ring-color) focus:outline-none
|
|
42
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
43
|
+
].freeze,
|
|
44
|
+
button: %w[hidden peer].freeze,
|
|
45
|
+
card: %w[hidden peer].freeze
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
INPUT_GROUP_BASE = %w[flex items-center overflow-hidden rounded-(--sp-radius-md) border].freeze
|
|
49
|
+
INPUT_GROUP_BORDER = { error: "border-(--sp-color-error)", default: "border-(--sp-color-muted-fg)" }.freeze
|
|
50
|
+
|
|
51
|
+
COMBOBOX_INPUT = %w[
|
|
52
|
+
[&>input:not([type=hidden])]:border-0
|
|
53
|
+
[&>input:not([type=hidden])]:rounded-none
|
|
54
|
+
[&>input:not([type=hidden])]:px-0
|
|
55
|
+
[&>input:not([type=hidden])]:py-0
|
|
56
|
+
[&>input:not([type=hidden])]:bg-transparent
|
|
57
|
+
[&>input:not([type=hidden])]:shadow-none
|
|
58
|
+
[&>input:not([type=hidden])]:focus:ring-0
|
|
59
|
+
].freeze
|
|
60
|
+
COMBOBOX_TRIGGER_GROUP = %w[
|
|
61
|
+
[&>div:first-child]:border-0
|
|
62
|
+
[&>div:first-child]:rounded-none
|
|
63
|
+
[&>div:first-child]:px-0
|
|
64
|
+
[&>div:first-child]:py-0
|
|
65
|
+
[&>div:first-child]:focus-within:ring-0
|
|
66
|
+
].freeze
|
|
67
|
+
|
|
68
|
+
BUTTON_REVEAL = %w[
|
|
69
|
+
self-stretch border-0 bg-transparent px-(--sp-space-3) cursor-pointer text-(--sp-color-muted-fg)
|
|
70
|
+
hover:text-(--sp-color-fg) text-(length:--sp-text-sm)
|
|
71
|
+
].freeze
|
|
72
|
+
BUTTON_CLEAR = %w[
|
|
73
|
+
self-stretch border-0 bg-transparent px-(--sp-space-2) cursor-pointer text-(--sp-color-muted-fg)
|
|
74
|
+
hover:text-(--sp-color-fg) text-(length:--sp-text-sm)
|
|
75
|
+
].freeze
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def form_field_input_classes(error: false)
|
|
80
|
+
{ classes: klasses(*INPUT_BASE, *(error ? INPUT_ERROR : INPUT_DEFAULT)) }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def form_field_input_textarea_classes(error: false)
|
|
84
|
+
form_field_input_classes(error: error)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def form_field_input_file_classes(error: false)
|
|
88
|
+
form_field_input_classes(error: error)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def form_field_input_select_classes(error: false)
|
|
92
|
+
form_field_input_classes(error: error)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def form_field_input_checkbox_classes(type: :default, variant: :default, **)
|
|
96
|
+
card_color = type == :card ? Card::VARIANTS.fetch(variant, Card::VARIANTS[:tertiary]) : []
|
|
97
|
+
{ classes: klasses(*CHECKBOX_TYPES.fetch(type), *card_color) }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def form_field_input_radio_classes(type: :default, variant: :default, **)
|
|
101
|
+
card_color = %i[button card].include?(type) ? Card::VARIANTS.fetch(variant, Card::VARIANTS[:tertiary]) : []
|
|
102
|
+
{ classes: klasses(*RADIO_TYPES.fetch(type), *card_color) }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def input_group_classes(error: false)
|
|
106
|
+
{ classes: klasses(*INPUT_GROUP_BASE, INPUT_GROUP_BORDER[error ? :error : :default]) }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def form_field_input_combobox_classes(error: false)
|
|
110
|
+
{ classes: klasses(
|
|
111
|
+
*INPUT_BASE,
|
|
112
|
+
*(error ? INPUT_ERROR : INPUT_DEFAULT),
|
|
113
|
+
*COMBOBOX_INPUT,
|
|
114
|
+
*COMBOBOX_TRIGGER_GROUP
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def form_field_input_reveal_classes(**)
|
|
120
|
+
{
|
|
121
|
+
classes: klasses(
|
|
122
|
+
"[&>input]:border-0",
|
|
123
|
+
"[&>input]:rounded-none",
|
|
124
|
+
"[&>input]:bg-transparent",
|
|
125
|
+
"[&>input]:shadow-none",
|
|
126
|
+
"[&>input]:focus:ring-0"
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def form_field_input_clearable_classes
|
|
132
|
+
{
|
|
133
|
+
classes: klasses(
|
|
134
|
+
"[&>input]:border-0",
|
|
135
|
+
"[&>input]:rounded-none",
|
|
136
|
+
"[&>input]:bg-transparent",
|
|
137
|
+
"[&>input]:shadow-none",
|
|
138
|
+
"[&>input]:focus:ring-0"
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def form_field_input_button_reveal_classes
|
|
144
|
+
{ classes: klasses(*BUTTON_REVEAL) }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def form_field_input_button_clear_classes
|
|
148
|
+
{ classes: klasses(*BUTTON_CLEAR) }
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -4,38 +4,8 @@ module StimulusPlumbers
|
|
|
4
4
|
module Themes
|
|
5
5
|
module Tailwind
|
|
6
6
|
module Form
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
text-(--sp-color-fg) bg-(--sp-color-bg) focus:outline-none focus:ring-2 focus:ring-offset-0
|
|
10
|
-
[.sp-form-input-group_&]:border-0 [.sp-form-input-group_&]:rounded-none
|
|
11
|
-
[.sp-form-input-group_&]:bg-transparent [.sp-form-input-group_&]:shadow-none
|
|
12
|
-
[.sp-form-input-group_&]:focus:ring-0
|
|
13
|
-
].freeze
|
|
14
|
-
INPUT_ERROR = %w[border-(--sp-color-error) focus:ring-(--sp-color-error)].freeze
|
|
15
|
-
INPUT_DEFAULT = %w[border-(--sp-color-muted-fg) focus:ring-(--sp-focus-ring-color)].freeze
|
|
16
|
-
|
|
17
|
-
GROUP_BASE = %w[flex gap-(--sp-space-1) mb-(--sp-space-3)].freeze
|
|
18
|
-
GROUP_INLINE = %w[flex-row items-center].freeze
|
|
19
|
-
|
|
20
|
-
LABEL = %w[text-(length:--sp-text-sm) font-medium text-(--sp-color-fg)].freeze
|
|
21
|
-
REQUIRED_MARK = %w[text-(--sp-color-error) ml-(--sp-space-0-5)].freeze
|
|
22
|
-
DETAILS = %w[text-(length:--sp-text-xs) text-(--sp-color-muted-fg)].freeze
|
|
23
|
-
ERROR_TEXT = %w[text-(length:--sp-text-xs) text-(--sp-color-error)].freeze
|
|
24
|
-
CHECKBOX = %w[size-(--sp-control-size) rounded border-(--sp-color-muted-fg) text-(--sp-color-primary)].freeze
|
|
25
|
-
RADIO = %w[size-(--sp-control-size) border-(--sp-color-muted-fg) text-(--sp-color-primary)].freeze
|
|
26
|
-
|
|
27
|
-
INPUT_GROUP_BASE = %w[flex items-center overflow-hidden rounded-(--sp-radius-md) border].freeze
|
|
28
|
-
INPUT_GROUP_BORDER = { error: "border-(--sp-color-error)", default: "border-(--sp-color-muted-fg)" }.freeze
|
|
29
|
-
|
|
30
|
-
BUTTON_REVEAL = %w[
|
|
31
|
-
self-stretch border-0 bg-transparent px-(--sp-space-3) cursor-pointer text-(--sp-color-muted-fg)
|
|
32
|
-
hover:text-(--sp-color-fg) text-(length:--sp-text-sm)
|
|
33
|
-
].freeze
|
|
34
|
-
BUTTON_CLEAR = %w[
|
|
35
|
-
self-stretch border-0 bg-transparent px-(--sp-space-2) cursor-pointer text-(--sp-color-muted-fg)
|
|
36
|
-
hover:text-(--sp-color-fg) text-(length:--sp-text-sm)
|
|
37
|
-
].freeze
|
|
38
|
-
SUBMIT_LINK = %w[cursor-pointer text-(length:--sp-text-sm) font-medium text-(--sp-color-fg) hover:underline].freeze
|
|
7
|
+
GROUP_BASE = %w[flex gap-(--sp-space-1) mb-(--sp-space-3)].freeze
|
|
8
|
+
GROUP_INLINE = %w[flex-row items-center].freeze
|
|
39
9
|
|
|
40
10
|
private
|
|
41
11
|
|
|
@@ -43,77 +13,15 @@ module StimulusPlumbers
|
|
|
43
13
|
{ classes: klasses(*GROUP_BASE, layout == :inline ? GROUP_INLINE : "flex-col") }
|
|
44
14
|
end
|
|
45
15
|
|
|
46
|
-
def
|
|
47
|
-
{ classes: klasses(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
{ classes: klasses(*DETAILS) }
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def form_error_classes
|
|
59
|
-
{ classes: klasses(*ERROR_TEXT) }
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def form_input_classes(error: false)
|
|
63
|
-
{ classes: klasses(*INPUT_BASE, *(error ? INPUT_ERROR : INPUT_DEFAULT)) }
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def form_textarea_classes(error: false)
|
|
67
|
-
form_input_classes(error: error)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def form_file_classes(error: false)
|
|
71
|
-
form_input_classes(error: error)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def form_select_classes(error: false)
|
|
75
|
-
form_input_classes(error: error)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def form_checkbox_classes(**)
|
|
79
|
-
{ classes: klasses(*CHECKBOX) }
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def form_radio_classes(**)
|
|
83
|
-
{ classes: klasses(*RADIO) }
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def input_group_classes(error: false)
|
|
87
|
-
{ classes: klasses(*INPUT_GROUP_BASE, INPUT_GROUP_BORDER[error ? :error : :default]) }
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def form_combobox_classes(error: false)
|
|
91
|
-
{ classes: klasses(*INPUT_BASE, *(error ? INPUT_ERROR : INPUT_DEFAULT), "sp-form-combobox") }
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def form_input_reveal_classes(**)
|
|
95
|
-
{ classes: "sp-form-input-group" }
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def form_input_clearable_classes
|
|
99
|
-
{ classes: "sp-form-input-group" }
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def form_button_reveal_classes
|
|
103
|
-
{ classes: klasses(*BUTTON_REVEAL) }
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def form_button_clear_classes
|
|
107
|
-
{ classes: klasses(*BUTTON_CLEAR) }
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def form_submit_classes(variant: :default)
|
|
111
|
-
case variant
|
|
112
|
-
when :button
|
|
113
|
-
{ classes: klasses(*Button::BASE, *Button::VARIANTS[:primary], *Button::SIZES[:md]) }
|
|
114
|
-
else
|
|
115
|
-
{ classes: klasses(*SUBMIT_LINK) }
|
|
116
|
-
end
|
|
16
|
+
def form_submit_classes(type: :default, variant: :primary)
|
|
17
|
+
{ classes: klasses(
|
|
18
|
+
*Button::BASE,
|
|
19
|
+
*Button::LAYOUT,
|
|
20
|
+
*Button::VARIANTS.fetch(variant, Button::VARIANTS[:primary]),
|
|
21
|
+
*Button::TYPES.fetch(type, Button::TYPES[:default]),
|
|
22
|
+
*(type == :card ? [] : Button::SIZES[:md])
|
|
23
|
+
)
|
|
24
|
+
}
|
|
117
25
|
end
|
|
118
26
|
end
|
|
119
27
|
end
|
|
@@ -9,8 +9,9 @@ module StimulusPlumbers
|
|
|
9
9
|
module Tailwind
|
|
10
10
|
module Icon
|
|
11
11
|
ALIASES = {
|
|
12
|
-
"close"
|
|
13
|
-
"calendar"
|
|
12
|
+
"close" => "x-mark",
|
|
13
|
+
"calendar" => "calendar-days",
|
|
14
|
+
"external-link" => "arrow-top-right-on-square"
|
|
14
15
|
}.freeze
|
|
15
16
|
|
|
16
17
|
ICONS = StimulusPlumbers::Themes::Icons::Registry.new(
|
|
@@ -7,8 +7,18 @@ module StimulusPlumbers
|
|
|
7
7
|
DIVIDER_SEPARATOR = %w[border-t border-(--sp-color-border) my-(--sp-space-1)].freeze
|
|
8
8
|
DIVIDER = %w[flex items-center gap-(--sp-space-3)].freeze
|
|
9
9
|
DIVIDER_LABEL = %w[text-(length:--sp-text-sm) text-(--sp-color-fg-muted) whitespace-nowrap font-medium].freeze
|
|
10
|
+
POPOVER_WRAPPER = %w[relative inline-block].freeze
|
|
11
|
+
POPOVER_TRIGGER = [
|
|
12
|
+
*Control::BASE,
|
|
13
|
+
"inline-flex items-center justify-center gap-(--sp-space-2)",
|
|
14
|
+
"rounded-(--sp-radius-md)",
|
|
15
|
+
"focus-visible:ring-(--sp-focus-ring-color)",
|
|
16
|
+
"border border-(--sp-color-border) bg-transparent text-(--sp-color-fg)",
|
|
17
|
+
"hover:bg-(--sp-color-muted)",
|
|
18
|
+
"h-9 px-(--sp-space-4) py-(--sp-space-2) text-(length:--sp-text-sm)"
|
|
19
|
+
].freeze
|
|
10
20
|
POPOVER = %w[
|
|
11
|
-
rounded-(--sp-radius-
|
|
21
|
+
rounded-(--sp-radius-md) border border-(--sp-color-border)
|
|
12
22
|
bg-(--sp-color-bg) shadow-(--sp-shadow-md) z-(--sp-z-popover)
|
|
13
23
|
].freeze
|
|
14
24
|
|
|
@@ -26,6 +36,14 @@ module StimulusPlumbers
|
|
|
26
36
|
{ classes: klasses(*DIVIDER_LABEL) }
|
|
27
37
|
end
|
|
28
38
|
|
|
39
|
+
def popover_wrapper_classes
|
|
40
|
+
{ classes: klasses(*POPOVER_WRAPPER) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def popover_trigger_classes
|
|
44
|
+
{ classes: klasses(*POPOVER_TRIGGER) }
|
|
45
|
+
end
|
|
46
|
+
|
|
29
47
|
def popover_classes
|
|
30
48
|
{ classes: klasses(*POPOVER) }
|
|
31
49
|
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Themes
|
|
5
|
+
module Tailwind
|
|
6
|
+
module Link
|
|
7
|
+
VARIANTS = {
|
|
8
|
+
default: %w[
|
|
9
|
+
[--link-color:var(--sp-color-primary)]
|
|
10
|
+
[--link-ring:var(--sp-color-primary)]
|
|
11
|
+
[--link-bg:var(--sp-color-primary)]
|
|
12
|
+
].freeze,
|
|
13
|
+
success: %w[
|
|
14
|
+
[--link-color:var(--sp-color-success)]
|
|
15
|
+
[--link-ring:var(--sp-color-success-ring)]
|
|
16
|
+
[--link-bg:var(--sp-color-success)]
|
|
17
|
+
].freeze,
|
|
18
|
+
destructive: %w[
|
|
19
|
+
[--link-color:var(--sp-color-destructive)]
|
|
20
|
+
[--link-ring:var(--sp-color-destructive)]
|
|
21
|
+
[--link-bg:var(--sp-color-destructive)]
|
|
22
|
+
].freeze,
|
|
23
|
+
warning: %w[
|
|
24
|
+
[--link-color:var(--sp-color-warning)]
|
|
25
|
+
[--link-ring:var(--sp-color-warning-ring)]
|
|
26
|
+
[--link-bg:var(--sp-color-warning)]
|
|
27
|
+
].freeze,
|
|
28
|
+
info: %w[
|
|
29
|
+
[--link-color:var(--sp-color-info)]
|
|
30
|
+
[--link-ring:var(--sp-color-info-ring)]
|
|
31
|
+
[--link-bg:var(--sp-color-info)]
|
|
32
|
+
].freeze
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
BASE = [
|
|
36
|
+
*Control::BASE,
|
|
37
|
+
"inline-flex items-center gap-(--sp-space-1)",
|
|
38
|
+
"text-(--link-color)",
|
|
39
|
+
"hover:underline",
|
|
40
|
+
"focus-visible:ring-(--link-ring)"
|
|
41
|
+
].freeze
|
|
42
|
+
|
|
43
|
+
BUTTON = [
|
|
44
|
+
*Control::BASE,
|
|
45
|
+
"whitespace-nowrap",
|
|
46
|
+
"inline-flex items-center justify-center gap-(--sp-space-2)",
|
|
47
|
+
"h-9 px-(--sp-space-4) text-(length:--sp-text-base)",
|
|
48
|
+
"[&:not(:has(>span))]:aspect-square",
|
|
49
|
+
"[&:not(:has(>span))]:px-0",
|
|
50
|
+
"rounded-(--sp-radius-md)",
|
|
51
|
+
"bg-(--sp-color-bg) text-(--link-color)",
|
|
52
|
+
"border border-(--link-color)",
|
|
53
|
+
"hover:bg-(--link-bg)/10",
|
|
54
|
+
"focus-visible:ring-(--link-ring)"
|
|
55
|
+
].freeze
|
|
56
|
+
|
|
57
|
+
CARD = [
|
|
58
|
+
*Control::BASE,
|
|
59
|
+
"whitespace-nowrap",
|
|
60
|
+
*Button::CARD,
|
|
61
|
+
"rounded-(--sp-radius-md)",
|
|
62
|
+
"bg-(--sp-color-bg) text-(--link-color)",
|
|
63
|
+
"border border-(--link-color) shadow-(--sp-shadow-xs)",
|
|
64
|
+
"hover:bg-(--link-bg)/10",
|
|
65
|
+
"focus-visible:ring-(--link-ring)"
|
|
66
|
+
].freeze
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def link_classes(type: :default, variant: :default)
|
|
71
|
+
variant_classes = VARIANTS.fetch(variant, VARIANTS[:default])
|
|
72
|
+
case type
|
|
73
|
+
when :button then { classes: klasses(*BUTTON, *variant_classes) }
|
|
74
|
+
when :card then { classes: klasses(*CARD, *variant_classes) }
|
|
75
|
+
else { classes: klasses(*BASE, *variant_classes) }
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def link_icon_classes
|
|
80
|
+
{ classes: klasses("size-(--sp-control-size)", "stroke-current") }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Themes
|
|
5
|
+
module Tailwind
|
|
6
|
+
module List
|
|
7
|
+
ITEM_BASE = [
|
|
8
|
+
*Control::BASE,
|
|
9
|
+
"flex items-center gap-(--sp-space-2) w-full",
|
|
10
|
+
"px-(--sp-space-2) py-(--sp-space-1)",
|
|
11
|
+
"rounded-(--sp-radius-sm) text-(length:--sp-text-sm)",
|
|
12
|
+
"cursor-pointer select-none",
|
|
13
|
+
"text-(--sp-color-fg)",
|
|
14
|
+
"focus-visible:ring-(--sp-color-primary-ring)",
|
|
15
|
+
"hover:bg-(--sp-color-muted) focus:bg-(--sp-color-muted) focus:text-(--sp-color-fg)",
|
|
16
|
+
"aria-[current]:bg-(--sp-color-primary)/10 aria-[current]:text-(--sp-color-primary)"
|
|
17
|
+
].freeze
|
|
18
|
+
|
|
19
|
+
ITEM_CONTENT_BASE = %w[
|
|
20
|
+
flex flex-col flex-1 min-w-0
|
|
21
|
+
].freeze
|
|
22
|
+
|
|
23
|
+
ITEM_TITLE_BASE = %w[
|
|
24
|
+
text-(length:--sp-text-sm) font-medium text-(--sp-color-fg)
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
27
|
+
ITEM_DESCRIPTION_BASE = %w[
|
|
28
|
+
text-(length:--sp-text-xs) text-(--sp-color-muted-fg)
|
|
29
|
+
].freeze
|
|
30
|
+
|
|
31
|
+
SECTION_TITLE_BASE = %w[
|
|
32
|
+
block px-(--sp-space-2) pb-(--sp-space-1)
|
|
33
|
+
text-(length:--sp-text-xs) font-semibold uppercase tracking-wider
|
|
34
|
+
text-(--sp-color-muted-fg)
|
|
35
|
+
].freeze
|
|
36
|
+
|
|
37
|
+
SECTION_DESCRIPTION_BASE = %w[
|
|
38
|
+
block px-(--sp-space-2) pb-(--sp-space-1)
|
|
39
|
+
text-(length:--sp-text-xs) text-(--sp-color-muted-fg)
|
|
40
|
+
].freeze
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def list_classes
|
|
45
|
+
{ classes: klasses("py-(--sp-space-1) divide-y divide-(--sp-color-border)") }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def list_section_classes
|
|
49
|
+
{ classes: klasses("py-(--sp-space-2)") }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def list_section_title_classes
|
|
53
|
+
{ classes: klasses(*SECTION_TITLE_BASE) }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def list_section_description_classes
|
|
57
|
+
{ classes: klasses(*SECTION_DESCRIPTION_BASE) }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def list_item_classes
|
|
61
|
+
{ classes: klasses(*ITEM_BASE) }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def list_item_icon_classes
|
|
65
|
+
{ classes: klasses("size-(--sp-control-size)", "stroke-current") }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def list_item_content_classes
|
|
69
|
+
{ classes: klasses(*ITEM_CONTENT_BASE) }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def list_item_title_classes
|
|
73
|
+
{ classes: klasses(*ITEM_TITLE_BASE) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def list_item_description_classes
|
|
77
|
+
{ classes: klasses(*ITEM_DESCRIPTION_BASE) }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|