shadcn_phlexcomponents 0.1.11 → 0.1.16

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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/app/javascript/controllers/accordion_controller.js +107 -0
  3. data/app/javascript/controllers/alert_dialog_controller.js +7 -0
  4. data/app/javascript/controllers/avatar_controller.js +14 -0
  5. data/app/javascript/controllers/checkbox_controller.js +29 -0
  6. data/app/javascript/controllers/collapsible_controller.js +39 -0
  7. data/app/javascript/controllers/combobox_controller.js +278 -0
  8. data/app/javascript/controllers/command_controller.js +207 -0
  9. data/app/javascript/controllers/date_picker_controller.js +258 -0
  10. data/app/javascript/controllers/date_range_picker_controller.js +200 -0
  11. data/app/javascript/controllers/dialog_controller.js +83 -0
  12. data/app/javascript/controllers/dropdown_menu_controller.js +238 -0
  13. data/app/javascript/controllers/dropdown_menu_sub_controller.js +118 -0
  14. data/app/javascript/controllers/form_field_controller.js +20 -0
  15. data/app/javascript/controllers/hover_card_controller.js +73 -0
  16. data/app/javascript/controllers/loading_button_controller.js +14 -0
  17. data/app/javascript/controllers/popover_controller.js +90 -0
  18. data/app/javascript/controllers/progress_controller.js +14 -0
  19. data/app/javascript/controllers/radio_group_controller.js +80 -0
  20. data/app/javascript/controllers/select_controller.js +265 -0
  21. data/app/javascript/controllers/sidebar_controller.js +29 -0
  22. data/app/javascript/controllers/sidebar_trigger_controller.js +15 -0
  23. data/app/javascript/controllers/slider_controller.js +82 -0
  24. data/app/javascript/controllers/switch_controller.js +26 -0
  25. data/app/javascript/controllers/tabs_controller.js +66 -0
  26. data/app/javascript/controllers/theme_switcher_controller.js +32 -0
  27. data/app/javascript/controllers/toast_container_controller.js +48 -0
  28. data/app/javascript/controllers/toast_controller.js +22 -0
  29. data/app/javascript/controllers/toggle_controller.js +20 -0
  30. data/app/javascript/controllers/toggle_group_controller.js +20 -0
  31. data/app/javascript/controllers/tooltip_controller.js +79 -0
  32. data/app/javascript/shadcn_phlexcomponents.js +60 -0
  33. data/app/javascript/utils/command.js +448 -0
  34. data/app/javascript/utils/floating_ui.js +160 -0
  35. data/app/javascript/utils/index.js +288 -0
  36. data/app/stylesheets/date_picker.css +118 -0
  37. data/app/typescript/controllers/accordion_controller.ts +136 -0
  38. data/app/typescript/controllers/alert_dialog_controller.ts +12 -0
  39. data/app/{javascript → typescript}/controllers/avatar_controller.ts +7 -2
  40. data/app/{javascript → typescript}/controllers/checkbox_controller.ts +11 -4
  41. data/app/{javascript → typescript}/controllers/collapsible_controller.ts +12 -5
  42. data/app/typescript/controllers/combobox_controller.ts +376 -0
  43. data/app/typescript/controllers/command_controller.ts +301 -0
  44. data/app/{javascript → typescript}/controllers/date_picker_controller.ts +185 -125
  45. data/app/{javascript → typescript}/controllers/date_range_picker_controller.ts +89 -79
  46. data/app/{javascript → typescript}/controllers/dialog_controller.ts +59 -57
  47. data/app/typescript/controllers/dropdown_menu_controller.ts +309 -0
  48. data/app/{javascript → typescript}/controllers/dropdown_menu_sub_controller.ts +31 -29
  49. data/app/{javascript → typescript}/controllers/form_field_controller.ts +6 -1
  50. data/app/{javascript → typescript}/controllers/hover_card_controller.ts +36 -26
  51. data/app/{javascript → typescript}/controllers/loading_button_controller.ts +6 -1
  52. data/app/{javascript → typescript}/controllers/popover_controller.ts +42 -65
  53. data/app/{javascript → typescript}/controllers/progress_controller.ts +9 -3
  54. data/app/{javascript → typescript}/controllers/radio_group_controller.ts +16 -9
  55. data/app/typescript/controllers/select_controller.ts +341 -0
  56. data/app/{javascript → typescript}/controllers/slider_controller.ts +23 -16
  57. data/app/{javascript → typescript}/controllers/switch_controller.ts +11 -4
  58. data/app/{javascript → typescript}/controllers/tabs_controller.ts +26 -18
  59. data/app/{javascript → typescript}/controllers/theme_switcher_controller.ts +6 -1
  60. data/app/{javascript → typescript}/controllers/toast_container_controller.ts +6 -1
  61. data/app/{javascript → typescript}/controllers/toast_controller.ts +7 -1
  62. data/app/typescript/controllers/toggle_controller.ts +28 -0
  63. data/app/typescript/controllers/toggle_group_controller.ts +28 -0
  64. data/app/{javascript → typescript}/controllers/tooltip_controller.ts +43 -31
  65. data/app/typescript/shadcn_phlexcomponents.ts +61 -0
  66. data/app/typescript/utils/command.ts +544 -0
  67. data/app/typescript/utils/floating_ui.ts +196 -0
  68. data/app/typescript/utils/index.ts +424 -0
  69. data/lib/install/install_shadcn_phlexcomponents.rb +10 -3
  70. data/lib/shadcn_phlexcomponents/alias.rb +3 -0
  71. data/lib/shadcn_phlexcomponents/components/accordion.rb +2 -1
  72. data/lib/shadcn_phlexcomponents/components/alert_dialog.rb +18 -15
  73. data/lib/shadcn_phlexcomponents/components/base.rb +14 -0
  74. data/lib/shadcn_phlexcomponents/components/collapsible.rb +1 -2
  75. data/lib/shadcn_phlexcomponents/components/combobox.rb +87 -57
  76. data/lib/shadcn_phlexcomponents/components/command.rb +77 -47
  77. data/lib/shadcn_phlexcomponents/components/date_picker.rb +25 -81
  78. data/lib/shadcn_phlexcomponents/components/date_range_picker.rb +21 -4
  79. data/lib/shadcn_phlexcomponents/components/dialog.rb +14 -12
  80. data/lib/shadcn_phlexcomponents/components/dropdown_menu.rb +5 -4
  81. data/lib/shadcn_phlexcomponents/components/dropdown_menu_sub.rb +2 -1
  82. data/lib/shadcn_phlexcomponents/components/form/form_combobox.rb +64 -0
  83. data/lib/shadcn_phlexcomponents/components/form.rb +14 -0
  84. data/lib/shadcn_phlexcomponents/components/hover_card.rb +3 -2
  85. data/lib/shadcn_phlexcomponents/components/popover.rb +3 -3
  86. data/lib/shadcn_phlexcomponents/components/select.rb +10 -25
  87. data/lib/shadcn_phlexcomponents/components/sheet.rb +15 -11
  88. data/lib/shadcn_phlexcomponents/components/table.rb +1 -1
  89. data/lib/shadcn_phlexcomponents/components/tabs.rb +1 -1
  90. data/lib/shadcn_phlexcomponents/components/toast_container.rb +1 -1
  91. data/lib/shadcn_phlexcomponents/components/toggle.rb +54 -0
  92. data/lib/shadcn_phlexcomponents/components/tooltip.rb +3 -2
  93. data/lib/shadcn_phlexcomponents/engine.rb +1 -5
  94. data/lib/shadcn_phlexcomponents/version.rb +1 -1
  95. metadata +71 -32
  96. data/app/javascript/controllers/accordion_controller.ts +0 -133
  97. data/app/javascript/controllers/combobox_controller.ts +0 -145
  98. data/app/javascript/controllers/command_controller.ts +0 -129
  99. data/app/javascript/controllers/command_root_controller.ts +0 -355
  100. data/app/javascript/controllers/dropdown_menu_controller.ts +0 -133
  101. data/app/javascript/controllers/dropdown_menu_root_controller.ts +0 -234
  102. data/app/javascript/controllers/select_controller.ts +0 -200
  103. data/app/javascript/shadcn_phlexcomponents.ts +0 -57
  104. data/app/javascript/utils.ts +0 -437
  105. /data/app/{javascript → typescript}/controllers/sidebar_controller.ts +0 -0
  106. /data/app/{javascript → typescript}/controllers/sidebar_trigger_controller.ts +0 -0
