stimulus_plumbers 0.2.9 → 0.3.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/README.md +5 -4
  4. data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.es.js +246 -269
  5. data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.umd.js +1 -1
  6. data/lib/stimulus_plumbers/components/avatar.rb +4 -3
  7. data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_month.rb +58 -39
  8. data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_week.rb +13 -10
  9. data/lib/stimulus_plumbers/components/calendar.rb +36 -13
  10. data/lib/stimulus_plumbers/components/combobox/trigger.rb +6 -6
  11. data/lib/stimulus_plumbers/components/date_picker/navigation.rb +14 -22
  12. data/lib/stimulus_plumbers/components/icon.rb +14 -20
  13. data/lib/stimulus_plumbers/components/popover.rb +11 -6
  14. data/lib/stimulus_plumbers/configuration.rb +3 -18
  15. data/lib/stimulus_plumbers/engine.rb +2 -2
  16. data/lib/stimulus_plumbers/form/builder.rb +1 -1
  17. data/lib/stimulus_plumbers/form/fields/combobox.rb +7 -4
  18. data/lib/stimulus_plumbers/form/fields/renderer.rb +5 -2
  19. data/lib/stimulus_plumbers/plumber/base.rb +1 -1
  20. data/lib/stimulus_plumbers/plumber/dispatcher/callable_inspector.rb +19 -0
  21. data/lib/stimulus_plumbers/plumber/dispatcher/instance_exec.rb +35 -0
  22. data/lib/stimulus_plumbers/plumber/dispatcher/klass_proxy.rb +34 -0
  23. data/lib/stimulus_plumbers/plumber/dispatcher/method_call.rb +36 -0
  24. data/lib/stimulus_plumbers/plumber/dispatcher.rb +4 -87
  25. data/lib/stimulus_plumbers/plumber/html_options.rb +6 -5
  26. data/lib/stimulus_plumbers/plumber/renderer.rb +2 -2
  27. data/lib/stimulus_plumbers/themes/base.rb +25 -5
  28. data/lib/stimulus_plumbers/themes/configuration.rb +38 -0
  29. data/lib/stimulus_plumbers/themes/schema/form/ranges.rb +14 -0
  30. data/lib/stimulus_plumbers/themes/schema/icon.rb +32 -0
  31. data/lib/stimulus_plumbers/themes/schema/ranges.rb +1 -1
  32. data/lib/stimulus_plumbers/themes/schema.rb +10 -4
  33. data/lib/stimulus_plumbers/version.rb +1 -1
  34. metadata +9 -11
  35. data/lib/stimulus_plumbers/themes/tailwind/action_list.rb +0 -33
  36. data/lib/stimulus_plumbers/themes/tailwind/avatar.rb +0 -52
  37. data/lib/stimulus_plumbers/themes/tailwind/button.rb +0 -89
  38. data/lib/stimulus_plumbers/themes/tailwind/calendar.rb +0 -80
  39. data/lib/stimulus_plumbers/themes/tailwind/card.rb +0 -24
  40. data/lib/stimulus_plumbers/themes/tailwind/combobox.rb +0 -75
  41. data/lib/stimulus_plumbers/themes/tailwind/form.rb +0 -108
  42. data/lib/stimulus_plumbers/themes/tailwind/layout.rb +0 -25
  43. data/lib/stimulus_plumbers/themes/tailwind_theme.rb +0 -31
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbers
4
- module Themes
5
- module Tailwind
6
- module ActionList
7
- LIST_ITEM_ACTIVE = %w[bg-[--sp-color-primary]/10 text-[--sp-color-primary]].freeze
8
- LIST_ITEM_BASE = %w[
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-[--sp-text-sm]
12
- cursor-pointer select-none outline-none
13
- hover:bg-[--sp-color-muted] focus:bg-[--sp-color-muted] focus:text-[--sp-color-fg]
14
- ].freeze
15
-
16
- private
17
-
18
- def action_list_classes
19
- { classes: klasses("py-[--sp-space-1]") }
20
- end
21
-
22
- def action_list_item_classes(active: false)
23
- {
24
- classes: klasses(
25
- *LIST_ITEM_BASE,
26
- *(active ? LIST_ITEM_ACTIVE : [])
27
- )
28
- }
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbers
4
- module Themes
5
- module Tailwind
6
- module Avatar
7
- COLORS = {
8
- amber: "text-white bg-amber-600",
9
- lime: "text-white bg-lime-600",
10
- sky: "text-white bg-sky-600",
11
- cyan: "text-white bg-cyan-600",
12
- teal: "text-white bg-teal-600",
13
- emerald: "text-white bg-emerald-600",
14
- indigo: "text-white bg-indigo-600",
15
- fuchsia: "text-white bg-fuchsia-600",
16
- rose: "text-white bg-rose-600",
17
- pink: "text-white bg-pink-600",
18
- violet: "text-white bg-violet-600",
19
- blue: "text-white bg-blue-600"
20
- }.freeze
21
-
22
- SIZES = {
23
- sm: "size-[--sp-icon-size]",
24
- md: "size-[--sp-avatar-size]",
25
- lg: "size-12"
26
- }.freeze
27
-
28
- BASE = %w[rounded-[--sp-radius-full] overflow-hidden inline-flex items-center justify-center].freeze
29
-
30
- def avatar_colors
31
- COLORS
32
- end
33
-
34
- def avatar_color_range
35
- COLORS.values
36
- end
37
-
38
- private
39
-
40
- def avatar_classes(size: :md, color: nil)
41
- {
42
- classes: klasses(
43
- SIZES.fetch(size, SIZES[:md]),
44
- COLORS.fetch(color, nil),
45
- *BASE
46
- )
47
- }
48
- end
49
- end
50
- end
51
- end
52
- end
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbers
4
- module Themes
5
- module Tailwind
6
- module Button
7
- VARIANTS = {
8
- primary: %w[
9
- bg-[--sp-color-primary]
10
- text-[--sp-color-primary-fg]
11
- hover:bg-[--sp-color-primary]/90
12
- focus-visible:ring-[--sp-focus-ring-color]
13
- ].freeze,
14
- secondary: %w[
15
- bg-[--sp-color-muted]
16
- text-[--sp-color-fg]
17
- hover:bg-[--sp-color-border]
18
- ].freeze,
19
- outline: %w[
20
- border border-[--sp-color-border]
21
- bg-transparent
22
- text-[--sp-color-fg]
23
- hover:bg-[--sp-color-muted]
24
- ].freeze,
25
- destructive: %w[
26
- bg-[--sp-color-destructive]
27
- text-[--sp-color-destructive-fg]
28
- hover:bg-[--sp-color-destructive]/90
29
- ].freeze,
30
- ghost: %w[hover:bg-[--sp-color-muted] text-[--sp-color-fg]].freeze,
31
- link: %w[text-[--sp-color-primary] underline-offset-4 hover:underline].freeze
32
- }.freeze
33
-
34
- SIZES = {
35
- sm: %w[h-8 px-[--sp-space-3] text-[--sp-text-sm]].freeze,
36
- md: %w[h-9 px-[--sp-space-4] text-[--sp-text-base]].freeze,
37
- lg: %w[h-11 px-[--sp-space-6] text-[--sp-text-lg]].freeze
38
- }.freeze
39
-
40
- FLEX_ALIGN = {
41
- row: {
42
- left: "justify-start",
43
- center: %w[justify-center items-center].freeze,
44
- right: "justify-end",
45
- top: "items-start",
46
- bottom: "items-end"
47
- }.freeze,
48
- col: {
49
- top: "justify-start",
50
- center: %w[justify-center items-center].freeze,
51
- bottom: "justify-end",
52
- left: "items-start",
53
- right: "items-end"
54
- }.freeze
55
- }.freeze
56
-
57
- BASE = %w[
58
- inline-flex items-center justify-center gap-2 font-medium
59
- rounded-[--sp-radius-md] transition-colors
60
- focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2
61
- disabled:pointer-events-none disabled:opacity-50
62
- ].freeze
63
-
64
- GROUP_BASE = %w[flex gap-[--sp-space-2]].freeze
65
-
66
- private
67
-
68
- def button_classes(variant: :primary, size: :md)
69
- {
70
- classes: klasses(
71
- *BASE,
72
- *VARIANTS.fetch(variant, []),
73
- *SIZES.fetch(size, [])
74
- )
75
- }
76
- end
77
-
78
- def button_group_classes(alignment: :left, direction: :row)
79
- {
80
- classes: klasses(
81
- *GROUP_BASE,
82
- *Array(FLEX_ALIGN.dig(direction, alignment))
83
- )
84
- }
85
- end
86
- end
87
- end
88
- end
89
- end
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbers
4
- module Themes
5
- module Tailwind
6
- module Calendar
7
- GRID = %w[w-full].freeze
8
-
9
- DAYS_OF_WEEK = %w[
10
- grid grid-cols-7 text-center text-[--sp-text-xs]
11
- font-medium text-[--sp-color-muted-fg] mb-1
12
- ].freeze
13
-
14
- DAYS_OF_MONTH = %w[grid grid-cols-7].freeze
15
-
16
- DAY = %w[
17
- size-[--sp-calendar-day-size] rounded-[--sp-radius-md]
18
- flex items-center justify-center text-[--sp-text-sm]
19
- hover:bg-[--sp-color-muted] cursor-pointer
20
- ].freeze
21
-
22
- DAY_SELECTED = %w[
23
- bg-[--sp-color-primary]
24
- text-[--sp-color-primary-fg]
25
- hover:bg-[--sp-color-primary]/90
26
- ].freeze
27
-
28
- NAV = %w[flex items-center justify-between gap-1 mb-2].freeze
29
-
30
- NAV_BTN = %w[
31
- inline-flex items-center justify-center
32
- size-[--sp-calendar-day-size] rounded-[--sp-radius-md]
33
- text-[--sp-color-fg] hover:bg-[--sp-color-muted]
34
- focus-visible:outline-none focus-visible:ring-2
35
- focus-visible:ring-[--sp-focus-ring-color]
36
- disabled:pointer-events-none disabled:opacity-50
37
- ].freeze
38
-
39
- NAV_ICON = %w[size-4 stroke-current].freeze
40
-
41
- private
42
-
43
- def calendar_classes
44
- { classes: klasses(*GRID) }
45
- end
46
-
47
- def calendar_days_of_week_classes
48
- { classes: klasses(*DAYS_OF_WEEK) }
49
- end
50
-
51
- def calendar_days_of_month_classes
52
- { classes: klasses(*DAYS_OF_MONTH) }
53
- end
54
-
55
- def calendar_day_classes(today: false, selected: false, outside: false)
56
- {
57
- classes: klasses(
58
- *DAY,
59
- *(today ? ["font-bold"] : []),
60
- *(selected ? DAY_SELECTED : []),
61
- *(outside ? %w[text-[--sp-color-muted-fg] opacity-50] : [])
62
- )
63
- }
64
- end
65
-
66
- def calendar_navigation_classes
67
- { classes: klasses(*NAV) }
68
- end
69
-
70
- def calendar_navigation_navigator_classes
71
- { classes: klasses(*NAV_BTN) }
72
- end
73
-
74
- def calendar_navigation_navigator_icon_classes
75
- { classes: klasses(*NAV_ICON) }
76
- end
77
- end
78
- end
79
- end
80
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbers
4
- module Themes
5
- module Tailwind
6
- module Card
7
- BASE = %w[
8
- rounded-[--sp-radius-lg] border border-[--sp-color-border]
9
- bg-[--sp-color-bg] shadow-[--sp-shadow-sm]
10
- ].freeze
11
-
12
- private
13
-
14
- def card_classes
15
- { classes: klasses(*BASE) }
16
- end
17
-
18
- def card_section_classes
19
- { classes: klasses("p-[--sp-space-6]") }
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbers
4
- module Themes
5
- module Tailwind
6
- module Combobox
7
- LISTBOX = %w[
8
- py-[--sp-space-1] overflow-y-auto max-h-60
9
- ].freeze
10
-
11
- OPTION_BASE = %w[
12
- flex items-center gap-[--sp-space-2] w-full
13
- px-[--sp-space-2] py-[--sp-space-1]
14
- rounded-[--sp-radius-sm] text-[--sp-text-sm]
15
- cursor-pointer select-none outline-none
16
- hover:bg-[--sp-color-muted] focus:bg-[--sp-color-muted]
17
- ].freeze
18
-
19
- OPTION_SELECTED = %w[
20
- bg-[--sp-color-primary]/10 text-[--sp-color-primary]
21
- ].freeze
22
-
23
- OPTION_DISABLED = %w[
24
- opacity-50 cursor-not-allowed pointer-events-none
25
- ].freeze
26
-
27
- OPTION_GROUP = %w[py-[--sp-space-1]].freeze
28
-
29
- AUTOCOMPLETE_LOADING = %w[
30
- flex items-center justify-center
31
- py-[--sp-space-2] text-[--sp-text-sm] text-[--sp-color-muted-fg]
32
- ].freeze
33
-
34
- AUTOCOMPLETE_EMPTY = %w[
35
- flex items-center justify-center
36
- py-[--sp-space-2] text-[--sp-text-sm] text-[--sp-color-muted-fg]
37
- ].freeze
38
-
39
- TIME = %w[flex gap-[--sp-space-2] overflow-hidden].freeze
40
-
41
- private
42
-
43
- def combobox_listbox_classes
44
- { classes: klasses(*LISTBOX) }
45
- end
46
-
47
- def combobox_option_classes(selected: false, disabled: false)
48
- {
49
- classes: klasses(
50
- *OPTION_BASE,
51
- *(selected ? OPTION_SELECTED : []),
52
- *(disabled ? OPTION_DISABLED : [])
53
- )
54
- }
55
- end
56
-
57
- def combobox_option_group_classes
58
- { classes: klasses(*OPTION_GROUP) }
59
- end
60
-
61
- def combobox_autocomplete_loading_classes
62
- { classes: klasses(*AUTOCOMPLETE_LOADING) }
63
- end
64
-
65
- def combobox_autocomplete_empty_classes
66
- { classes: klasses(*AUTOCOMPLETE_EMPTY) }
67
- end
68
-
69
- def combobox_time_classes
70
- { classes: klasses(*TIME) }
71
- end
72
- end
73
- end
74
- end
75
- end
@@ -1,108 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbers
4
- module Themes
5
- module Tailwind
6
- module Form
7
- INPUT_BASE = %w[
8
- w-full rounded-md border px-3 py-2 text-sm text-gray-900
9
- bg-white focus:outline-none focus:ring-2 focus:ring-offset-0
10
- ].freeze
11
- INPUT_ERROR = %w[border-red-700 focus:ring-red-700].freeze
12
- INPUT_DEFAULT = %w[border-gray-500 focus:ring-blue-700].freeze
13
-
14
- GROUP_BASE = %w[flex gap-1 mb-3].freeze
15
- GROUP_INLINE = %w[flex-row items-center].freeze
16
-
17
- LABEL = %w[text-sm font-medium text-gray-900].freeze
18
- REQUIRED_MARK = %w[text-red-700 ml-0.5].freeze
19
- DETAILS = %w[text-xs text-gray-600].freeze
20
- ERROR_TEXT = %w[text-xs text-red-700].freeze
21
- CHECKBOX = %w[size-4 rounded border-gray-500 text-blue-700].freeze
22
- RADIO = %w[size-4 border-gray-500 text-blue-700].freeze
23
-
24
- INPUT_GROUP_BASE = %w[flex items-center overflow-hidden rounded-md border].freeze
25
- INPUT_GROUP_BORDER = { error: "border-red-700", default: "border-gray-500" }.freeze
26
-
27
- INPUT_REVEAL = %w[
28
- flex-1 border-0 bg-transparent px-3 py-2 text-sm text-gray-900 focus:outline-none
29
- ].freeze
30
- BUTTON_REVEAL = %w[
31
- self-stretch border-0 bg-transparent px-3 cursor-pointer text-gray-600 hover:text-gray-900 text-sm
32
- ].freeze
33
- SUBMIT_LINK = %w[cursor-pointer text-sm font-medium text-gray-900 hover:underline].freeze
34
-
35
- private
36
-
37
- def form_group_classes(layout: :stacked, **_rest)
38
- { classes: klasses(*GROUP_BASE, layout == :inline ? GROUP_INLINE : "flex-col") }
39
- end
40
-
41
- def form_label_classes(hidden: false, **)
42
- { classes: klasses(*LABEL, hidden ? "sr-only" : nil) }
43
- end
44
-
45
- def form_required_mark_classes
46
- { classes: klasses(*REQUIRED_MARK) }
47
- end
48
-
49
- def form_details_classes
50
- { classes: klasses(*DETAILS) }
51
- end
52
-
53
- def form_error_classes
54
- { classes: klasses(*ERROR_TEXT) }
55
- end
56
-
57
- def form_input_classes(error: false)
58
- { classes: klasses(*INPUT_BASE, *(error ? INPUT_ERROR : INPUT_DEFAULT)) }
59
- end
60
-
61
- def form_textarea_classes(error: false)
62
- form_input_classes(error: error)
63
- end
64
-
65
- def form_file_classes(error: false)
66
- form_input_classes(error: error)
67
- end
68
-
69
- def form_select_classes(error: false)
70
- form_input_classes(error: error)
71
- end
72
-
73
- def form_checkbox_classes(**)
74
- { classes: klasses(*CHECKBOX) }
75
- end
76
-
77
- def form_radio_classes(**)
78
- { classes: klasses(*RADIO) }
79
- end
80
-
81
- def form_input_group_classes(error: false)
82
- { classes: klasses(*INPUT_GROUP_BASE, INPUT_GROUP_BORDER[error ? :error : :default]) }
83
- end
84
-
85
- def form_combobox_classes(error: false)
86
- form_input_classes(error: error)
87
- end
88
-
89
- def form_input_reveal_classes
90
- { classes: klasses(*INPUT_REVEAL) }
91
- end
92
-
93
- def form_button_reveal_classes
94
- { classes: klasses(*BUTTON_REVEAL) }
95
- end
96
-
97
- def form_submit_classes(variant: :default)
98
- case variant
99
- when :button
100
- { classes: klasses(*Button::BASE, *Button::VARIANTS[:primary], *Button::SIZES[:md]) }
101
- else
102
- { classes: klasses(*SUBMIT_LINK) }
103
- end
104
- end
105
- end
106
- end
107
- end
108
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbers
4
- module Themes
5
- module Tailwind
6
- module Layout
7
- DIVIDER = %w[border-t border-[--sp-color-border] my-[--sp-space-1]].freeze
8
- POPOVER = %w[
9
- rounded-[--sp-radius-lg] border border-[--sp-color-border]
10
- bg-[--sp-color-bg] shadow-[--sp-shadow-md] z-[--sp-z-popover]
11
- ].freeze
12
-
13
- private
14
-
15
- def divider_classes
16
- { classes: klasses(*DIVIDER) }
17
- end
18
-
19
- def popover_classes
20
- { classes: klasses(*POPOVER) }
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "tailwind/action_list"
4
- require_relative "tailwind/combobox"
5
- require_relative "tailwind/avatar"
6
- require_relative "tailwind/button"
7
- require_relative "tailwind/calendar"
8
- require_relative "tailwind/card"
9
- require_relative "tailwind/form"
10
- require_relative "tailwind/layout"
11
-
12
- module StimulusPlumbers
13
- module Themes
14
- class TailwindTheme < Base
15
- include Tailwind::ActionList
16
- include Tailwind::Combobox
17
- include Tailwind::Avatar
18
- include Tailwind::Button
19
- include Tailwind::Calendar
20
- include Tailwind::Card
21
- include Tailwind::Form
22
- include Tailwind::Layout
23
-
24
- private
25
-
26
- def klasses(*classes)
27
- classes.flatten.reject(&:blank?).join(" ")
28
- end
29
- end
30
- end
31
- end