diff --git a/index.js b/index.js index df27c1b..b30ac87 100644 --- a/index.js +++ b/index.js @@ -15,6 +15,7 @@ import ThemeToggleController from "./lib/rbui/theme_toggle/theme_toggle_controll import TooltipController from "./lib/rbui/tooltip/tooltip_controller"; import SelectController from "./lib/rbui/select/select_controller"; import SelectItemController from "./lib/rbui/select/select_item_controller"; +import AccordionController from "./lib/rbui/accordion/accordion_controller"; // Register all controllers application.register("rbui--combobox", ComboboxController); @@ -24,6 +25,7 @@ application.register("rbui--theme-toggle", ThemeToggleController); application.register("rbui--tooltip", TooltipController); application.register("rbui--select", SelectController); application.register("rbui--select-item", SelectItemController); +application.register("rbui--accordion", AccordionController); // Export all controllers so user of npm package can lazy load controllers export { default as ComboboxController } from "./lib/rbui/combobox/combobox_controller"; @@ -33,6 +35,7 @@ export { default as ThemeToggleController } from "./lib/rbui/theme_toggle/theme_ export { default as TooltipController } from "./lib/rbui/tooltip/tooltip_controller"; export { default as SelectController } from "./lib/rbui/select/select_controller"; export { default as SelectItemController } from "./lib/rbui/select/select_item_controller"; +export { default as AccordionController } from "./lib/rbui/accordion/accordion_controller"; // Export application export { application }; diff --git a/lib/phlex_ui.rb b/lib/phlex_ui.rb index 7b5dc18..204b074 100644 --- a/lib/phlex_ui.rb +++ b/lib/phlex_ui.rb @@ -49,6 +49,7 @@ loader.collapse("#{__dir__}/rbui/theme_toggle") loader.collapse("#{__dir__}/rbui/tooltip") loader.collapse("#{__dir__}/rbui/select") +loader.collapse("#{__dir__}/rbui/accordion") loader.setup # ready! diff --git a/lib/phlex_ui/accordion.rb b/lib/rbui/accordion/accordion.rb similarity index 93% rename from lib/phlex_ui/accordion.rb rename to lib/rbui/accordion/accordion.rb index af4842c..70ce830 100644 --- a/lib/phlex_ui/accordion.rb +++ b/lib/rbui/accordion/accordion.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module PhlexUI +module RBUI class Accordion < Base def view_template(&) div(**attrs, &) diff --git a/lib/phlex_ui/accordion/accordion_content.rb b/lib/rbui/accordion/accordion_content.rb similarity index 83% rename from lib/phlex_ui/accordion/accordion_content.rb rename to lib/rbui/accordion/accordion_content.rb index 24875d2..a8b168c 100644 --- a/lib/phlex_ui/accordion/accordion_content.rb +++ b/lib/rbui/accordion/accordion_content.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module PhlexUI +module RBUI class AccordionContent < Base def view_template(&) div(**attrs, &) @@ -11,7 +11,7 @@ def view_template(&) def default_attrs { data: { - accordion_target: "content" + rbui__accordion_target: "content" }, class: "overflow-y-hidden", style: "height: 0px;" diff --git a/lib/rbui/accordion/accordion_controller.js b/lib/rbui/accordion/accordion_controller.js new file mode 100644 index 0000000..d646cef --- /dev/null +++ b/lib/rbui/accordion/accordion_controller.js @@ -0,0 +1,81 @@ +import { Controller } from "@hotwired/stimulus" +import { animate } from "motion" + +// Connects to data-controller="rbui--accordion" +export default class extends Controller { + static targets = ['icon', 'content'] + static values = { + open: { + type: Boolean, + default: false, + }, + animationDuration: { + type: Number, + default: 0.15, // Default animation duration (in seconds) + }, + animationEasing: { + type: String, + default: 'ease-in-out', // Default animation easing + }, + rotateIcon: { + type: Number, + default: 180, // Default icon rotation (in degrees) + }, + } + + connect() { + // Set the initial state of the accordion + let originalAnimationDuration = this.animationDurationValue + this.animationDurationValue = 0 + this.openValue ? this.open() : this.close() + this.animationDurationValue = originalAnimationDuration + } + + // Toggle the 'open' value + toggle() { + this.openValue = !this.openValue + } + + // Handle changes in the 'open' value + openValueChanged(isOpen, wasOpen) { + if (isOpen) { + this.open() + } else { + this.close() + } + } + + // Open the accordion content + open() { + if (this.hasContentTarget) { + this.revealContent() + this.hasIconTarget && this.rotateIcon() + this.openValue = true + } + } + + // Close the accordion content + close() { + if (this.hasContentTarget) { + this.hideContent() + this.hasIconTarget && this.rotateIcon() + this.openValue = false + } + } + + // Reveal the accordion content with animation + revealContent() { + const contentHeight = this.contentTarget.scrollHeight; + animate(this.contentTarget, { height: `${contentHeight}px` }, { duration: this.animationDurationValue, easing: this.animationEasingValue }) + } + + // Hide the accordion content with animation + hideContent() { + animate(this.contentTarget, { height: 0 }, { duration: this.animationDurationValue, easing: this.animationEasingValue }) + } + + // Rotate the accordion icon 180deg using animate function + rotateIcon() { + animate(this.iconTarget, { rotate: `${this.openValue ? this.rotateIconValue : 0}deg` }) + } +} diff --git a/lib/phlex_ui/accordion/accordion_default_content.rb b/lib/rbui/accordion/accordion_default_content.rb similarity index 93% rename from lib/phlex_ui/accordion/accordion_default_content.rb rename to lib/rbui/accordion/accordion_default_content.rb index f091ed2..c405d6b 100644 --- a/lib/phlex_ui/accordion/accordion_default_content.rb +++ b/lib/rbui/accordion/accordion_default_content.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module PhlexUI +module RBUI class AccordionDefaultContent < Base def view_template(&) div(**attrs, &) diff --git a/lib/phlex_ui/accordion/accordion_default_trigger.rb b/lib/rbui/accordion/accordion_default_trigger.rb similarity index 79% rename from lib/phlex_ui/accordion/accordion_default_trigger.rb rename to lib/rbui/accordion/accordion_default_trigger.rb index 866b694..968f6dc 100644 --- a/lib/phlex_ui/accordion/accordion_default_trigger.rb +++ b/lib/rbui/accordion/accordion_default_trigger.rb @@ -1,17 +1,17 @@ # frozen_string_literal: true -module PhlexUI +module RBUI class AccordionDefaultTrigger < Base def view_template(&block) div(class: "flex items-center justify-between w-full") do p(&block) - PhlexUI.AccordionIcon + RBUI.AccordionIcon end end def default_attrs { - data: {action: "click->accordion#toggle"}, + data: {action: "click->rbui--accordion#toggle"}, class: "w-full flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline" } end diff --git a/lib/phlex_ui/accordion/accordion_icon.rb b/lib/rbui/accordion/accordion_icon.rb similarity index 92% rename from lib/phlex_ui/accordion/accordion_icon.rb rename to lib/rbui/accordion/accordion_icon.rb index 1479ef1..5fbb5ac 100644 --- a/lib/phlex_ui/accordion/accordion_icon.rb +++ b/lib/rbui/accordion/accordion_icon.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module PhlexUI +module RBUI class AccordionIcon < Base def view_template(&block) span(**attrs) do @@ -31,7 +31,7 @@ def icon def default_attrs { class: "opacity-50", - data: {accordion_target: "icon"} + data: {rbui__accordion_target: "icon"} } end end diff --git a/lib/phlex_ui/accordion/accordion_item.rb b/lib/rbui/accordion/accordion_item.rb similarity index 70% rename from lib/phlex_ui/accordion/accordion_item.rb rename to lib/rbui/accordion/accordion_item.rb index 412aa9a..6dab750 100644 --- a/lib/phlex_ui/accordion/accordion_item.rb +++ b/lib/rbui/accordion/accordion_item.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module PhlexUI +module RBUI class AccordionItem < Base def initialize(open: false, rotate_icon: 180, **attrs) @open = open @@ -17,9 +17,9 @@ def view_template(&) def default_attrs { data: { - controller: "accordion", - accordion_open_value: @open, - accordion_rotate_icon_value: @rotate_icon + controller: "rbui--accordion", + rbui__accordion_open_value: @open, + rbui__accordion_rotate_icon_value: @rotate_icon }, class: "border-b" } diff --git a/lib/phlex_ui/accordion/accordion_trigger.rb b/lib/rbui/accordion/accordion_trigger.rb similarity index 81% rename from lib/phlex_ui/accordion/accordion_trigger.rb rename to lib/rbui/accordion/accordion_trigger.rb index 147d8e7..45a0abb 100644 --- a/lib/phlex_ui/accordion/accordion_trigger.rb +++ b/lib/rbui/accordion/accordion_trigger.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module PhlexUI +module RBUI class AccordionTrigger < Base def view_template(&) button(**attrs, &) @@ -8,7 +8,7 @@ def view_template(&) def default_attrs { - data: {action: "click->accordion#toggle"}, + data: {action: "click->rbui--accordion#toggle"}, class: "w-full flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline" } end diff --git a/test/phlex_ui/accordion_test.rb b/test/rbui/accordion_test.rb similarity index 61% rename from test/phlex_ui/accordion_test.rb rename to test/rbui/accordion_test.rb index b570d33..464caf6 100644 --- a/test/phlex_ui/accordion_test.rb +++ b/test/rbui/accordion_test.rb @@ -2,29 +2,29 @@ require "test_helper" -class PhlexUI::AccordionTest < Minitest::Test +class RBUI::AccordionTest < Minitest::Test include Phlex::Testing::ViewHelper def test_render_with_default_items output = phlex_context do - PhlexUI.Accordion do - PhlexUI.AccordionItem do - PhlexUI.AccordionDefaultTrigger { "Title" } - PhlexUI.AccordionDefaultContent { "Content" } + RBUI.Accordion do + RBUI.AccordionItem do + RBUI.AccordionDefaultTrigger { "Title" } + RBUI.AccordionDefaultContent { "Content" } end end end - assert_match(/