@@ -4,74 +4,6 @@ module ShadcnPhlexcomponents
4
4
  class DatePicker < Base
5
5
  class_variants(base: "w-full")
6
6
 
7
- class << self
8
- button = Button.new
9
-
10
- {
11
- input_container: <<~HEREDOC,
12
- focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]
13
- data-[focus=true]:border-ring data-[focus=true]:ring-ring/50 data-[focus=true]:ring-[3px]
14
- data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50 flex shadow-xs transition-[color,box-shadow]
15
- rounded-md border bg-transparent dark:bg-input/30 border-input outline-none h-9 flex items-center
16
- aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive
17
- HEREDOC
18
- input: <<~HEREDOC,
19
- md:text-sm placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground
20
- flex h-9 w-full min-w-0 text-base outline-none px-3 py-1#{" "}
21
- HEREDOC
22
- calendar: {
23
- calendar: "relative flex flex-col outline-none",
24
- controls: "absolute z-20 -left-1 -right-1 -top-1.5 flex justify-between items-center pt-1.5 px-1 pointer-events-none box-content",
25
- grid: "grid gap-4 grid-cols-1 md:grid-cols-2",
26
- column: "flex flex-col",
27
- header: "relative flex items-center mb-4",
28
- headerContent: "grid grid-flow-col gap-x-1 auto-cols-max items-center justify-center px-4 whitespace-pre-wrap grow",
29
- month: button.class_variants(variant: :outline, size: :sm, class: "text-xs h-7 bg-transparent"),
30
- year: button.class_variants(variant: :outline, size: :sm, class: "text-xs h-7 bg-transparent"),
31
- arrowPrev: button.class_variants(variant: :outline, size: :icon, class: "pointer-events-auto size-7 bg-transparent p-0 opacity-50 hover:opacity-100"),
32
- arrowNext: button.class_variants(variant: :outline, size: :icon, class: "pointer-events-auto size-7 bg-transparent p-0 opacity-50 hover:opacity-100"),
33
- wrapper: "flex items-center content-center h-full",
34
- content: "flex flex-col grow h-full",
35
- months: "grid gap-2 grid-cols-4 items-center grow",
36
- monthsMonth: button.class_variants(variant: :ghost, class: "aria-[selected=true]:text-primary-foreground aria-[selected=true]:bg-primary aria-[selected=true]:hover:text-primary-foreground aria-[selected=true]:hover:bg-primary"),
37
- years: "grid gap-2 grid-cols-5 items-center grow",
38
- yearsYear: button.class_variants(variant: :ghost, class: "aria-[selected=true]:text-primary-foreground aria-[selected=true]:bg-primary aria-[selected=true]:hover:text-primary-foreground aria-[selected=true]:hover:bg-primary"),
39
- week: "grid mb-2 grid-cols-[repeat(7,_1fr)] justify-items-center items-center text-center",
40
- weekDay: "text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
41
- weekNumbers: "vc-week-numbers",
42
- weekNumbersTitle: "vc-week-numbers__title",
43
- weekNumbersContent: "vc-week-numbers__content",
44
- weekNumber: "vc-week-number",
45
- dates: "grid gap-y-2 grid-cols-[repeat(7,_1fr)] justify-items-center items-center",
46
- date: <<~HEREDOC,
47
- vc-date data-[vc-date-selected]:[&_button]:bg-primary#{" "}
48
- data-[vc-date-selected]:[&_button]:text-primary-foreground#{" "}
49
- data-[vc-date-selected]:[&_button]:hover:bg-primary#{" "}
50
- data-[vc-date-selected]:[&_button]:hover:text-primary-foreground#{" "}
51
- data-[vc-date-selected]:[&_button]:focus:bg-primary#{" "}
52
- data-[vc-date-selected]:[&_button]:focus:text-primary-foreground
53
- data-[vc-date-today]:[&_button]:bg-accent
54
- data-[vc-date-today]:[&_button]:text-accent-foreground
55
- data-[vc-date-month=prev]:[&_button]:text-muted-foreground
56
- data-[vc-date-month=next]:[&_button]:text-muted-foreground
57
- data-[vc-date-selected='middle']:data-[vc-date-selected]:[&_button]:bg-accent
58
- data-[vc-date-selected='middle']:data-[vc-date-selected]:[&_button]:text-accent-foreground
59
- data-[vc-date-hover]:[&_button]:bg-accent data-[vc-date-hover]:[&_button]:text-accent-foreground
60
- HEREDOC
61
- dateBtn: button.class_variants(variant: :ghost, class: "size-8 p-0 font-normal aria-[disabled]:text-muted-foreground aria-[disabled]:opacity-50 aria-[disabled]:pointer-events-none"),
62
- datePopup: "vc-date__popup",
63
- dateRangeTooltip: "vc-date-range-tooltip",
64
- time: "vc-time",
65
- timeContent: "vc-time__content",
66
- timeHour: "vc-time__hour",
67
- timeMinute: "vc-time__minute",
68
- timeKeeping: "vc-time__keeping",
69
- timeRanges: "vc-time__ranges",
70
- timeRange: "vc-time__range",
71
- },
72
- }
73
- end
74
-
75
7
  def initialize(
76
8
  name: nil,
77
9
  id: nil,
@@ -86,6 +18,15 @@ module ShadcnPhlexcomponents
86
18
  )
