shadcn_phlexcomponents 0.1.17 → 0.1.19

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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +321 -23
  3. data/app/javascript/controllers/accordion_controller.js +1 -0
  4. data/app/javascript/controllers/alert_dialog_controller.js +1 -0
  5. data/app/javascript/controllers/avatar_controller.js +1 -0
  6. data/app/javascript/controllers/checkbox_controller.js +1 -0
  7. data/app/javascript/controllers/collapsible_controller.js +1 -0
  8. data/app/javascript/controllers/combobox_controller.js +1 -1
  9. data/app/javascript/controllers/command_controller.js +1 -0
  10. data/app/javascript/controllers/date_picker_controller.js +21 -5
  11. data/app/javascript/controllers/date_range_picker_controller.js +1 -0
  12. data/app/javascript/controllers/dialog_controller.js +1 -0
  13. data/app/javascript/controllers/dropdown_menu_controller.js +1 -0
  14. data/app/javascript/controllers/dropdown_menu_sub_controller.js +1 -0
  15. data/app/javascript/controllers/form_field_controller.js +1 -0
  16. data/app/javascript/controllers/hover_card_controller.js +1 -0
  17. data/app/javascript/controllers/loading_button_controller.js +1 -0
  18. data/app/javascript/controllers/popover_controller.js +1 -0
  19. data/app/javascript/controllers/progress_controller.js +1 -0
  20. data/app/javascript/controllers/radio_group_controller.js +1 -0
  21. data/app/javascript/controllers/select_controller.js +1 -0
  22. data/app/javascript/controllers/slider_controller.js +1 -0
  23. data/app/javascript/controllers/switch_controller.js +1 -0
  24. data/app/javascript/controllers/tabs_controller.js +1 -0
  25. data/app/javascript/controllers/theme_switcher_controller.js +1 -0
  26. data/app/javascript/controllers/toast_container_controller.js +1 -0
  27. data/app/javascript/controllers/toast_controller.js +1 -0
  28. data/app/javascript/controllers/toggle_controller.js +1 -0
  29. data/app/javascript/controllers/toggle_group_controller.js +1 -0
  30. data/app/javascript/controllers/tooltip_controller.js +2 -1
  31. data/app/javascript/shadcn_phlexcomponents.js +27 -60
  32. data/app/javascript/utils/command.js +0 -2
  33. data/app/javascript/utils/floating_ui.js +10 -17
  34. data/app/stylesheets/date_picker.css +1 -1
  35. data/app/stylesheets/shadcn_phlexcomponents.css +173 -0
  36. data/app/typescript/controllers/accordion_controller.ts +2 -0
  37. data/app/typescript/controllers/alert_dialog_controller.ts +2 -0
  38. data/app/typescript/controllers/avatar_controller.ts +2 -0
  39. data/app/typescript/controllers/checkbox_controller.ts +2 -0
  40. data/app/typescript/controllers/collapsible_controller.ts +2 -0
  41. data/app/typescript/controllers/combobox_controller.ts +2 -1
  42. data/app/typescript/controllers/command_controller.ts +2 -0
  43. data/app/typescript/controllers/date_picker_controller.ts +27 -7
  44. data/app/typescript/controllers/date_range_picker_controller.ts +2 -0
  45. data/app/typescript/controllers/dialog_controller.ts +2 -0
  46. data/app/typescript/controllers/dropdown_menu_controller.ts +2 -0
  47. data/app/typescript/controllers/dropdown_menu_sub_controller.ts +2 -0
  48. data/app/typescript/controllers/form_field_controller.ts +2 -0
  49. data/app/typescript/controllers/hover_card_controller.ts +2 -0
  50. data/app/typescript/controllers/loading_button_controller.ts +2 -0
  51. data/app/typescript/controllers/popover_controller.ts +2 -0
  52. data/app/typescript/controllers/progress_controller.ts +2 -0
  53. data/app/typescript/controllers/radio_group_controller.ts +2 -0
  54. data/app/typescript/controllers/select_controller.ts +2 -0
  55. data/app/typescript/controllers/slider_controller.ts +2 -0
  56. data/app/typescript/controllers/switch_controller.ts +2 -0
  57. data/app/typescript/controllers/tabs_controller.ts +2 -0
  58. data/app/typescript/controllers/theme_switcher_controller.ts +2 -0
  59. data/app/typescript/controllers/toast_container_controller.ts +2 -0
  60. data/app/typescript/controllers/toast_controller.ts +2 -0
  61. data/app/typescript/controllers/toggle_controller.ts +2 -0
  62. data/app/typescript/controllers/toggle_group_controller.ts +2 -0
  63. data/app/typescript/controllers/tooltip_controller.ts +3 -1
  64. data/app/typescript/shadcn_phlexcomponents.ts +27 -61
  65. data/app/typescript/utils/command.ts +0 -2
  66. data/app/typescript/utils/floating_ui.ts +11 -20
  67. data/app/typescript/utils/index.ts +1 -1
  68. data/lib/install/upgrade_shadcn_phlexcomponents.rb +28 -0
  69. data/lib/shadcn_phlexcomponents/components/accordion.rb +56 -13
  70. data/lib/shadcn_phlexcomponents/components/alert.rb +35 -16
  71. data/lib/shadcn_phlexcomponents/components/alert_dialog.rb +58 -18
  72. data/lib/shadcn_phlexcomponents/components/aspect_ratio.rb +33 -2
  73. data/lib/shadcn_phlexcomponents/components/avatar.rb +24 -7
  74. data/lib/shadcn_phlexcomponents/components/badge.rb +23 -18
  75. data/lib/shadcn_phlexcomponents/components/base.rb +2 -2
  76. data/lib/shadcn_phlexcomponents/components/breadcrumb.rb +46 -6
  77. data/lib/shadcn_phlexcomponents/components/button.rb +32 -27
  78. data/lib/shadcn_phlexcomponents/components/card.rb +59 -10
  79. data/lib/shadcn_phlexcomponents/components/checkbox.rb +51 -30
  80. data/lib/shadcn_phlexcomponents/components/checkbox_group.rb +24 -4
  81. data/lib/shadcn_phlexcomponents/components/combobox.rb +224 -81
  82. data/lib/shadcn_phlexcomponents/components/command.rb +167 -63
  83. data/lib/shadcn_phlexcomponents/components/date_picker.rb +140 -48
  84. data/lib/shadcn_phlexcomponents/components/date_range_picker.rb +26 -44
  85. data/lib/shadcn_phlexcomponents/components/dialog.rb +86 -32
  86. data/lib/shadcn_phlexcomponents/components/dropdown_menu.rb +74 -25
  87. data/lib/shadcn_phlexcomponents/components/dropdown_menu_sub.rb +52 -24
  88. data/lib/shadcn_phlexcomponents/components/form/form_checkbox.rb +1 -1
  89. data/lib/shadcn_phlexcomponents/components/form/form_checkbox_group.rb +1 -1
  90. data/lib/shadcn_phlexcomponents/components/form/form_combobox.rb +1 -1
  91. data/lib/shadcn_phlexcomponents/components/form/form_date_picker.rb +1 -1
  92. data/lib/shadcn_phlexcomponents/components/form/form_date_range_picker.rb +1 -1
  93. data/lib/shadcn_phlexcomponents/components/form/form_error.rb +8 -1
  94. data/lib/shadcn_phlexcomponents/components/form/form_helpers.rb +3 -2
  95. data/lib/shadcn_phlexcomponents/components/form/form_hint.rb +8 -1
  96. data/lib/shadcn_phlexcomponents/components/form/form_input.rb +1 -1
  97. data/lib/shadcn_phlexcomponents/components/form/form_radio_group.rb +1 -1
  98. data/lib/shadcn_phlexcomponents/components/form/form_select.rb +1 -1
  99. data/lib/shadcn_phlexcomponents/components/form/form_slider.rb +1 -1
  100. data/lib/shadcn_phlexcomponents/components/form/form_switch.rb +1 -1
  101. data/lib/shadcn_phlexcomponents/components/form/form_textarea.rb +1 -1
  102. data/lib/shadcn_phlexcomponents/components/form.rb +22 -6
  103. data/lib/shadcn_phlexcomponents/components/hover_card.rb +48 -18
  104. data/lib/shadcn_phlexcomponents/components/input.rb +13 -8
  105. data/lib/shadcn_phlexcomponents/components/label.rb +9 -4
  106. data/lib/shadcn_phlexcomponents/components/link.rb +8 -1
  107. data/lib/shadcn_phlexcomponents/components/pagination.rb +34 -6
  108. data/lib/shadcn_phlexcomponents/components/popover.rb +43 -13
  109. data/lib/shadcn_phlexcomponents/components/progress.rb +37 -6
  110. data/lib/shadcn_phlexcomponents/components/radio_group.rb +41 -15
  111. data/lib/shadcn_phlexcomponents/components/select.rb +99 -42
  112. data/lib/shadcn_phlexcomponents/components/separator.rb +9 -4
  113. data/lib/shadcn_phlexcomponents/components/sheet.rb +94 -28
  114. data/lib/shadcn_phlexcomponents/components/skeleton.rb +8 -1
  115. data/lib/shadcn_phlexcomponents/components/switch.rb +45 -17
  116. data/lib/shadcn_phlexcomponents/components/table.rb +84 -17
  117. data/lib/shadcn_phlexcomponents/components/tabs.rb +36 -12
  118. data/lib/shadcn_phlexcomponents/components/textarea.rb +12 -7
  119. data/lib/shadcn_phlexcomponents/components/toast.rb +46 -20
  120. data/lib/shadcn_phlexcomponents/components/toast_container.rb +19 -14
  121. data/lib/shadcn_phlexcomponents/components/toggle.rb +29 -24
  122. data/lib/shadcn_phlexcomponents/components/tooltip.rb +49 -14
  123. data/lib/shadcn_phlexcomponents/configuration.rb +46 -0
  124. data/lib/shadcn_phlexcomponents/initializers/shadcn_phlexcomponents.rb +28 -0
  125. data/lib/shadcn_phlexcomponents/version.rb +1 -1
  126. data/lib/shadcn_phlexcomponents.rb +12 -1
  127. data/lib/tasks/upgrade.rake +10 -0
  128. metadata +16 -14
  129. data/app/typescript/controllers/sidebar_controller.ts +0 -39
  130. data/app/typescript/controllers/sidebar_trigger_controller.ts +0 -21
