diff --git a/.gitignore b/.gitignore index aa5a7bc86..9b8b985ed 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ CLAUDE.local.md +specs/ diff --git a/docs/app/components/shared/components_list.rb b/docs/app/components/shared/components_list.rb index 9f2775c1b..c46e8f9f8 100644 --- a/docs/app/components/shared/components_list.rb +++ b/docs/app/components/shared/components_list.rb @@ -50,6 +50,8 @@ def components {name: "Tabs", path: docs_tabs_path}, {name: "Textarea", path: docs_textarea_path}, {name: "Theme Toggle", path: docs_theme_toggle_path}, + {name: "Toggle", path: docs_toggle_path}, + {name: "Toggle Group", path: docs_toggle_group_path}, {name: "Tooltip", path: docs_tooltip_path}, {name: "Typography", path: docs_typography_path} ] diff --git a/docs/app/components/shared/navbar.rb b/docs/app/components/shared/navbar.rb index c8d45178e..513d0d6f7 100644 --- a/docs/app/components/shared/navbar.rb +++ b/docs/app/components/shared/navbar.rb @@ -34,37 +34,29 @@ def view_template def dark_mode_toggle ThemeToggle do - SetLightMode do - Button(variant: :ghost, icon: true) do - svg( - xmlns: "http://www.w3.org/2000/svg", - viewbox: "0 0 24 24", - fill: "currentColor", - class: "w-5 h-5" - ) do |s| - s.path( - d: - "M12 2.25a.75.75 0 01.75.75v2.25a.75.75 0 01-1.5 0V3a.75.75 0 01.75-.75zM7.5 12a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM18.894 6.166a.75.75 0 00-1.06-1.06l-1.591 1.59a.75.75 0 101.06 1.061l1.591-1.59zM21.75 12a.75.75 0 01-.75.75h-2.25a.75.75 0 010-1.5H21a.75.75 0 01.75.75zM17.834 18.894a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 10-1.061 1.06l1.59 1.591zM12 18a.75.75 0 01.75.75V21a.75.75 0 01-1.5 0v-2.25A.75.75 0 0112 18zM7.758 17.303a.75.75 0 00-1.061-1.06l-1.591 1.59a.75.75 0 001.06 1.061l1.591-1.59zM6 12a.75.75 0 01-.75.75H3a.75.75 0 010-1.5h2.25A.75.75 0 016 12zM6.697 7.757a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 00-1.061 1.06l1.59 1.591z" - ) - end - end + # Sun (visible in light mode) + svg( + xmlns: "http://www.w3.org/2000/svg", + viewbox: "0 0 24 24", + fill: "currentColor", + class: "w-5 h-5 dark:hidden" + ) do |s| + s.path( + d: "M12 2.25a.75.75 0 01.75.75v2.25a.75.75 0 01-1.5 0V3a.75.75 0 01.75-.75zM7.5 12a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM18.894 6.166a.75.75 0 00-1.06-1.06l-1.591 1.59a.75.75 0 101.06 1.061l1.591-1.59zM21.75 12a.75.75 0 01-.75.75h-2.25a.75.75 0 010-1.5H21a.75.75 0 01.75.75zM17.834 18.894a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 10-1.061 1.06l1.59 1.591zM12 18a.75.75 0 01.75.75V21a.75.75 0 01-1.5 0v-2.25A.75.75 0 0112 18zM7.758 17.303a.75.75 0 00-1.061-1.06l-1.591 1.59a.75.75 0 001.06 1.061l1.591-1.59zM6 12a.75.75 0 01-.75.75H3a.75.75 0 010-1.5h2.25A.75.75 0 016 12zM6.697 7.757a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 00-1.061 1.06l1.59 1.591z" + ) end - SetDarkMode do - Button(variant: :ghost, icon: true) do - svg( - xmlns: "http://www.w3.org/2000/svg", - viewbox: "0 0 24 24", - fill: "currentColor", - class: "w-4 h-4" - ) do |s| - s.path( - fill_rule: "evenodd", - d: - "M9.528 1.718a.75.75 0 01.162.819A8.97 8.97 0 009 6a9 9 0 009 9 8.97 8.97 0 003.463-.69.75.75 0 01.981.98 10.503 10.503 0 01-9.694 6.46c-5.799 0-10.5-4.701-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 01.818.162z", - clip_rule: "evenodd" - ) - end - end + # Moon (visible in dark mode) + svg( + xmlns: "http://www.w3.org/2000/svg", + viewbox: "0 0 24 24", + fill: "currentColor", + class: "w-5 h-5 hidden dark:inline-block" + ) do |s| + s.path( + fill_rule: "evenodd", + d: "M9.528 1.718a.75.75 0 01.162.819A8.97 8.97 0 009 6a9 9 0 009 9 8.97 8.97 0 003.463-.69.75.75 0 01.981.98 10.503 10.503 0 01-9.694 6.46c-5.799 0-10.5-4.701-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 01.818.162z", + clip_rule: "evenodd" + ) end end end diff --git a/docs/app/controllers/docs_controller.rb b/docs/app/controllers/docs_controller.rb index f60ca1237..20b70c066 100644 --- a/docs/app/controllers/docs_controller.rb +++ b/docs/app/controllers/docs_controller.rb @@ -222,6 +222,14 @@ def theme_toggle render Views::Docs::ThemeToggle.new end + def toggle + render Views::Docs::Toggle.new + end + + def toggle_group + render Views::Docs::ToggleGroup.new + end + def tooltip render Views::Docs::Tooltip.new end diff --git a/docs/app/javascript/controllers/index.js b/docs/app/javascript/controllers/index.js index e68815bd9..3fe2204d6 100644 --- a/docs/app/javascript/controllers/index.js +++ b/docs/app/javascript/controllers/index.js @@ -43,12 +43,12 @@ application.register("ruby-ui--command", RubyUi__CommandController) import RubyUi__ContextMenuController from "./ruby_ui/context_menu_controller" application.register("ruby-ui--context-menu", RubyUi__ContextMenuController) -import RubyUi__DataTableController from "./ruby_ui/data_table_controller" -application.register("ruby-ui--data-table", RubyUi__DataTableController) - import RubyUi__DataTableColumnVisibilityController from "./ruby_ui/data_table_column_visibility_controller" application.register("ruby-ui--data-table-column-visibility", RubyUi__DataTableColumnVisibilityController) +import RubyUi__DataTableController from "./ruby_ui/data_table_controller" +application.register("ruby-ui--data-table", RubyUi__DataTableController) + import RubyUi__DataTableSearchController from "./ruby_ui/data_table_search_controller" application.register("ruby-ui--data-table-search", RubyUi__DataTableSearchController) @@ -91,6 +91,12 @@ application.register("ruby-ui--tabs", RubyUi__TabsController) import RubyUi__ThemeToggleController from "./ruby_ui/theme_toggle_controller" application.register("ruby-ui--theme-toggle", RubyUi__ThemeToggleController) +import RubyUi__ToggleController from "./ruby_ui/toggle_controller" +application.register("ruby-ui--toggle", RubyUi__ToggleController) + +import RubyUi__ToggleGroupController from "./ruby_ui/toggle_group_controller" +application.register("ruby-ui--toggle-group", RubyUi__ToggleGroupController) + import RubyUi__TooltipController from "./ruby_ui/tooltip_controller" application.register("ruby-ui--tooltip", RubyUi__TooltipController) diff --git a/docs/app/javascript/controllers/ruby_ui/theme_toggle_controller.js b/docs/app/javascript/controllers/ruby_ui/theme_toggle_controller.js index 01b3b24b7..b1eb97f78 100644 --- a/docs/app/javascript/controllers/ruby_ui/theme_toggle_controller.js +++ b/docs/app/javascript/controllers/ruby_ui/theme_toggle_controller.js @@ -1,30 +1,38 @@ import { Controller } from "@hotwired/stimulus" +// Connects to data-controller="ruby-ui--theme-toggle" +// Sits on the same wrapper as ruby-ui--toggle. Listens for the toggle's +// ruby-ui--toggle:change event. pressed = dark mode. export default class extends Controller { - initialize() { - this.setTheme() + connect() { + this.applyTheme(this.currentTheme()) } - setTheme() { - // On page load or when changing themes, best to add inline in `head` to avoid FOUC - if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) { - document.documentElement.classList.add('dark') - document.documentElement.classList.remove('light') - } else { - document.documentElement.classList.remove('dark') - document.documentElement.classList.add('light') - } + apply(event) { + const pressed = event.detail?.pressed + const theme = pressed ? "dark" : "light" + localStorage.theme = theme + this.applyTheme(theme) } - setLightTheme() { - // Whenever the user explicitly chooses light mode - localStorage.theme = 'light' - this.setTheme() + currentTheme() { + if (localStorage.theme === "dark") return "dark" + if (localStorage.theme === "light") return "light" + return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" } - setDarkTheme() { - // Whenever the user explicitly chooses dark mode - localStorage.theme = 'dark' - this.setTheme() + applyTheme(theme) { + const html = document.documentElement + if (theme === "dark") { + html.classList.add("dark") + html.classList.remove("light") + } else { + html.classList.add("light") + html.classList.remove("dark") + } + // Flip the sibling Toggle controller's pressed value; it will propagate + // aria-pressed / data-state to the button target. + const dark = theme === "dark" + this.element.setAttribute("data-ruby-ui--toggle-pressed-value", dark ? "true" : "false") } } diff --git a/docs/app/javascript/controllers/ruby_ui/toggle_controller.js b/docs/app/javascript/controllers/ruby_ui/toggle_controller.js new file mode 100644 index 000000000..259488760 --- /dev/null +++ b/docs/app/javascript/controllers/ruby_ui/toggle_controller.js @@ -0,0 +1,33 @@ +import { Controller } from "@hotwired/stimulus" + +// Connects to data-controller="ruby-ui--toggle" +// Sits on a wrapper element; the visible