87
19
  @name = name
88
20
  @id = id
21
+
22
+ if value
23
+ value = if value.is_a?(String)
24
+ DateTime.parse(value) rescue nil
25
+ else
26
+ value
27
+ end
28
+ end
29
+
89
30
  @value = value&.utc&.iso8601
90
31
  @format = format
91
32
  @select_only = select_only
@@ -93,8 +34,7 @@ module ShadcnPhlexcomponents
93
34
  @disabled = disabled
94
35
  @mask = mask
95
36
  @aria_id = "date-picker-#{SecureRandom.hex(5)}"
96
- @date_picker_styles = self.class.date_picker_styles
97
- @options = options.merge(styles: @date_picker_styles[:calendar])
37
+ @options = options
98
38
  super(**attributes)
99
39
  end
100
40
 
@@ -112,6 +52,8 @@ module ShadcnPhlexcomponents
112
52
 
113
53
  def view_template(&)
114
54
  div(**@attributes) do
55
+ overlay("date-picker")
56
+
115
57
  input(
116
58
  type: :hidden,
117
59
  name: @name,
@@ -130,12 +72,19 @@ module ShadcnPhlexcomponents
130
72
  placeholder: @placeholder,
131
73
  )
132
74
  else
133
- div(class: @date_picker_styles[:input_container], data: { date_picker_target: "inputContainer", disabled: @disabled }) do
75
+ div(class: <<~HEREDOC,
76
+ focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]
77
+ data-[focus=true]:border-ring data-[focus=true]:ring-ring/50 data-[focus=true]:ring-[3px]
78
+ data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50 flex shadow-xs transition-[color,box-shadow]
79
+ rounded-md border bg-transparent dark:bg-input/30 border-input outline-none h-9 flex items-center
80
+ aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive
81
+ HEREDOC
82
+ data: { date_picker_target: "inputContainer", disabled: @disabled }) do
134
83
  input(
135
84
  id: @id,
136
85
  placeholder: @placeholder || @format,
137
86
  type: :text,
138
- class: @date_picker_styles[:input],
87
+ class: "md:text-sm placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground flex h-9 w-full min-w-0 text-base outline-none px-3 py-1",
139
88
  disabled: @disabled,
140
89
  data: {
141
90
  date_picker_target: "input",
@@ -223,13 +172,7 @@ module ShadcnPhlexcomponents
223
172
  end
224
173
 
225
174
  def class_variants(**args)
226
- PopoverContent.new.class_variants(
227
- class: <<~HEREDOC,
228
- fixed left-1/2 top-1/2 shadow-lg -translate-x-1/2 -translate-y-1/2 pointer-events-auto w-max
229
- md:relative md:left-[unset] md:top-[unset] md:shadow-md md:translate-x-[unset] md:translate-y-[unset] md:pointer-events-[unset]
230
- #{args[:class]}
231
- HEREDOC
232
- )
175
+ PopoverContent.new.class_variants(class: "w-fit #{args[:class]}")
233
176
  end
234
177
 
235
178
  def default_attributes
@@ -252,7 +195,8 @@ module ShadcnPhlexcomponents
252
195
 
253
196
  def view_template(&)
254
197
  div(
255
- class: "hidden fixed top-0 left-0 w-max z-50",
198
+ style: { display: "none" },
199
+ class: "fixed z-50 top-1/4 left-1/2 -translate-x-1/2 pointer-events-auto md:top-auto md:left-auto md:translate-none md:pointer-events-[unset]",
256
200
  data: { "#{stimulus_controller_name}-target" => "contentContainer" },
257
201
  ) do
258
202
  div(**@attributes) do
@@ -261,4 +205,4 @@ module ShadcnPhlexcomponents
261
205
  end
262
206
  end
263
207
  end
264
- end
208
+ end
@@ -24,6 +24,16 @@ module ShadcnPhlexcomponents
24
24
  raise ArgumentError, "Expected an array for \"value\", got #{value.class}"
25
25
  end
26
26
 
27
+ if value
28
+ value = value.map do |v|
29
+ if v.is_a?(String)
30
+ DateTime.parse(v) rescue nil
31
+ else
32
+ v
33
+ end
34
+ end
35
+ end
36
+
27
37
  @name = name ? name[0] : nil
28
38
  @end_name = name ? name[1] : nil
29
39
  @value = (value ? value[0] : nil)&.utc&.iso8601
@@ -35,8 +45,7 @@ module ShadcnPhlexcomponents
35
45
  @disabled = disabled
36
46
  @mask = mask
37
47
  @aria_id = "date-range-picker-#{SecureRandom.hex(5)}"
38
- @date_picker_styles = DatePicker.date_picker_styles
39
- @options = options.merge(styles: @date_picker_styles[:calendar])
48
+ @options = options
40
49
  super(**attributes)
41
50
  end
42
51
 
@@ -55,6 +64,8 @@ module ShadcnPhlexcomponents
55
64
 
56
65
  def view_template(&)
57
66
  div(**@attributes) do
67
+ overlay('date-range-picker')
68
+
58
69
  input(
59
70
  type: :hidden,
60
71
  name: @name,
@@ -81,14 +92,20 @@ module ShadcnPhlexcomponents
81
92
  )
82
93
  else
83
94
  div(
84
- class: @date_picker_styles[:input_container],
95
+ class: <<~HEREDOC,
96
+ focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]
97
+ data-[focus=true]:border-ring data-[focus=true]:ring-ring/50 data-[focus=true]:ring-[3px]
98
+ data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50 flex shadow-xs transition-[color,box-shadow]
99
+ rounded-md border bg-transparent dark:bg-input/30 border-input outline-none h-9 flex items-center
100
+ aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive
101
+ HEREDOC
85
102
  data: { date_range_picker_target: "inputContainer", disabled: @disabled },
86
103
  ) do
87
104
  input(
88
105
  id: @id,
89
106
  placeholder: @placeholder || "#{@format} - #{@format}",
90
107
  type: :text,
91
- class: @date_picker_styles[:input],
108
+ class: "md:text-sm placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground flex h-9 w-full min-w-0 text-base outline-none px-3 py-1",
92
109
  disabled: @disabled,
93
110
  data: {
94
111
  date_range_picker_target: "input",
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ShadcnPhlexcomponents
4
4
  class Dialog < Base
5
- class_variants(base: "inline-block max-w-fit")
5
+ class_variants(base: "inline-flex max-w-fit")
6
6
 
7
7
  def initialize(open: false, **attributes)
8
8
  @open = open
@@ -42,13 +42,17 @@ module ShadcnPhlexcomponents
42
42
  {
43
43
  data: {
44
44
  controller: "dialog",
45
- dialog_is_open_value: @open.to_s,
46
- },
45
+ dialog_is_open_value: @open.to_s
46
+ }
47
47
  }
48
48
  end
49
49
 
50
50
  def view_template(&)
51
- div(**@attributes, &)
51
+ div(**@attributes) do
52
+ overlay("dialog")
53
+
54
+ yield
55
+ end
52
56
  end
53
57
  end
54
58
 
@@ -67,10 +71,10 @@ module ShadcnPhlexcomponents
67
71
  expanded: "false",
68
72
  controls: "#{@aria_id}-content",
69
73
  },
70
- data: {
71
- action: "click->dialog#open",
72
- dialog_target: "trigger",
74
+ data: {
73
75
  as_child: @as_child.to_s,
76
+ dialog_target: "trigger",
77
+ action: "click->dialog#open"
74
78
  },
75
79
  }
76
80
  end
@@ -98,7 +102,7 @@ module ShadcnPhlexcomponents
98
102
  data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95
99
103
  data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)]