@@ -2,7 +2,14 @@
2
2
 
3
3
  module ShadcnPhlexcomponents
4
4
  class Command < Base
5
- class_variants(base: "inline-flex max-w-fit")
5
+ class_variants(
6
+ **(
7
+ ShadcnPhlexcomponents.configuration.command&.dig(:root) ||
8
+ {
9
+ base: "inline-flex max-w-fit",
10
+ }
11
+ ),
12
+ )
6
13
 
7
14
  MODIFIER_KEYS = [
8
15
  :ctrl,
@@ -11,9 +18,9 @@ module ShadcnPhlexcomponents
11
18
  ]
12
19
 
13
20
  def initialize(
14
- open: false,
15
- modifier_key: nil,
16
- shortcut_key: nil,
21
+ open: false,
22
+ modifier_key: nil,
23
+ shortcut_key: nil,
17
24
  search_path: nil,
18
25
  search_error_text: "Something went wrong, please try again.",
19
26
  search_empty_text: "No results found",
@@ -44,8 +51,8 @@ module ShadcnPhlexcomponents
44
51
  search_error_text: @search_error_text,
45
52
  search_empty_text: @search_empty_text,
46
53
  search_placeholder_text: @search_placeholder_text,
47
- aria_id: @aria_id,
48
- **attributes,
54
+ aria_id: @aria_id,
55
+ **attributes,
49
56
  &
50
57
  )
