stimulus_plumbers_tailwind 0.3.3 → 0.4.2

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.
@@ -4,33 +4,32 @@ module StimulusPlumbers
4
4
  module Themes
5
5
  module Tailwind
6
6
  module Combobox
7
+ # ── Container / popover ───────────────────────────────────────────────
8
+ CONTAINER = %w[relative].freeze
9
+ POPOVER = %w[absolute top-full left-0 min-w-full].freeze
10
+
11
+ # ── Trigger ───────────────────────────────────────────────────────────
7
12
  TRIGGER = %w[
8
- w-full rounded-(--sp-radius-md) border border-(--sp-color-muted-fg)
13
+ w-full rounded-(--sp-radius-md) border border-(--sp-color-muted-fg) hover:border-(--sp-color-fg)
9
14
  px-(--sp-space-3) py-(--sp-space-2)
10
15
  text-(length:--sp-text-sm) text-(--sp-color-fg) bg-(--sp-color-bg)
11
16
  focus:outline-none focus:ring-2 focus:ring-(--sp-focus-ring-color)
12
- [.sp-form-combobox_&]:border-0 [.sp-form-combobox_&]:rounded-none
13
- [.sp-form-combobox_&]:px-0 [.sp-form-combobox_&]:py-0
14
- [.sp-form-combobox_&]:bg-transparent [.sp-form-combobox_&]:shadow-none
15
- [.sp-form-combobox_&]:focus:ring-0
16
- [.sp-combobox-group_&]:border-0 [.sp-combobox-group_&]:rounded-none
17
- [.sp-combobox-group_&]:px-0 [.sp-combobox-group_&]:py-0
18
- [.sp-combobox-group_&]:bg-transparent [.sp-combobox-group_&]:shadow-none
19
- [.sp-combobox-group_&]:focus:ring-0
20
17
  ].freeze
21
18
 
22
19
  TRIGGER_GROUP = %w[
23
20
  flex items-center gap-(--sp-space-2) overflow-hidden
24
- rounded-(--sp-radius-md) border border-(--sp-color-muted-fg) bg-(--sp-color-bg)
21
+ rounded-(--sp-radius-md) border border-(--sp-color-muted-fg) hover:border-(--sp-color-fg) bg-(--sp-color-bg)
25
22
  px-(--sp-space-3) py-(--sp-space-2)
26
23
  focus-within:outline-none focus-within:ring-2 focus-within:ring-(--sp-focus-ring-color)
27
- sp-combobox-group
28
- [.sp-form-combobox_&]:border-0 [.sp-form-combobox_&]:rounded-none
29
- [.sp-form-combobox_&]:focus-within:ring-0
24
+ [&>input]:border-0 [&>input]:rounded-none
25
+ [&>input]:px-0 [&>input]:py-0
26
+ [&>input]:bg-transparent [&>input]:shadow-none
27
+ [&>input]:focus:ring-0
30
28
  ].freeze
31
29
 
30
+ # ── Listbox / options ─────────────────────────────────────────────────
32
31
  LISTBOX = %w[
33
- py-(--sp-space-1) overflow-y-auto max-h-60
32
+ py-(--sp-space-1) overflow-y-auto max-h-60 min-h-(--sp-space-6)
34
33
  ].freeze
35
34
 