100
104
  translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg
101
- pointer-events-auto
105
+ pointer-events-auto outline-none
102
106
  HEREDOC
103
107
  )
104
108
 
@@ -117,16 +121,14 @@ module ShadcnPhlexcomponents
117
121
  labelledby: "#{@aria_id}-title",
118
122
  },
119
123
  data: {
120
- dialog_target: "content",
121
124
  state: "closed",
122
- action: "dialog:click:outside->dialog#close",
125
+ dialog_target: "content"
123
126
  },
124
127
  }
125
128
  end
126
129
 
127
130
  def view_template(&)
128
- @class = @attributes.delete(:class)
129
- div(class: "#{@class} hidden", **@attributes) do
131
+ div(style: { display: "none" }, **@attributes) do
130
132
  yield
131
133
 
132
134
  button(
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ShadcnPhlexcomponents
4
4
  class DropdownMenu < Base
5
- class_variants(base: "inline-block max-w-fit")
5
+ class_variants(base: "inline-flex max-w-fit")
6
6
 
7
7
  def initialize(open: false, **attributes)
8
8
  @aria_id = "dropdown-menu-#{SecureRandom.hex(5)}"
@@ -93,9 +93,9 @@ module ShadcnPhlexcomponents
93
93
  dropdown_menu_target: "trigger",
94
94
  action: <<~HEREDOC,
95
95
  click->dropdown-menu#toggle
96
- keydown.space->dropdown-menu#open
97
- keydown.enter->dropdown-menu#open
98
96
  keydown.down->dropdown-menu#open:prevent
97
+ keydown.space->dropdown-menu#open:prevent
98
+ keydown.enter->dropdown-menu#open:prevent
99
99
  HEREDOC
100
100
  },