51
58
  end
@@ -69,8 +76,8 @@ module ShadcnPhlexcomponents
69
76
  command_is_open_value: @open.to_s,
70
77
  modifier_key: @modifier_key,
71
78
  shortcut_key: @shortcut_key,
72
- search_path: @search_path
73
- }
79
+ search_path: @search_path,
80
+ },
74
81
  }
75
82
  end
76
83
 
@@ -111,7 +118,7 @@ module ShadcnPhlexcomponents
111
118
  },
112
119
  data: {
113
120
  command_target: "trigger",
114
- action: "click->command#open"
121
+ action: "click->command#open",
115
122
  },
116
123
  }
117
124
  end
@@ -137,19 +144,24 @@ module ShadcnPhlexcomponents
137
144
 
138
145
  class CommandContent < Base
139
146
  class_variants(
140
- base: <<~HEREDOC,
141
- bg-background bg-clip-padding dark:bg-neutral-900 dark:ring-neutral-800 data-[state=closed]:animate-out#{" "}
142
- data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0
143
- data-[state=open]:zoom-in-95 duration-200 fixed gap-4 grid left-[50%] max-w-[calc(100%-2rem)] p-2 pb-11 ring-4 ring-neutral-200/80
144
- rounded-xl shadow-2xl sm:max-w-lg top-[50%] translate-x-[-50%] translate-y-[-50%] w-full z-50 pointer-events-auto outline-none
145
- HEREDOC
147
+ **(
148
+ ShadcnPhlexcomponents.configuration.command&.dig(:content) ||
149
+ {
150
+ base: <<~HEREDOC,
151
+ bg-background bg-clip-padding dark:bg-neutral-900 dark:ring-neutral-800 data-[state=closed]:animate-out#{" "}
152
+ data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0
153
+ data-[state=open]:zoom-in-95 duration-200 fixed gap-4 grid left-[50%] max-w-[calc(100%-2rem)] p-2 pb-11 ring-4 ring-neutral-200/80
154
+ rounded-xl shadow-2xl sm:max-w-lg top-[50%] translate-x-[-50%] translate-y-[-50%] w-full z-50 pointer-events-auto outline-none
155
+ HEREDOC
156
+ }
157
+ ),
146
158
  )