36
35
  OPTION_BASE = %w[
@@ -51,6 +50,7 @@ module StimulusPlumbers
51
50
 
52
51
  OPTION_GROUP = %w[py-(--sp-space-1)].freeze
53
52
 
53
+ # ── Typeahead states ──────────────────────────────────────────────────
54
54
  TYPEAHEAD_LOADING = %w[
55
55
  flex items-center justify-center
56
56
  py-(--sp-space-2) text-(length:--sp-text-sm) text-(--sp-color-muted-fg)
@@ -61,10 +61,26 @@ module StimulusPlumbers
61
61
  py-(--sp-space-2) text-(length:--sp-text-sm) text-(--sp-color-muted-fg)
62
62
  ].freeze
63
63
 
64
- TIME = %w[flex gap-(--sp-space-2) overflow-hidden].freeze
64
+ # ── Time picker ───────────────────────────────────────────────────────
65
+ TIME = %w[flex gap-(--sp-space-2) overflow-hidden].freeze
66
+ TIME_DRUM_UNIT = %w[flex-1 min-w-0].freeze
67
+ TIME_DRUM_PERIOD = %w[shrink-0].freeze
68
+
69
+ # ── Date navigation ───────────────────────────────────────────────────
70
+ DATE_NAV = %w[flex items-center justify-between gap-(--sp-space-1) mb-(--sp-space-2)].freeze
71
+ DATE_NAV_BTN = %w[].freeze
72
+ DATE_NAV_TITLE = %w[flex-1 text-center].freeze
65
73
 
66
74
  private
67
75
 
76
+ def combobox_classes
77
+ { classes: klasses(*CONTAINER) }
78
+ end
79
+
80
+ def combobox_popover_classes
81
+ { classes: klasses(*POPOVER) }
82
+ end
83
+
68
84
  def combobox_trigger_classes
69
85
  { classes: klasses(*TRIGGER) }
70
86
  end
@@ -95,6 +111,10 @@ module StimulusPlumbers
95
111
  { classes: klasses(*TYPEAHEAD_LOADING) }
96
112
  end
97
113
 
114
+ def combobox_typeahead_loading_icon_classes
115
+ { classes: klasses("size-(--sp-icon-size-md)", "animate-spin") }
116
+ end
117
+
98
118
  def combobox_typeahead_empty_classes
99
119
  { classes: klasses(*TYPEAHEAD_EMPTY) }
100
120
  end
@@ -102,6 +122,22 @@ module StimulusPlumbers
102
122
  def combobox_time_classes
103
123
  { classes: klasses(*TIME) }
104
124
  end
125
+
126
+ def combobox_time_drum_classes(type: :unit)
127
+ { classes: klasses(*(type == :period ? TIME_DRUM_PERIOD : TIME_DRUM_UNIT)) }
128
+ end
129
+
130
+ def combobox_date_navigation_classes
131
+ { classes: klasses(*DATE_NAV) }
132
+ end
133
+
134
+ def combobox_date_navigation_navigator_classes
135
+ { classes: klasses(*DATE_NAV_BTN) }
136
+ end
137
+
138
+ def combobox_date_navigation_title_classes
139
+ { classes: klasses(*DATE_NAV_TITLE) }
140
+ end
105
141
  end
106
142
  end
107
143
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Tailwind
6
+ module Control
7
+ BASE = %w[
8
+ font-medium transition-colors
9
+ focus-visible:outline-none
10
+ focus-visible:ring-(length:--sp-focus-ring-width)
11
+ focus-visible:ring-offset-(length:--sp-focus-ring-offset)
12
+ disabled:pointer-events-none disabled:opacity-50
13
+ ].freeze
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,159 @@
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_GROUP_TYPES = {
14
+ filled: %w[relative].freeze,
15
+ outlined: %w[relative].freeze,
16
+ standard: %w[relative z-0].freeze
17
+ }.freeze
18
+
19
+ FLOATING_LABEL_BASE = %w[
20
+ absolute text-(length:--sp-text-sm) text-(--sp-color-muted-fg)
21
+ duration-300 transform origin-[0]
22
+ ].freeze
23
+ FLOATING_LABEL_FOCUS = %w[peer-focus-within:text-(--sp-color-primary)].freeze
24
+ FLOATING_LABEL_ERROR = %w[text-(--sp-color-error)].freeze
25
+ FLOATING_LABEL_TYPES = {
26
+ filled: %w[
27
+ -translate-y-(--sp-space-4) scale-75 top-(--sp-space-4) z-10 start-(--sp-space-2-5)
28
+ peer-placeholder-shown:scale-100
29
+ peer-placeholder-shown:translate-y-0
30
+ peer-has-[input:not(:focus):placeholder-shown]:scale-100
31
+ peer-has-[input:not(:focus):placeholder-shown]:translate-y-0
32
+ peer-focus-within:scale-75
33
+ peer-focus-within:-translate-y-(--sp-space-4)
34
+ rtl:peer-focus-within:translate-x-1/4 rtl:peer-focus-within:left-auto
35
+ ].freeze,
36
+ outlined: %w[
37
+ -translate-y-(--sp-space-4) scale-75 top-(--sp-space-2) z-10 start-1
38
+ bg-(--sp-color-bg) px-(--sp-space-2) peer-focus-within:px-(--sp-space-2)
39
+ peer-placeholder-shown:scale-100
40
+ peer-placeholder-shown:-translate-y-1/2
41
+ peer-placeholder-shown:top-1/2
42
+ peer-has-[input:not(:focus):placeholder-shown]:scale-100
43
+ peer-has-[input:not(:focus):placeholder-shown]:-translate-y-1/2
44
+ peer-has-[input:not(:focus):placeholder-shown]:top-1/2
45
+ peer-focus-within:top-(--sp-space-2) peer-focus-within:scale-75
46
+ peer-focus-within:-translate-y-(--sp-space-4)
47
+ rtl:peer-focus-within:translate-x-1/4 rtl:peer-focus-within:left-auto
48
+ ].freeze,
49
+ standard: %w[
50
+ -translate-y-(--sp-space-6) scale-75 top-(--sp-space-3) -z-10 start-0
51
+ peer-focus-within:start-0
52
+ peer-placeholder-shown:scale-100
53
+ peer-placeholder-shown:translate-y-0
54
+ peer-has-[input:not(:focus):placeholder-shown]:scale-100
55
+ peer-has-[input:not(:focus):placeholder-shown]:translate-y-0
56
+ peer-focus-within:scale-75
57
+ peer-focus-within:-translate-y-(--sp-space-6)
58
+ rtl:peer-focus-within:translate-x-1/4 rtl:peer-focus-within:left-auto
59
+ ].freeze
60
+ }.freeze
61
+
62
+ CHECKBOX_LABEL_TYPES = {
63
+ default: %w[
64
+ flex items-center gap-(--sp-space-2) cursor-pointer py-(--sp-space-0-5) select-none
65
+ text-(length:--sp-text-sm) text-(--sp-color-fg)
66
+ ].freeze,
67
+ button: %w[
68
+ flex items-center gap-(--sp-space-3) flex-1 p-(--sp-space-4) cursor-pointer select-none
69
+ text-(length:--sp-text-sm) text-(--sp-color-muted-fg)
70
+ bg-(--sp-color-bg) border border-(--sp-color-border) rounded-(--sp-radius-md) shadow-(--sp-shadow-xs)
71
+ hover:bg-(--sp-color-muted)
72
+ ].freeze,
73
+ card: %w[
74
+ flex justify-between items-center gap-(--sp-space-3) flex-1 p-(--sp-space-4) cursor-pointer select-none
75
+ text-(length:--sp-text-sm) text-(--sp-color-muted-fg)
76
+ bg-(--sp-color-bg) border border-(--sp-color-border) rounded-(--sp-radius-md) shadow-(--sp-shadow-xs)
77
+ hover:bg-(--sp-color-muted) hover:border-(--sp-color-border-strong) hover:text-(--sp-color-fg)
78
+ has-[:checked]:border-(--card-ring) has-[:checked]:bg-(--card-ring)/10
79
+ has-[:checked]:text-(--sp-color-fg) has-[:checked]:hover:bg-(--card-ring)/15
80
+ ].freeze
81
+ }.freeze
82
+
83
+ RADIO_LABEL_TYPES = {
84
+ default: %w[
85
+ flex items-center gap-(--sp-space-2) cursor-pointer py-(--sp-space-0-5) select-none
86
+ text-(length:--sp-text-sm) text-(--sp-color-fg)
87
+ ].freeze,
88
+ button: %w[
89
+ inline-flex items-center justify-between flex-1 p-(--sp-space-4) cursor-pointer select-none
90
+ text-(length:--sp-text-sm) text-(--sp-color-muted-fg)
91
+ bg-(--sp-color-bg) border border-(--sp-color-border) rounded-(--sp-radius-md)
92
+ hover:bg-(--sp-color-muted)
93
+ group-has-[:checked]:border-(--card-ring) group-has-[:checked]:bg-(--card-ring)/10
94
+ group-has-[:checked]:text-(--sp-color-fg) group-has-[:checked]:hover:bg-(--card-ring)/15
95
+ ].freeze,
96
+ card: %w[
97
+ flex items-start flex-1 p-(--sp-space-4) cursor-pointer select-none
98
+ text-(length:--sp-text-sm) text-(--sp-color-muted-fg)
99
+ bg-(--sp-color-bg) border border-(--sp-color-border) rounded-(--sp-radius-md) shadow-(--sp-shadow-xs)
100
+ hover:bg-(--sp-color-muted) hover:border-(--sp-color-border-strong) hover:text-(--sp-color-fg)
101
+ group-has-[:checked]:border-(--card-ring) group-has-[:checked]:bg-(--card-ring)/10
102
+ group-has-[:checked]:text-(--sp-color-fg) group-has-[:checked]:hover:bg-(--card-ring)/15
103
+ ].freeze
104
+ }.freeze
105
+
106
+ CHOICE_ITEMS_LAYOUT = {
107
+ stacked: %w[flex flex-col gap-(--sp-space-1)].freeze,
108
+ inline: %w[flex flex-row flex-wrap gap-x-(--sp-space-4) gap-y-(--sp-space-1)].freeze
109
+ }.freeze
110
+
111
+ private
112
+
113
+ def form_field_input_group_classes(floating: nil)
114
+ { classes: klasses(*FLOATING_GROUP_TYPES.fetch(floating, [])) }
115
+ end
116
+
117
+ def form_field_label_classes(floating: nil, hidden: false, error: false, **)
118
+ if floating
119
+ color = error ? FLOATING_LABEL_ERROR : FLOATING_LABEL_FOCUS
120
+ { classes: klasses(*FLOATING_LABEL_BASE, *FLOATING_LABEL_TYPES.fetch(floating, []), *color) }
121
+ else
122
+ { classes: klasses(*LABEL, hidden ? "sr-only" : nil) }
123
+ end
124
+ end
125
+
126
+ def form_field_required_mark_classes
127
+ { classes: klasses(*REQUIRED_MARK) }
128
+ end
129
+
130
+ def form_field_hint_classes
131
+ { classes: klasses(*HINT) }
132
+ end
133
+
134
+ def form_field_error_classes
135
+ { classes: klasses(*ERROR_TEXT) }
136
+ end
137
+
138
+ def form_field_choice_items_classes(layout: :stacked)
139
+ { classes: klasses(*CHOICE_ITEMS_LAYOUT.fetch(layout)) }
140
+ end
141
+
142
+ def form_field_checkbox_label_classes(type: :default, variant: :default)
143
+ card_color = type == :card ? Card::VARIANTS.fetch(variant, Card::VARIANTS[:tertiary]) : []
144
+ { classes: klasses(*CHECKBOX_LABEL_TYPES.fetch(type), *card_color) }
145
+ end
146
+
147
+ def form_field_radio_label_classes(type: :default, variant: :default)
148
+ card_color = %i[button card].include?(type) ? Card::VARIANTS.fetch(variant, Card::VARIANTS[:tertiary]) : []
149
+ { classes: klasses(*RADIO_LABEL_TYPES.fetch(type), *card_color) }
150
+ end
151
+
152
+ def form_field_radio_item_group_classes
153
+ { classes: "contents group" }
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,240 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Tailwind
6
+ module Form
7
+ module Input
8
+ # ── Standalone input ──────────────────────────────────────────────────
9
+ INPUT_BASE = %w[
10
+ w-full rounded-(--sp-radius-md) border px-(--sp-space-3) py-(--sp-space-2)
11
+ text-(length:--sp-text-sm) text-(--sp-color-fg) bg-(--sp-color-bg)
12
+ focus:outline-none focus:ring-(length:--sp-focus-ring-width) focus:ring-offset-0
13
+ ].freeze
14
+ INPUT_DEFAULT = %w[
15
+ border-(--sp-color-muted-fg) hover:border-(--sp-color-fg)
16
+ focus:ring-(--sp-focus-ring-color)
17
+ ].freeze
18
+ INPUT_ERROR = %w[border-(--sp-color-error) focus:ring-(--sp-color-error)].freeze
19
+
20
+ # ── Floating input ────────────────────────────────────────────────────
21
+ FLOATING_INPUT_BASE = %w[
22
+ peer w-full text-(length:--sp-text-sm) text-(--sp-color-fg) appearance-none
23
+ focus:outline-none focus:ring-0
24
+ focus-visible:outline-none focus-visible:ring-0
25
+ ].freeze
26
+ FLOATING_INPUT_TYPES = {
27
+ filled: %w[
28
+ rounded-t-(--sp-radius-md) px-(--sp-space-2-5) pb-(--sp-space-2-5) pt-(--sp-space-5)
29
+ bg-(--sp-color-bg-muted) border-0 border-b-2
30
+ ].freeze,
31
+ outlined: %w[
32
+ px-(--sp-space-2-5) pb-(--sp-space-2-5) pt-(--sp-space-4)
33
+ bg-transparent rounded-(--sp-radius-md) border
34
+ ].freeze,
35
+ standard: %w[
36
+ py-(--sp-space-2-5) px-0
37
+ bg-transparent border-0 border-b-2
38
+ ].freeze
39
+ }.freeze
40
+ FLOATING_INPUT_DEFAULT = %w[
41
+ border-(--sp-color-muted-fg) hover:border-(--sp-color-fg)
42
+ focus:border-(--sp-color-primary)
43
+ ].freeze
44
+ FLOATING_INPUT_ERROR = %w[border-(--sp-color-error)].freeze
45
+
46
+ # ── Input group ───────────────────────────────────────────────────────
47
+ INPUT_GROUP_BASE = %w[flex items-center overflow-hidden rounded-(--sp-radius-md) border].freeze
48
+ INPUT_GROUP_BORDER = { error: "border-(--sp-color-error)", default: "border-(--sp-color-muted-fg)" }.freeze
49
+
50
+ # ── Floating input group ──────────────────────────────────────────────
51
+ FLOATING_INPUT_GROUP_BASE = %w[flex items-center overflow-hidden peer].freeze
52
+ FLOATING_INPUT_GROUP_TYPES = {
53
+ filled: %w[rounded-t-(--sp-radius-md) bg-(--sp-color-bg-muted) border-0 border-b-2].freeze,
54
+ outlined: %w[rounded-(--sp-radius-md) border].freeze,
55
+ standard: %w[rounded-none bg-transparent border-0 border-b-2].freeze
56
+ }.freeze
57
+ FLOATING_INPUT_GROUP_DEFAULT = %w[
58
+ border-(--sp-color-muted-fg) hover:border-(--sp-color-fg)
59
+ focus-within:border-(--sp-color-primary)
60
+ ].freeze
61
+ FLOATING_INPUT_GROUP_ERROR = %w[border-(--sp-color-error)].freeze
62
+
63
+ # ── Combobox wrappers ─────────────────────────────────────────────────
64
+ COMBOBOX_INPUT = %w[
65
+ [&>input:not([type=hidden])]:border-0
66
+ [&>input:not([type=hidden])]:rounded-none
67
+ [&>input:not([type=hidden])]:px-0
68
+ [&>input:not([type=hidden])]:py-0
69
+ [&>input:not([type=hidden])]:bg-transparent
70
+ [&>input:not([type=hidden])]:shadow-none
71
+ [&>input:not([type=hidden])]:focus:ring-0
72
+ ].freeze
73
+ COMBOBOX_TRIGGER_GROUP = %w[
74
+ [&>div:first-child]:border-0
75
+ [&>div:first-child]:rounded-none
76
+ [&>div:first-child]:px-0
77
+ [&>div:first-child]:py-0
78
+ [&>div:first-child]:focus-within:ring-0
79
+ ].freeze
80
+
81
+ # ── Choice inputs ─────────────────────────────────────────────────────
82
+ CHECKBOX_TYPES = {
83
+ default: %w[
84
+ size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0
85
+ border border-(--sp-color-border) bg-(--sp-color-muted)
86
+ focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none
87
+ disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer
88
+ ].freeze,
89
+ button: %w[
90
+ size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0
91
+ border border-(--sp-color-border) bg-(--sp-color-muted)
92
+ focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none
93
+ disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer
94
+ ].freeze,
95
+ card: %w[
96
+ size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0
97
+ border border-(--sp-color-border) bg-(--sp-color-muted)
98
+ checked:border-(--card-ring)
99
+ focus:ring-(length:--sp-focus-ring-width) focus:ring-(--card-ring) focus:outline-none
100
+ disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer
101
+ ].freeze
102
+ }.freeze
103
+
104
+ RADIO_TYPES = {
105
+ default: %w[
106
+ size-(--sp-control-size) rounded-full shrink-0
107
+ [accent-color:var(--sp-color-primary)] cursor-pointer
108
+ focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none
109
+ disabled:opacity-50 disabled:cursor-not-allowed
110
+ ].freeze,
111
+ button: %w[hidden].freeze,
112
+ card: %w[hidden].freeze
113
+ }.freeze
114
+
115
+ # ── Utility buttons ───────────────────────────────────────────────────
116
+ BUTTON_REVEAL = [
117
+ *Control::BASE,
118
+ "inline-flex items-center justify-center",
119
+ "focus-visible:ring-(--sp-focus-ring-color)",
120
+ "self-stretch px-(--sp-space-2) border-0 bg-transparent cursor-pointer text-(--sp-color-muted-fg)",
121
+ "rounded-(--sp-radius-sm) hover:bg-(--sp-color-muted) hover:text-(--sp-color-fg)"
122
+ ].freeze
123
+ BUTTON_CLEAR = [
124
+ *Control::BASE,
125
+ "inline-flex items-center justify-center",
126
+ "focus-visible:ring-(--sp-focus-ring-color)",
127
+ "self-stretch px-(--sp-space-2) border-0 bg-transparent cursor-pointer text-(--sp-color-muted-fg)",
128
+ "rounded-(--sp-radius-sm) hover:bg-(--sp-color-muted) hover:text-(--sp-color-fg)"
129
+ ].freeze
130
+
131
+ private
132
+
133
+ def form_field_input_classes(floating: nil, error: false)
134
+ if floating
135
+ { classes: klasses(
136
+ *FLOATING_INPUT_BASE,
137
+ *FLOATING_INPUT_TYPES.fetch(floating, []),
138
+ *(error ? FLOATING_INPUT_ERROR : FLOATING_INPUT_DEFAULT)
139
+ )
140
+ }
141
+ else
142
+ { classes: klasses(*INPUT_BASE, *(error ? INPUT_ERROR : INPUT_DEFAULT)) }
143
+ end
144
+ end
145
+
146
+ def form_field_input_textarea_classes(floating: nil, error: false)
147
+ form_field_input_classes(floating: floating, error: error)
148
+ end
149
+
150
+ def form_field_input_file_classes(floating: nil, error: false)
151
+ form_field_input_classes(floating: floating, error: error)
152
+ end
153
+
154
+ def form_field_input_select_classes(floating: nil, error: false)
155
+ form_field_input_classes(floating: floating, error: error)
156
+ end
157
+
158
+ def form_field_input_checkbox_classes(type: :default, variant: :default, **)
159
+ card_color = type == :card ? Card::VARIANTS.fetch(variant, Card::VARIANTS[:tertiary]) : []
160
+ { classes: klasses(*CHECKBOX_TYPES.fetch(type), *card_color) }
161
+ end
162
+
163
+ def form_field_input_radio_classes(type: :default, variant: :default, **)
164
+ card_color = %i[button card].include?(type) ? Card::VARIANTS.fetch(variant, Card::VARIANTS[:tertiary]) : []
165
+ { classes: klasses(*RADIO_TYPES.fetch(type), *card_color) }
166
+ end
167
+
168
+ def input_group_classes(error: false, floating: nil)
169
+ if floating
170
+ color = error ? FLOATING_INPUT_GROUP_ERROR : FLOATING_INPUT_GROUP_DEFAULT
171
+ { classes: klasses(*FLOATING_INPUT_GROUP_BASE, *FLOATING_INPUT_GROUP_TYPES.fetch(floating, []), *color) }
172
+ else
173
+ { classes: klasses(*INPUT_GROUP_BASE, INPUT_GROUP_BORDER[error ? :error : :default]) }
174
+ end
175
+ end
176
+
177
+ def form_field_input_combobox_classes(floating: nil, error: false)
178
+ if floating
179
+ form_field_input_combobox_floating_classes(floating: floating, error: error)
180
+ else
181
+ {
182
+ classes: klasses(
183
+ *INPUT_BASE,
184
+ *(error ? INPUT_ERROR : INPUT_DEFAULT),
185
+ *COMBOBOX_INPUT,
186
+ *COMBOBOX_TRIGGER_GROUP
187
+ )
188
+ }
189
+ end
190
+ end
191
+
192
+ def form_field_input_combobox_floating_classes(floating: :standard, error: false)
193
+ {
194
+ classes: klasses(
195
+ *FLOATING_INPUT_BASE,
196
+ *FLOATING_INPUT_TYPES.fetch(floating, []),
197
+ *(error ? FLOATING_INPUT_ERROR : FLOATING_INPUT_DEFAULT),
198
+ *COMBOBOX_INPUT,
199
+ *COMBOBOX_TRIGGER_GROUP
200
+ )
201
+ }
202
+ end
203
+
204
+ def form_field_input_reveal_classes(**)
205
+ {
206
+ classes: klasses(
207
+ "peer",
208
+ "[&>input]:border-0",
209
+ "[&>input]:rounded-none",
210
+ "[&>input]:bg-transparent",
211
+ "[&>input]:shadow-none",
212
+ "[&>input]:focus:ring-0"
213
+ )
214
+ }
215
+ end
216
+
217
+ def form_field_input_clearable_classes
218
+ {
219
+ classes: klasses(
220
+ "[&>input]:border-0",
221
+ "[&>input]:rounded-none",
222
+ "[&>input]:bg-transparent",
223
+ "[&>input]:shadow-none",
224
+ "[&>input]:focus:ring-0"
225
+ )
226
+ }
227
+ end
228
+
229
+ def form_field_input_button_reveal_classes
230
+ { classes: klasses(*BUTTON_REVEAL) }
231
+ end
232
+
233
+ def form_field_input_button_clear_classes
234
+ { classes: klasses(*BUTTON_CLEAR) }
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
@@ -4,38 +4,8 @@ module StimulusPlumbers
4
4
  module Themes
5
5
  module Tailwind
6
6
  module Form
7
- INPUT_BASE = %w[
8
- w-full rounded-(--sp-radius-md) border px-(--sp-space-3) py-(--sp-space-2) text-(length:--sp-text-sm)
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,78 +13,7 @@ module StimulusPlumbers
43
13
  { classes: klasses(*GROUP_BASE, layout == :inline ? GROUP_INLINE : "flex-col") }
44
14
  end
45
15
 
46
- def form_label_classes(hidden: false, **)
47
- { classes: klasses(*LABEL, hidden ? "sr-only" : nil) }
48
- end
49
-
50
- def form_required_mark_classes
51
- { classes: klasses(*REQUIRED_MARK) }
52
- end
53
-
54
- def form_details_classes
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
117
- end
16
+ def form_submit_classes(**) = {}
118
17
  end
119
18
  end
120
19
  end
@@ -9,8 +9,14 @@ module StimulusPlumbers
9
9
  module Tailwind
10
10
  module Icon
11
11
  ALIASES = {
12
- "close" => "x-mark",
13
- "calendar" => "calendar-days"
12
+ "close" => "x-mark",
13
+ "download" => "arrow-down-tray",
14
+ "book" => "book-open",
15
+ "edit" => "pencil",
16
+ "email" => "envelope",
17
+ "calendar" => "calendar-days",
18
+ "external-link" => "arrow-top-right-on-square",
19
+ "reveal" => "eye"
14
20
  }.freeze
15
21
 
16
22
  ICONS = StimulusPlumbers::Themes::Icons::Registry.new(
@@ -25,7 +31,7 @@ module StimulusPlumbers
25
31
  private
26
32
 
27
33
  def icon_classes
28
- { classes: "size-(--sp-icon-default)" }
34
+ { classes: "size-(--sp-icon-size-lg)" }
29
35
  end
30
36
  end
31
37
  end