101
101
  }
@@ -123,7 +123,8 @@ module ShadcnPhlexcomponents
123
123
 
124
124
  def view_template(&)
125
125
  div(
126
- class: "hidden fixed top-0 left-0 w-max z-50",
126
+ style: { display: "none" },
127
+ class: "fixed top-0 left-0 w-max z-50",
127
128
  data: { dropdown_menu_target: "contentContainer" },
128
129
  ) do
129
130
  div(**@attributes, &)
@@ -125,7 +125,8 @@ module ShadcnPhlexcomponents
125
125
 
126
126
  def view_template(&)
127
127
  div(
128
- class: "hidden fixed top-0 left-0 w-max z-50",
128
+ style: { display: "none" },
129
+ class: "fixed top-0 left-0 w-max z-50",
129
130
  data: { dropdown_menu_sub_target: "contentContainer" },
130
131
  ) do
131
132
  div(**@attributes, &)
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class FormCombobox < Base
5
+ include FormHelpers
6
+
7
+ def initialize(
8
+ method = nil,
9
+ model: false,
10
+ object_name: nil,
11
+ collection: [],
12
+ value_method: nil,
13
+ text_method: nil,
14
+ value: nil,
15
+ name: nil,
16
+ id: nil,
17
+ label: nil,
18
+ error: nil,
19
+ hint: nil,
20
+ disabled_items: nil,
21
+ **attributes
22
+ )
23
+ @method = method
24
+ @model = model
25
+ @object_name = object_name
26
+
27
+ @collection = if collection.first&.is_a?(Hash)
28
+ convert_collection_hash_to_struct(collection, value_method: value_method, text_method: text_method)
29
+ else
30
+ collection
31
+ end
32
+
33
+ @value_method = value_method
34
+ @text_method = text_method
35
+ @value = default_value(value, method)
36
+ @name = name
37
+ @id = id
38
+ @label = label
39
+ @error = default_error(error, method)
40
+ @hint = hint
41
+ @disabled_items = disabled_items
42
+ @aria_id = "form-field-#{SecureRandom.hex(5)}"
43
+ super(**attributes)
44
+ end
45
+
46
+ def view_template(&)
47
+ vanish(&)
48
+
49
+ @id ||= field_id(@object_name, @method)
50
+ @name ||= field_name(@object_name, @method)
51
+
52
+ div(class: "space-y-2", data: label_and_hint_container_attributes) do
53
+ render_label(&)
54
+
55
+ Combobox(id: @id, name: @name, value: @value, aria: aria_attributes, **@attributes) do |c|
56
+ c.items(@collection, value_method: @value_method, text_method: @text_method, disabled_items: @disabled_items)
57
+ end
58
+
59
+ render_hint(&)
60
+ render_error
61
+ end
62
+ end
63
+ end
64
+ end
@@ -11,6 +11,7 @@ require_relative "form/form_checkbox_group"
11
11
  require_relative "form/form_date_picker"