147
159
 
148
160
  def initialize(
149
161
  search_error_text: nil,
150
162
  search_empty_text: nil,
151
163
  search_placeholder_text: nil,
152
- aria_id: nil,
164
+ aria_id: nil,
153
165
  **attributes
154
166
  )
155
167
  @search_error_text = search_error_text
@@ -187,7 +199,7 @@ module ShadcnPhlexcomponents
187
199
  template do
188
200
  CommandGroup do
189
201
  CommandLabel { "" }
190
- end
202
+ end
191
203
  end
192
204
 
193
205
  div(class: "text-popover-foreground flex h-full w-full flex-col overflow-hidden bg-transparent") do
@@ -202,33 +214,13 @@ module ShadcnPhlexcomponents
202
214
  for: "#{@aria_id}-search",
203
215
  ) { @search_placeholder_text }
204
216
 
205
- div(class: "flex h-9 items-center gap-2 border px-3 bg-input/50 border-input rounded-md") do
217
+ CommandSearchInputContainer do
206
218
  icon("search", class: "size-4 shrink-0 opacity-50")
207
219
 
208
- input(
209
- class: "placeholder:text-muted-foreground flex w-full rounded-md bg-transparent py-3 text-sm
210
- outline-hidden disabled:cursor-not-allowed disabled:opacity-50 h-9",
211
- id: "#{@aria_id}-search",
212
- placeholder: @search_placeholder_text,
213
- type: :text,
214
- autocomplete: "off",
215
- autocorrect: "off",
216
- role: "combobox",
217
- spellcheck: "false",
218
- aria: {
219
- autocomplete: "list",
220
- expanded: "false",
221
- controls: "#{@aria_id}-list",
222
- labelledby: "#{@aria_id}-search-label",
223
- },
224
- data: {
225
- command_target: "searchInput",
226
- action: "keydown->command#inputKeydown input->command#search",
227
- },
228
- )
220
+ CommandSearchInput(aria_id: @aria_id, search_placeholder_text: @search_placeholder_text)
229
221
  end
230
222
 
231
- div(class: "mt-3 p-1 max-h-80 min-h-80 overflow-y-auto", data: { command_target: "listContainer"}) do
223
+ CommandListContainer do
232
224
  CommandText(target: "empty") { @search_empty_text }
233
225
  CommandText(target: "error") { @search_error_text }
234
226
  CommandText(target: "loading") do
@@ -248,13 +240,18 @@ module ShadcnPhlexcomponents
248
240
 
249
241
  class CommandItem < Base
