diff --git a/lib/phlexy_ui.rb b/lib/phlexy_ui.rb index 5ddbe74..24d655b 100644 --- a/lib/phlexy_ui.rb +++ b/lib/phlexy_ui.rb @@ -17,6 +17,7 @@ module PhlexyUI autoload :Tabs, "phlexy_ui/tabs" autoload :Drawer, "phlexy_ui/drawer" autoload :Dropdown, "phlexy_ui/dropdown" + autoload :Menu, "phlexy_ui/menu" end loader.eager_load diff --git a/lib/phlexy_ui/menu.rb b/lib/phlexy_ui/menu.rb new file mode 100644 index 0000000..bf74a03 --- /dev/null +++ b/lib/phlexy_ui/menu.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module PhlexyUI + class Menu < Base + def view_template(&) + generate_classes!( + component_html_class: :menu, + modifiers_map: MENU_MODIFIERS_MAP, + base_modifiers:, + options: + ).then do |classes| + ul(class: classes, **options, &) + end + end + + def title(*, **, &) + li(class: "menu-title", **, &) + end + + def item(*base_modifiers, **, &) + generate_classes!( + modifiers_map: MENU_ITEM_MODIFIERS_MAP, + base_modifiers:, + options: + ).then do |classes| + li(class: classes, &) + end + end + + def submenu(*, **, &) + render SubMenu.new(*, **, &) + end + + private + + MENU_MODIFIERS_MAP = { + xs: "menu-xs", + sm: "menu-sm", + md: "menu-md", + lg: "menu-lg", + vertical: "menu-vertical", + horizontal: "menu-horizontal" + }.freeze + + MENU_ITEM_MODIFIERS_MAP = { + disabled: "disabled", + active: "active", + focus: "focus" + } + end +end diff --git a/lib/phlexy_ui/menu/sub_menu.rb b/lib/phlexy_ui/menu/sub_menu.rb new file mode 100644 index 0000000..9a26fef --- /dev/null +++ b/lib/phlexy_ui/menu/sub_menu.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module PhlexyUI + class Menu + # @private + class SubMenu < Base + include Phlex::DeferredRender + + def initialize(*, **) + super + @items ||= [] + end + + def view_template(&) + attributes = ATTRIBUTES_MAP.select do |key| + base_modifiers.include?(key) + end + + details(**attributes) do + if @subtitle + summary do + render @subtitle + end + end + + if @items.any? + ul do + @items.each do |item| + li do + render item + end + end + end + end + end + end + + def subtitle(&block) + @subtitle = block + end + + def item(&block) + @items << block + end + + def submenu(*, **, &) + @items << self.class.new(*, **, &) + end + + private + + ATTRIBUTES_MAP = { + open: true + }.freeze + end + end +end diff --git a/spec/lib/phlexy_ui/menu_spec.rb b/spec/lib/phlexy_ui/menu_spec.rb new file mode 100644 index 0000000..0df3990 --- /dev/null +++ b/spec/lib/phlexy_ui/menu_spec.rb @@ -0,0 +1,92 @@ +require "spec_helper" + +describe PhlexyUI::Dropdown do + subject(:output) { render described_class.new } + + describe "rendering a full menu" do + let(:component) do + Class.new(Phlex::HTML) do + def view_template(&) + render PhlexyUI::Menu.new(:xs) do |menu| + menu.title do + "My Menu" + end + + menu.item do + "Item 1" + end + + menu.item(:disabled) do + "Item 2" + end + + menu.item(:active) do + "Item 3" + end + + menu.item(:focus) do + "Item 4" + end + + menu.item do + menu.submenu do |submenu_1| + submenu_1.subtitle do + "Parent 1" + end + + submenu_1.item do + "Child 1" + end + + submenu_1.submenu(:open) do |submenu_2| + submenu_2.subtitle do + "Parent 2" + end + + submenu_2.item do + "Child 2" + end + end + end + end + end + end + end + end + + subject(:output) do + render component.new + end + + it "is expected to match the formatted HTML" do + expected_html = html <<~HTML + <ul class="menu menu-xs"> + <li class="menu-title">My Menu</li> + <li>Item 1</li> + <li class="disabled">Item 2</li> + <li class="active">Item 3</li> + <li class="focus">Item 4</li> + <li> + <details> + <summary>Parent 1</summary> + + <ul> + <li>Child 1</li> + <li> + <details open> + <summary>Parent 2</summary> + <ul> + <li>Child 2</li> + </ul> + </details> + </li> + </ul> + </details> + </li> + </ul> + HTML + + is_expected.to eq(expected_html) + end + end +end