12
12
  require_relative "form/form_date_range_picker"
13
13
  require_relative "form/form_select"
14
+ require_relative "form/form_combobox"
14
15
  require_relative "form/form_radio_group"
15
16
  require_relative "form/form_slider"
16
17
 
@@ -83,6 +84,19 @@ module ShadcnPhlexcomponents
83
84
  )
84
85
  end
85
86
 
87
+ def combobox(method = nil, collection = [], value_method:, text_method:, **attributes, &)
88
+ FormCombobox(
89
+ method,
90
+ model: @model,
91
+ object_name: @object_name,
92
+ collection: collection,
93
+ value_method: value_method,
94
+ text_method: text_method,
95
+ **attributes,
96
+ &
97
+ )
98
+ end
99
+
86
100
  def date_picker(method = nil, **attributes, &)
87
101
  FormDatePicker(method, model: @model, object_name: @object_name, **attributes, &)
88
102
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ShadcnPhlexcomponents
4
4
  class HoverCard < Base
5
- class_variants(base: "inline-block max-w-fit")
5
+ class_variants(base: "inline-flex max-w-fit")
6
6
 
7
7
  def initialize(open: false, **attributes)
8
8
  @open = open
@@ -88,7 +88,8 @@ module ShadcnPhlexcomponents
88
88
 
89
89
  def view_template(&)
90
90
  div(
91
- class: "hidden fixed top-0 left-0 w-max z-50",
91
+ style: { display: "none" },
92
+ class: "fixed top-0 left-0 w-max z-50",
92
93
  data: { hover_card_target: "contentContainer" },
93
94
  ) do
94
95
  div(**@attributes, &)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ShadcnPhlexcomponents