250
242
  class_variants(
251
- base: <<~HEREDOC,
252
- data-[highlighted=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex
253
- cursor-default items-center gap-2 px-3 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none
254
- data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4
255
- data-[highlighted=true]:border-input data-[highlighted=true]:bg-input/50 h-9 rounded-md border border-transparent
256
- font-medium
257
- HEREDOC
243
+ **(
244
+ ShadcnPhlexcomponents.configuration.command&.dig(:item) ||
245
+ {
246
+ base: <<~HEREDOC,
247
+ data-[highlighted=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex
248
+ cursor-default items-center gap-2 px-3 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none
249
+ data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4
250
+ data-[highlighted=true]:border-input data-[highlighted=true]:bg-input/50 h-9 rounded-md border border-transparent
251
+ font-medium
252
+ HEREDOC
253
+ }
254
+ ),
258
255
  )
259
256
 
260
257
  def initialize(value: nil, aria_id: nil, **attributes)
@@ -289,11 +286,14 @@ module ShadcnPhlexcomponents
289
286
  end
290
287
 
291
288
  class CommandLabel < Base
292
- class_variants(base: "text-muted-foreground text-xs px-3 pb-1 text-xs font-medium")
293
-
294
- def initialize( **attributes)
295
- super(**attributes)
296
- end
289
+ class_variants(
290
+ **(
291
+ ShadcnPhlexcomponents.configuration.command&.dig(:label) ||
292
+ {
293
+ base: "text-muted-foreground text-xs px-3 pb-1 text-xs font-medium",
294
+ }
295
+ ),
296
+ )
297
297
 
298
298
  def view_template(&)
299
299
  div(**@attributes, &)
@@ -301,7 +301,14 @@ module ShadcnPhlexcomponents
301
301
  end
302
302
 
303
303
  class CommandGroup < Base
304
- class_variants(base: "scroll-mt-16 first:pt-0 pt-3")
304
+ class_variants(
305
+ **(
306
+ ShadcnPhlexcomponents.configuration.command&.dig(:group) ||
307
+ {
308
+ base: "scroll-mt-16 first:pt-0 pt-3",
309
+ }
310
+ ),
311
+ )
305
312
 
306
313
  def initialize(aria_id: nil, **attributes)
307
314
  @aria_id = aria_id
@@ -326,8 +333,15 @@ module ShadcnPhlexcomponents
326
333
  end
327
334
 
328
335
  class CommandText < Base
329
- class_variants(base: "py-6 text-center text-sm hidden")
330
-
336
+ class_variants(
337
+ **(
338
+ ShadcnPhlexcomponents.configuration.command&.dig(:text) ||
339
+ {
340
+ base: "py-6 text-center text-sm hidden",
341
+ }
342
+ ),
343
+ )
344
+
331
345
  def initialize(target:, **attributes)
332
346
  @target = target
333
347
  super(**attributes)
@@ -347,10 +361,15 @@ module ShadcnPhlexcomponents
347
361
 
348
362
  class CommandKbd < Base
349
363
  class_variants(
350
- base: <<~HEREDOC,
351
- bg-background text-muted-foreground pointer-events-none flex h-5 items-center justify-center gap-1 rounded
352
- border px-1 font-sans text-[0.7rem] font-medium select-none [&_svg:not([class*='size-'])]:size-3
353
- HEREDOC
364
+ **(
365
+ ShadcnPhlexcomponents.configuration.command&.dig(:kbd) ||
366
+ {
367
+ base: <<~HEREDOC,
368
+ bg-background text-muted-foreground pointer-events-none flex h-5 items-center justify-center gap-1 rounded
369
+ border px-1 font-sans text-[0.7rem] font-medium select-none [&_svg:not([class*='size-'])]:size-3
370
+ HEREDOC
371
+ }
372
+ ),
354
373
  )
355
374
 
356
375
  def view_template(&)
@@ -360,10 +379,15 @@ module ShadcnPhlexcomponents
360
379
 
361
380
  class CommandFooter < Base
362
381
  class_variants(
363
- base: <<~HEREDOC,
364
- text-muted-foreground absolute inset-x-0 bottom-0 z-20 flex h-10 items-center gap-2 rounded-b-xl border-t#{" "}
365
- border-t-neutral-100 bg-neutral-50 px-4 text-xs font-medium dark:border-t-neutral-700 dark:bg-neutral-800
366
- HEREDOC
382
+ **(
383
+ ShadcnPhlexcomponents.configuration.command&.dig(:footer) ||
384
+ {
385
+ base: <<~HEREDOC,
386
+ text-muted-foreground absolute inset-x-0 bottom-0 z-20 flex h-10 items-center gap-2 rounded-b-xl border-t#{" "}
387
+ border-t-neutral-100 bg-neutral-50 px-4 text-xs font-medium dark:border-t-neutral-700 dark:bg-neutral-800
388
+ HEREDOC
389
+ }
390
+ ),
367
391
  )
368
392
 
369
393
  def view_template
@@ -378,4 +402,84 @@ module ShadcnPhlexcomponents
378
402
  end
379
403
  end
380
404
  end
405
+
406
+ class CommandSearchInputContainer < Base
407
+ class_variants(
408
+ **(
409
+ ShadcnPhlexcomponents.configuration.command&.dig(:search_input_container) ||
410
+ {
411
+ base: "flex h-9 items-center gap-2 border px-3 bg-input/50 border-input rounded-md",
412
+ }
413
+ ),
414
+ )
415
+
416
+ def view_template(&)
417
+ div(**@attributes, &)
418
+ end
419
+ end
420
+
421
+ class CommandSearchInput < Base
422
+ class_variants(
423
+ **(
424
+ ShadcnPhlexcomponents.configuration.command&.dig(:search_input) ||
425
+ {
426
+ base: <<~HEREDOC,
427
+ placeholder:text-muted-foreground flex w-full rounded-md bg-transparent py-3 text-sm
428
+ outline-hidden disabled:cursor-not-allowed disabled:opacity-50 h-9
429
+ HEREDOC
430
+ }
431
+ ),
432
+ )
433
+
434
+ def initialize(aria_id: nil, search_placeholder_text: nil, **attributes)
435
+ @aria_id = aria_id
436
+ @search_placeholder_text = search_placeholder_text
437
+ super(**attributes)
438
+ end
439
+
440
+ def default_attributes
441
+ {
442
+ id: "#{@aria_id}-search",
443
+ placeholder: @search_placeholder_text,
444
+ type: :text,
445
+ autocomplete: "off",
446
+ autocorrect: "off",
447
+ role: "combobox",
448
+ spellcheck: "false",
449
+ aria: {
450
+ autocomplete: "list",
451
+ expanded: "false",
452
+ controls: "#{@aria_id}-list",
453
+ labelledby: "#{@aria_id}-search-label",
454
+ },
455
+ data: {
456
+ command_target: "searchInput",
457
+ action: "keydown->command#inputKeydown input->command#search",
458
+ },
459
+ }
460
+ end
461
+
462
+ def view_template(&)
463
+ input(**@attributes)
464
+ end
465
+ end
466
+
467
+ class CommandListContainer < Base
468
+ class_variants(
469
+ **(
470
+ ShadcnPhlexcomponents.configuration.command&.dig(:list_container) ||
471
+ {
472
+ base: "mt-3 p-1 max-h-80 min-h-80 overflow-y-auto",
473
+ }
474
+ ),
475
+ )
476
+
477
+ def default_attributes
478
+ { data: { command_target: "listContainer" } }
479
+ end
480
+
481
+ def view_template(&)
482
+ div(**@attributes, &)
483
+ end
484
+ end
381
485
  end
@@ -2,7 +2,14 @@
2
2
 
3
3
  module ShadcnPhlexcomponents
4
4
  class DatePicker < Base
5
- class_variants(base: "w-full")
5
+ class_variants(
6
+ **(
7
+ ShadcnPhlexcomponents.configuration.date_picker&.dig(:root) ||
8
+ {
9
+ base: "w-full",
10
+ }
11
+ ),
12
+ )
6
13
 
7
14
  def initialize(
8
15
  name: nil,
@@ -20,8 +27,13 @@ module ShadcnPhlexcomponents
20
27
  @id = id
21
28
 
22
29
  if value
23
- value = if value.is_a?(String)
24
- DateTime.parse(value) rescue nil
30
+ value = if value.is_a?(String)
31
+ begin
32
+ # Use Time.zone.parse to ensure consistent timezone handling
33
+ Time.zone ? Time.zone.parse(value) : Time.parse(value)
34
+ rescue
35
+ nil
36
+ end
25
37
  else
26
38
  value
27
39
  end
@@ -66,44 +78,27 @@ module ShadcnPhlexcomponents
66
78
  # label will trigger the popover to appear
67
79
  DatePickerTrigger(
68
80
  disabled: @disabled,
69
- aria_id: @aria_id,
70
81
  select_only: @select_only,
71
82
  id: @id,
72
83
  placeholder: @placeholder,
84
+ stimulus_controller_name: "date-picker",
85
+ aria_id: @aria_id,
73
86
  )
74
87
  else
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
83
- input(
84
- id: @id,
85
- placeholder: @placeholder || @format,
86
- type: :text,
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",
88
- disabled: @disabled,
89
- data: {
90
- date_picker_target: "input",
91
- action: "input->date-picker#inputDate
92
- blur->date-picker#inputBlur
93
- focus->date-picker#setContainerFocus",
94
- },
95
- )
88
+ DatePickerInputContainer(disabled: @disabled, stimulus_controller_name: "date-picker") do
89
+ DatePickerInput(id: @id, placeholder: @placeholder, format: @format, disabled: @disabled, stimulus_controller_name: "date-picker", aria_id: @aria_id)
96
90
 
97
91
  DatePickerTrigger(
98
92
  disabled: @disabled,
99
- aria_id: @aria_id,
100
93
  select_only: @select_only,
101
94
  placeholder: @placeholder,
95
+ stimulus_controller_name: "date-picker",
96
+ aria_id: @aria_id,
102
97
  )
103
98
  end
104
99
  end
105
100
 
106
- DatePickerContent(aria_id: @aria_id)
101
+ DatePickerContent(stimulus_controller_name: "date-picker", aria_id: @aria_id)
107
102
  end
108
103
  end
109
104
  end
@@ -112,12 +107,14 @@ module ShadcnPhlexcomponents
112
107
  def initialize(
113
108
  select_only: true,
114
109
  placeholder: nil,
110
+ stimulus_controller_name: nil,
115
111
  aria_id: nil,
116
112
  **attributes
117
113
  )
118
114
  @select_only = select_only
119
115
  @placeholder = placeholder
120
116
  @aria_id = aria_id
117
+ @stimulus_controller_name = stimulus_controller_name
121
118
  super(**attributes)
122
119
  end
123
120
 
@@ -132,7 +129,7 @@ module ShadcnPhlexcomponents
132
129
  def view_template
133
130
  if @select_only
134
131
  button(type: :button, disabled: @disabled, **@attributes) do
135
- span(class: "pointer-events-none", data: { "#{stimulus_controller_name}-target" => "triggerText" })
132
+ span(class: "pointer-events-none", data: { "#{@stimulus_controller_name}-target" => "triggerText" })
136
133
 
137
134
  icon("calendar", class: "size-5")
138
135
  end
@@ -152,21 +149,18 @@ module ShadcnPhlexcomponents
152
149
  },
153
150
  data: {
154
151
  placeholder: @placeholder,
155
- action: "click->#{stimulus_controller_name}#toggle",
156
- "#{stimulus_controller_name}-target" => "trigger",
152
+ action: "click->#{@stimulus_controller_name}#toggle",
153
+ "#{@stimulus_controller_name}-target" => "trigger",
157
154
  },
158
155
  }
159
156
  end
160
-
161
- def stimulus_controller_name
162
- "date-picker"
163
- end
164
157
  end
165
158
 
166
159
  class DatePickerContent < Base
167
- def initialize(side: :bottom, align: :start, aria_id: nil, **attributes)
160
+ def initialize(side: :bottom, align: :start, stimulus_controller_name: nil, aria_id: nil, **attributes)
168
161
  @side = side
169
162
  @align = align
163
+ @stimulus_controller_name = stimulus_controller_name
170
164
  @aria_id = aria_id
171
165
  super(**attributes)
172
166
  end
@@ -183,26 +177,124 @@ module ShadcnPhlexcomponents
183
177
  data: {
184
178
  side: @side,
185
179
  align: @align,
186
- "#{stimulus_controller_name}-target" => "content",
187
- action: "#{stimulus_controller_name}:click:outside->#{stimulus_controller_name}#clickOutside",
180
+ "#{@stimulus_controller_name}-target" => "content",
181
+ action: "#{@stimulus_controller_name}:click:outside->#{@stimulus_controller_name}#clickOutside",
188
182
  },
189
183
  }
