diff --git a/lib/ruby_ui/toggle/toggle.rb b/lib/ruby_ui/toggle/toggle.rb new file mode 100644 index 00000000..5681914a --- /dev/null +++ b/lib/ruby_ui/toggle/toggle.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module RubyUI + class Toggle < Base + SIZES = { + default: "h-10 px-3 min-w-10", + sm: "h-9 px-2.5 min-w-9", + lg: "h-11 px-5 min-w-11", + } + + BACKGROUNDS = { + default: "bg-transparent", + outline: "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground", + } + + def initialize(variant: :default, size: :default, **args) + @variant = variant + @size = size + super(**args) + end + + def view_template(&) + button(**attrs, &) + end + + private + + def default_attrs + { + class: [ + "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2", + SIZES[@size], + BACKGROUNDS[@variant], + ], + aria_pressed: "false", + data_state: "off", + data: { + controller: "ruby-ui--toggle", + ruby_ui__toggle_target: "button", + action: "click->ruby-ui--toggle#changeState" + } + } + end + end +end diff --git a/lib/ruby_ui/toggle/toggle_controller.js b/lib/ruby_ui/toggle/toggle_controller.js new file mode 100644 index 00000000..3dfa70a1 --- /dev/null +++ b/lib/ruby_ui/toggle/toggle_controller.js @@ -0,0 +1,15 @@ +import { Controller } from "@hotwired/stimulus"; + +// Connects to data-controller="ruby-ui--toggle" +export default class extends Controller { + static targets = ["button"]; + + changeState() { + const currentState = this.buttonTarget.dataset.state; + const newState = currentState === "on" ? "off" : "on"; + + this.buttonTarget.dataset.state = newState; + + this.buttonTarget.setAttribute("aria-pressed", newState === "on" ? "true" : "false"); + } +} diff --git a/test/ruby_ui/toggle_test.rb b/test/ruby_ui/toggle_test.rb new file mode 100644 index 00000000..e820761b --- /dev/null +++ b/test/ruby_ui/toggle_test.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "test_helper" + +class RubyUI::ToggleTest < ComponentTest + def test_render_with_default_variant_and_size + output = phlex do + RubyUI.Toggle { "Default Toggle" } + end + + assert_match(/Default Toggle/, output) + assert_match(/data-state="off"/, output) + assert_match(/data-controller="ruby-ui--toggle"/, output) + assert_match(/h-10 px-3 min-w-10/, output) + assert_match(/bg-transparent/, output) + end + + def test_render_with_custom_variant_and_size + output = phlex do + RubyUI.Toggle(variant: :outline, size: :lg) { "Custom Toggle" } + end + + assert_match(/Custom Toggle/, output) + assert_match(/data-state="off"/, output) + assert_match(/h-11 px-5 min-w-11/, output) + assert_match(/border border-input bg-transparent hover:bg-accent hover:text-accent-foreground/, output) + end +end