4
4
  class Popover < Base
5
- class_variants(base: "inline-block max-w-fit")
5
+ class_variants(base: "inline-flex max-w-fit")
6
6
 
7
7
  def initialize(open: false, **attributes)
8
8
  @open = open
@@ -23,7 +23,6 @@ module ShadcnPhlexcomponents
23
23
  data: {
24
24
  controller: "popover",
25
25
  popover_is_open_value: @open.to_s,
26
- side: @side,
27
26
  },
28
27
  }
29
28
  end
@@ -92,7 +91,8 @@ module ShadcnPhlexcomponents
92
91
 
93
92
  def view_template(&)
94
93
  div(
95
- class: "hidden fixed top-0 left-0 w-max z-50",
94
+ style: { display: "none" },
95
+ class: "fixed top-0 left-0 w-max z-50",
96
96
  data: { popover_target: "contentContainer" },
97
97
  ) do
98
98
  div(**@attributes, &)
@@ -10,7 +10,6 @@ module ShadcnPhlexcomponents
10
10
  value: nil,
11
11
  placeholder: nil,
12
12
  native: false,
13
- dir: "ltr",
14
13
  include_blank: false,
15
14
  disabled: false,
16
15
  **attributes
@@ -20,7 +19,6 @@ module ShadcnPhlexcomponents
20
19
  @value = value
21
20
  @placeholder = placeholder
22
21
  @native = native
23
- @dir = dir
24
22
  @include_blank = include_blank
25
23
  @disabled = disabled
26
24
  @aria_id = "select-#{SecureRandom.hex(5)}"
@@ -39,7 +37,6 @@ module ShadcnPhlexcomponents
39
37
  SelectTrigger(
40
38
  id: @id,
41
39
  aria_id: @aria_id,
42
- dir: @dir,
43
40
  value: @value,
44
41
  placeholder: @placeholder,
45
42
  disabled: @disabled,
@@ -49,7 +46,7 @@ module ShadcnPhlexcomponents
49
46
 
50
47
  def content(**attributes, &)
51
48
  SelectContent(
52
- aria_id: @aria_id, dir: @dir, include_blank: @include_blank, native: @native, **attributes, &
49
+ aria_id: @aria_id, include_blank: @include_blank, native: @native, **attributes, &
53
50
  )
54
51
  end
55
52
 
@@ -75,13 +72,12 @@ module ShadcnPhlexcomponents
75
72
  SelectTrigger(
76
73
  id: @id,
77
74
  aria_id: @aria_id,
78
- dir: @dir,
79
75
  value: @value,
80
76
  placeholder: @placeholder,
81
77
  disabled: @disabled,
82
78
  )
83
79
 
84
- SelectContent(aria_id: @aria_id, dir: @dir, include_blank: @include_blank, native: @native) do
80
+ SelectContent(aria_id: @aria_id, include_blank: @include_blank, native: @native) do
85
81
  collection.each do |item|
86
82
  value = item.public_send(value_method)
87
83
  text = item.public_send(text_method)
@@ -151,7 +147,7 @@ module ShadcnPhlexcomponents
151
147
  next if content_child.is_a?(Nokogiri::XML::Text) || content_child.is_a?(Nokogiri::XML::Comment)
152
148
 
153
149
  if content_child.attributes["data-select-target"]&.value == "group"
154
- group_label = content_child.at_css('[data-select-target="label"]')&.text
150
+ group_label = content_child.at_css('[data-shadcn-phlexcomponents="select-label"]')&.text
155
151
 
156
152
  optgroup(label: group_label, class: NATIVE_OPTION_STYLES) do
157
153
  content_child.css('[data-select-target="item"]').each do |i|
@@ -195,11 +191,10 @@ module ShadcnPhlexcomponents
195
191
  HEREDOC
196
192
  )
197
193
 
198
- def initialize(id: nil, value: nil, placeholder: nil, dir: "ltr", aria_id: nil, **attributes)
194
+ def initialize(id: nil, value: nil, placeholder: nil, aria_id: nil, **attributes)
199
195
  @id = id
200
196
  @value = value
201
197
  @placeholder = placeholder
202
- @dir = dir
203
198
  @aria_id = aria_id
204
199
  super(**attributes)
205
200
  end
