diff --git a/docs/app/views/docs/date_picker.rb b/docs/app/views/docs/date_picker.rb index c50ece0c..61b37497 100644 --- a/docs/app/views/docs/date_picker.rb +++ b/docs/app/views/docs/date_picker.rb @@ -11,19 +11,7 @@ def view_template render Docs::VisualCodeExample.new(title: "Single Date", context: self) do <<~RUBY - div(class: 'space-y-4 w-[260px]') do - Popover(options: { trigger: 'click' }) do - PopoverTrigger(class: 'w-full') do - div(class: 'grid w-full max-w-sm items-center gap-1.5') do - label(for: "date") { "Select a date" } - Input(type: 'string', placeholder: "Select a date", class: 'rounded-md border shadow', id: 'date', data_controller: 'ruby-ui--calendar-input') - end - end - PopoverContent do - Calendar(input_id: '#date') - end - end - end + DatePicker(id: "date") RUBY end diff --git a/gem/lib/generators/ruby_ui/dependencies.yml b/gem/lib/generators/ruby_ui/dependencies.yml index 0abe0a27..ea1c60bc 100644 --- a/gem/lib/generators/ruby_ui/dependencies.yml +++ b/gem/lib/generators/ruby_ui/dependencies.yml @@ -52,6 +52,12 @@ context_menu: js_packages: - "tippy.js" +date_picker: + components: + - "Input" + - "Popover" + - "Calendar" + data_table: components: - "Button" diff --git a/gem/lib/ruby_ui/date_picker/date_picker.rb b/gem/lib/ruby_ui/date_picker/date_picker.rb new file mode 100644 index 00000000..08c1ed6c --- /dev/null +++ b/gem/lib/ruby_ui/date_picker/date_picker.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +require "securerandom" + +module RubyUI + class DatePicker < Base + def initialize( + id: nil, + name: nil, + label: "Select a date", + value: nil, + placeholder: "Select a date", + selected_date: value, + date_format: "yyyy-MM-dd", + popover_options: {}, + input_attrs: {}, + calendar_attrs: {}, + trigger_attrs: {}, + content_attrs: {}, + **attrs + ) + @id = id || "date-picker-#{SecureRandom.hex(4)}" + @name = name + @label = label + @value = value || selected_date&.to_s + @placeholder = placeholder + @selected_date = selected_date + @date_format = date_format + @popover_options = {trigger: "click"}.merge(popover_options) + @input_attrs = input_attrs + @calendar_attrs = calendar_attrs + @trigger_attrs = trigger_attrs + @content_attrs = content_attrs + super(**attrs) + end + + def view_template + div(**attrs) do + RubyUI.Popover(options: @popover_options) do + RubyUI.PopoverTrigger(**trigger_attrs) do + div(class: "grid w-full max-w-sm items-center gap-1.5") do + label(for: @id) { @label } if @label + RubyUI.Input(**input_attrs) + end + end + RubyUI.PopoverContent(**content_attrs) do + RubyUI.Calendar(input_id: "##{@id}", selected_date: @selected_date, date_format: @date_format, **calendar_attrs) + end + end + end + end + + private + + def default_attrs + { + class: "space-y-4 w-[260px]" + } + end + + def trigger_attrs + mix({class: "w-full"}, @trigger_attrs) + end + + def input_attrs + mix({ + type: "string", + placeholder: @placeholder, + id: @id, + name: @name, + value: @value, + data_controller: "ruby-ui--calendar-input", + class: "rounded-md border shadow" + }.compact, @input_attrs) + end + + def calendar_attrs + mix({}, @calendar_attrs) + end + + def content_attrs + mix({}, @content_attrs) + end + end +end diff --git a/gem/lib/ruby_ui/date_picker/date_picker_docs.rb b/gem/lib/ruby_ui/date_picker/date_picker_docs.rb new file mode 100644 index 00000000..61b37497 --- /dev/null +++ b/gem/lib/ruby_ui/date_picker/date_picker_docs.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class Views::Docs::DatePicker < Views::Base + def view_template + component = "DatePicker" + + div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do + render Docs::Header.new(title: "Date Picker", description: "A date picker component with input.") + + Heading(level: 2) { "Usage" } + + render Docs::VisualCodeExample.new(title: "Single Date", context: self) do + <<~RUBY + DatePicker(id: "date") + RUBY + end + + render Components::ComponentSetup::Tabs.new(component_name: component) + + render Docs::ComponentsTable.new(component_files(component)) + end + end +end diff --git a/gem/test/generators/component_generator_test.rb b/gem/test/generators/component_generator_test.rb index 93d1c15b..950190bd 100644 --- a/gem/test/generators/component_generator_test.rb +++ b/gem/test/generators/component_generator_test.rb @@ -3,6 +3,7 @@ require "test_helper" require "fileutils" require "tmpdir" +require "yaml" # Tests the file filtering logic used in ComponentGenerator#components_file_paths # to ensure documentation files (*_docs.rb) are excluded from component generation. @@ -67,4 +68,11 @@ def test_includes_docs_file_when_with_docs_is_true assert_includes file_names, "link_docs.rb" end end + + def test_date_picker_installs_composed_components + dependencies_path = File.expand_path("../../lib/generators/ruby_ui/dependencies.yml", __dir__) + dependencies = YAML.load_file(dependencies_path) + + assert_equal ["Input", "Popover", "Calendar"], dependencies.fetch("date_picker").fetch("components") + end end diff --git a/gem/test/ruby_ui/date_picker_test.rb b/gem/test/ruby_ui/date_picker_test.rb new file mode 100644 index 00000000..1ddb019e --- /dev/null +++ b/gem/test/ruby_ui/date_picker_test.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require "test_helper" + +class RubyUI::DatePickerTest < ComponentTest + def test_render + output = phlex do + RubyUI.DatePicker(id: "event-date", name: "event[date]", value: "2026-05-15") + end + + assert_match(/