190
184
  end
191
185
 
192
- def stimulus_controller_name
193
- "date-picker"
194
- end
195
-
196
186
  def view_template(&)
197
- div(
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]",
200
- data: { "#{stimulus_controller_name}-target" => "contentContainer" },
201
- ) do
187
+ DatePickerContentContainer(stimulus_controller_name: @stimulus_controller_name) do
202
188
  div(**@attributes) do
203
- div(data: { "#{stimulus_controller_name}-target" => "calendar" })
189
+ div(data: { "#{@stimulus_controller_name}-target" => "calendar" })
204
190
  end
205
191
  end
206
192
  end
207
193
  end
208
- end
194
+
195
+ class DatePickerContentContainer < Base
196
+ class_variants(
197
+ **(
198
+ ShadcnPhlexcomponents.configuration.date_picker&.dig(:content_container) ||
199
+ {
200
+ base: "fixed top-0 left-0 w-max z-50",
201
+ }
202
+ ),
203
+ )
204
+
205
+ def initialize(stimulus_controller_name: nil, **attributes)
206
+ @stimulus_controller_name = stimulus_controller_name
207
+ super(**attributes)
208
+ end
209
+
210
+ def default_attributes
211
+ {
212
+ style: { display: "none" },
213
+ data: { "#{@stimulus_controller_name}-target" => "contentContainer" },
214
+ }
215
+ end
216
+
217
+ def view_template(&)
218
+ div(**@attributes, &)
219
+ end
220
+ end
221
+
222
+ class DatePickerInputContainer < Base
223
+ class_variants(
224
+ **(
225
+ ShadcnPhlexcomponents.configuration.date_picker&.dig(:input_container) ||
226
+ {
227
+ base: <<~HEREDOC,
228
+ focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]
229
+ data-[focus=true]:border-ring data-[focus=true]:ring-ring/50 data-[focus=true]:ring-[3px]
230
+ data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50 flex shadow-xs transition-[color,box-shadow]
231
+ rounded-md border bg-transparent dark:bg-input/30 border-input outline-none h-9 flex items-center
232
+ aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive
233
+ HEREDOC
234
+ }
235
+ ),
236
+ )
237
+
238
+ def initialize(disabled: false, stimulus_controller_name: nil, **attributes)
239
+ @disabled = disabled
240
+ @stimulus_controller_name = stimulus_controller_name
241
+ super(**attributes)
242
+ end
243
+
244
+ def default_attributes
245
+ {
246
+ data: {
247
+ "#{@stimulus_controller_name}-target" => "inputContainer",
248
+ disabled: @disabled,
249
+ },
250
+ }
251
+ end
252
+
253
+ def view_template(&)
254
+ div(**@attributes, &)
255
+ end
256
+ end
257
+
258
+ class DatePickerInput < Base
259
+ class_variants(
260
+ **(
261
+ ShadcnPhlexcomponents.configuration.date_picker&.dig(:input) ||
262
+ {
263
+ base: <<~HEREDOC,
264
+ md:text-sm placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground
265
+ flex h-9 w-full min-w-0 text-base outline-none px-3 py-1
266
+ HEREDOC
267
+ }
268
+ ),
269
+ )
270
+
271
+ def initialize(id: nil, placeholder: nil, format: nil, disabled: false, stimulus_controller_name: nil, aria_id: nil, **attributes)
272
+ @id = id
273
+ @placeholder = placeholder
274
+ @format = format
275
+ @disabled = disabled
276
+ @stimulus_controller_name = stimulus_controller_name
277
+ @aria_id = aria_id
278
+ super(**attributes)
279
+ end
280
+
281
+ def default_attributes
282
+ {
283
+ id: @id || "#{@aria_id}-input",
284
+ placeholder: @placeholder || @format,
285
+ type: :text,
286
+ disabled: @disabled,
287
+ data: {
288
+ "#{@stimulus_controller_name}-target" => "input",
289
+ action: "input->#{@stimulus_controller_name}#inputDate
290
+ blur->#{@stimulus_controller_name}#inputBlur
291
+ focus->#{@stimulus_controller_name}#setContainerFocus",
292
+ },
293
+ }
294
+ end
295
+
296
+ def view_template
297
+ input(**@attributes)
298
+ end
299
+ end
300
+ end