@@ -218,7 +213,6 @@ module ShadcnPhlexcomponents
218
213
  {
219
214
  type: "button",
220
215
  id: @id,
221
- dir: @dir,
222
216
  role: "combobox",
223
217
  aria: {
224
218
  autocomplete: "none",
@@ -230,9 +224,9 @@ module ShadcnPhlexcomponents
230
224
  has_value: @value.present?.to_s,
231
225
  action: <<~HEREDOC,
232
226
  click->select#toggle
233
- keydown.space->select#open
234
- keydown.enter->select#open
235
227
  keydown.down->select#open:prevent
228
+ keydown.space->select#open:prevent
229
+ keydown.enter->select#open:prevent
236
230
  HEREDOC
237
231
  select_target: "trigger",
238
232
  },
@@ -252,10 +246,9 @@ module ShadcnPhlexcomponents
252
246
  HEREDOC
253
247
  )
254
248
 
255
- def initialize(include_blank: false, native: false, dir: "ltr", side: :bottom, align: :center, aria_id: nil, **attributes)
249
+ def initialize(include_blank: false, native: false, side: :bottom, align: :center, aria_id: nil, **attributes)
256
250
  @include_blank = include_blank
257
251
  @native = native
258
- @dir = dir
259
252
  @side = side
260
253
  @align = align
261
254
  @aria_id = aria_id
@@ -264,12 +257,13 @@ module ShadcnPhlexcomponents
264
257
 
265
258
  def view_template(&)
266
259
  div(
267
- class: "hidden fixed top-0 left-0 w-max z-50",
260
+ style: { display: "none" },
261
+ class: "fixed top-0 left-0 w-max z-50",
268
262
  data: { select_target: "contentContainer" },
269
263
  ) do
270
264
  div(**@attributes) do
271
265
  if @include_blank && !@native
272
- SelectItem(aria_id: @aria_id, value: "", class: "h-8", hide_icon: true) do
266
+ SelectItem(aria_id: @aria_id, value: "", class: "h-8") do
273
267
  @include_blank.is_a?(String) ? @include_blank : ""
274
268
  end
275
269
  end
@@ -282,7 +276,6 @@ module ShadcnPhlexcomponents
282
276
  def default_attributes
283
277
  {
284
278
  id: "#{@aria_id}-content",
285
- dir: @dir,
286
279
  tabindex: -1,
287
280
  role: "listbox",
288
281
  aria: {
@@ -316,14 +309,6 @@ module ShadcnPhlexcomponents
316
309
  def view_template(&)
317
310
  div(**@attributes, &)
318
311
  end
319
-
320
- def default_attributes
321
- {
322
- data: {
323
- select_target: "label",
324
- },
325
- }
326
- end
327
312
  end
328
313
 
329
314
  class SelectItem < Base
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ShadcnPhlexcomponents
4
4
  class Sheet < Base
5
- class_variants(base: "inline-block max-w-fit")
5
+ class_variants(base: "inline-flex max-w-fit")
6
6
 
7
7
  def initialize(open: false, **attributes)
8
8
  @open = open
@@ -42,12 +42,17 @@ module ShadcnPhlexcomponents
42
42
  {
43
43
  data: {
44
44
  controller: "dialog",
45
- },
46
- }
45
+ dialog_is_open_value: @open.to_s
46
+ }
47
+ }
47
48
  end
48
49
 
49
50
  def view_template(&)
50
- div(**@attributes, &)
51
+ div(**@attributes) do
52
+ overlay("dialog")
53
+
54
+ yield
55
+ end
51
56
  end
52
57
  end
53
58
 
@@ -66,10 +71,10 @@ module ShadcnPhlexcomponents
66
71
  expanded: false,
67
72
  controls: "#{@aria_id}-content",
68
73
  },
69
- data: {
74
+ data: {
70
75
  as_child: @as_child.to_s,
71
- action: "click->dialog#open",
72
76
  dialog_target: "trigger",
77
+ action: "click->dialog#open"
73
78
  },
74
79
  }
75
80
  end
@@ -95,7 +100,7 @@ module ShadcnPhlexcomponents
95
100
  base: <<~HEREDOC,
96
101
  bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4
97
102
  shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500
98
- pointer-events-auto
103
+ pointer-events-auto outline-none
99
104
  HEREDOC
100
105
  variants: {
101
106
  side: {
@@ -117,8 +122,7 @@ module ShadcnPhlexcomponents
117
122
  end
118
123
 
119
124
  def view_template(&)
120
- @class = @attributes.delete(:class)
121
- div(class: "#{@class} hidden", **@attributes) do
125
+ div(style: { display: "none" }, **@attributes) do
122
126
  yield
123
127
 
124
128
  button(
@@ -145,8 +149,8 @@ module ShadcnPhlexcomponents
145
149
  labelledby: "#{@aria_id}-title",
146
150
  },
147
151
  data: {
148
- dialog_target: "content",
149
- action: "dialog:click:outside->dialog#close",
152
+ state: "closed",
153
+ dialog_target: "content"
150
154
  },
151
155
  }
152
156
  end