@@ -18,7 +18,9 @@ defmodule DemoWeb.OdysseyShowcaseLive do
1818 "percentage" => "" ,
1919 "start_date" => "" ,
2020 "expiration_date" => "" ,
21- "whitelisted_products" => ""
21+ "whitelisted_products" => "" ,
22+ "category_id" => "" ,
23+ "tag_ids" => ""
2224 }
2325
2426 socket =
@@ -30,10 +32,13 @@ defmodule DemoWeb.OdysseyShowcaseLive do
3032 |> assign ( :status_selection , "Active" )
3133 |> assign ( :size_selection , "Medium" )
3234 |> assign ( :channel_selection , :email )
35+ |> assign ( :standalone_select_item , nil )
3336 |> assign ( :form_data , form_data )
3437 |> assign ( :form , to_form ( form_data , as: "form" ) )
3538 |> assign ( :sample_activities , sample_activities ( ) )
3639 |> assign ( :sample_products , sample_products ( ) )
40+ |> assign ( :sample_categories , sample_categories ( ) )
41+ |> assign ( :sample_tags , sample_tags ( ) )
3742
3843 { :ok , socket }
3944 end
@@ -101,6 +106,12 @@ defmodule DemoWeb.OdysseyShowcaseLive do
101106 { :noreply , socket }
102107 end
103108
109+ @ impl true
110+ def handle_info ( { :item_selected , _context , id } , socket ) do
111+ item = Enum . find ( socket . assigns . sample_categories , & ( to_string ( & 1 . id ) == to_string ( id ) ) )
112+ { :noreply , assign ( socket , :standalone_select_item , item ) }
113+ end
114+
104115 @ impl true
105116 def render ( assigns ) do
106117 ~H"""
@@ -320,6 +331,53 @@ defmodule DemoWeb.OdysseyShowcaseLive do
320331
321332 < . odyssey_divider />
322333
334+ <!-- Select Dropdown Section -->
335+ < section >
336+ < h2 class = "text-2xl font-semibold mb-6 " > Select Dropdown</ h2 >
337+ < div class = "space-y-4 " >
338+ < div >
339+ < h3 class = "text-lg font-medium mb-2 " > Standalone Select (event-based)</ h3 >
340+ < p class = "text-gray-600 mb-4 " >
341+ Fires an event when an item is selected. No form integration needed.
342+ </ p >
343+ < . odyssey_select
344+ id = "standalone-category-picker "
345+ items = { @ sample_categories }
346+ on_select = { :item_selected }
347+ title = "Pick a Category "
348+ color_field = { :color }
349+ />
350+ < p class = "text-sm text-gray-600 mt-2 " >
351+ Selected: < strong > { if @ standalone_select_item , do: @ standalone_select_item . name , else: "None" } </ strong >
352+ </ p >
353+ </ div >
354+
355+ < div >
356+ < h3 class = "text-lg font-medium mb-2 " > Standalone Select with Label</ h3 >
357+ < . odyssey_select
358+ id = "labeled-category-picker "
359+ items = { @ sample_categories }
360+ on_select = { :item_selected }
361+ title = "Pick a Category "
362+ color_field = { :color }
363+ label = "Category "
364+ />
365+ </ div >
366+
367+ < div >
368+ < h3 class = "text-lg font-medium mb-2 " > Select without Colors</ h3 >
369+ < . odyssey_select
370+ id = "no-color-picker "
371+ items = { @ sample_tags }
372+ on_select = { :item_selected }
373+ title = "Pick a Tag "
374+ />
375+ </ div >
376+ </ div >
377+ </ section >
378+
379+ < . odyssey_divider />
380+
323381 <!-- Navigation Section -->
324382 < section >
325383 < h2 class = "text-2xl font-semibold mb-6 " > Navigation Components</ h2 >
@@ -580,6 +638,52 @@ defmodule DemoWeb.OdysseyShowcaseLive do
580638
581639 < . odyssey_divider />
582640
641+ < div >
642+ < h3 class = "text-lg font-medium mb-4 " > Select Dropdown (Form-Integrated)</ h3 >
643+ < p class = "text-gray-600 mb-4 " >
644+ The same odyssey_select component, but with a form field binding.
645+ Automatically tracks selected state and syncs with the form.
646+ </ p >
647+
648+ < . form for = { @ form } phx-change = "validate " id = "select-form " class = "space-y-6 " >
649+ < div >
650+ < h4 class = "text-base font-medium mb-2 " > Single Select</ h4 >
651+ < p class = "text-gray-500 text-sm mb-2 " >
652+ Selecting an item closes the dropdown and shows the selection in the button.
653+ </ p >
654+ < . odyssey_select
655+ field = { @ form [ :category_id ] }
656+ items = { @ sample_categories }
657+ title = "Select Category "
658+ color_field = { :color }
659+ label = "Category "
660+ />
661+ < p class = "text-sm text-gray-500 mt-1 " >
662+ Form value: < strong > { @ form_data [ "category_id" ] || "(empty)" } </ strong >
663+ </ p >
664+ </ div >
665+
666+ < div >
667+ < h4 class = "text-base font-medium mb-2 " > Multiple Select</ h4 >
668+ < p class = "text-gray-500 text-sm mb-2 " >
669+ Clicking items toggles them on/off. Dropdown stays open for further selections.
670+ </ p >
671+ < . odyssey_select
672+ field = { @ form [ :tag_ids ] }
673+ items = { @ sample_tags }
674+ title = "Select Tags "
675+ multiple = { true }
676+ label = "Tags "
677+ />
678+ < p class = "text-sm text-gray-500 mt-1 " >
679+ Form value: < strong > { @ form_data [ "tag_ids" ] || "(empty)" } </ strong >
680+ </ p >
681+ </ div >
682+ </ . form >
683+ </ div >
684+
685+ < . odyssey_divider />
686+
583687 < div >
584688 < h3 class = "text-lg font-medium mb-4 " > Activity Picker Component</ h3 >
585689 < p class = "text-gray-600 mb-4 " >
@@ -652,6 +756,27 @@ defmodule DemoWeb.OdysseyShowcaseLive do
652756 ]
653757 end
654758
759+ defp sample_categories do
760+ [
761+ % { id: "cat_1" , name: "Food & Drink" , color: "#F59E0B" } ,
762+ % { id: "cat_2" , name: "Outdoor Adventures" , color: "#10B981" } ,
763+ % { id: "cat_3" , name: "Arts & Culture" , color: "#8B5CF6" } ,
764+ % { id: "cat_4" , name: "Water Sports" , color: "#3B82F6" } ,
765+ % { id: "cat_5" , name: "Nightlife" , color: "#EC4899" }
766+ ]
767+ end
768+
769+ defp sample_tags do
770+ [
771+ % { id: "tag_1" , name: "Family Friendly" } ,
772+ % { id: "tag_2" , name: "Bestseller" } ,
773+ % { id: "tag_3" , name: "New" } ,
774+ % { id: "tag_4" , name: "Seasonal" } ,
775+ % { id: "tag_5" , name: "Premium" } ,
776+ % { id: "tag_6" , name: "Group Discount" }
777+ ]
778+ end
779+
655780 defp sample_activities do
656781 [
657782 % {
0 commit comments