├── .editorconfig ├── .gitattributes ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .rspec ├── .ruby-version ├── .vscode └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Gemfile ├── Gemfile.lock ├── LICENSE.md ├── Procfile.dev ├── README.md ├── Rakefile ├── app ├── assets │ ├── builds │ │ └── .keep │ ├── config │ │ └── manifest.js │ ├── images │ │ └── .keep │ └── stylesheets │ │ ├── application.css │ │ ├── application.tailwind.css │ │ ├── lambda.light.css │ │ └── shadcn.css ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── components │ └── shadcn │ │ ├── form_builder.rb │ │ └── select_component.rb ├── controllers │ ├── application_controller.rb │ ├── components_controller.rb │ ├── concerns │ │ └── .keep │ ├── documentation_controller.rb │ └── users_controller.rb ├── helpers │ ├── application_helper.rb │ ├── components │ │ ├── accordion_helper.rb │ │ ├── alert_dialog_helper.rb │ │ ├── alert_helper.rb │ │ ├── badge_helper.rb │ │ ├── button_helper.rb │ │ ├── card_helper.rb │ │ ├── checkbox_helper.rb │ │ ├── collapsible_helper.rb │ │ ├── combobox_helper.rb │ │ ├── date_picker_helper.rb │ │ ├── dialog_helper.rb │ │ ├── dropdown_menu_helper.rb │ │ ├── dropzone_helper.rb │ │ ├── filter_helper.rb │ │ ├── forms_helper.rb │ │ ├── hover_card_helper.rb │ │ ├── input_helper.rb │ │ ├── label_helper.rb │ │ ├── list_helper.rb │ │ ├── popover_helper.rb │ │ ├── progress_helper.rb │ │ ├── select_helper.rb │ │ ├── separator_helper.rb │ │ ├── sheet_helper.rb │ │ ├── skeleton_helper.rb │ │ ├── slider_helper.rb │ │ ├── switch_helper.rb │ │ ├── table_helper.rb │ │ ├── tabs_helper.rb │ │ ├── textarea_helper.rb │ │ ├── toast_helper.rb │ │ ├── toggle_helper.rb │ │ └── tooltip_helper.rb │ ├── components_helper.rb │ ├── documentation_helper.rb │ └── examples_helper.rb ├── javascript │ ├── application.js │ ├── controllers │ │ ├── application.js │ │ ├── hello_controller.js │ │ ├── highlight_controller.js │ │ ├── index.js │ │ ├── theme_controller.js │ │ └── ui │ │ │ ├── accordion_controller.js │ │ │ ├── checkbox_controller.js │ │ │ ├── collapsible_controller.js │ │ │ ├── date-picker_controller.js │ │ │ ├── dialog_controller.js │ │ │ ├── dropdown_controller.js │ │ │ ├── dropzone_controller.js │ │ │ ├── filter_controller.js │ │ │ ├── hover-card_controller.js │ │ │ ├── popover_controller.js │ │ │ ├── select_controller.js │ │ │ ├── sheet_controller.js │ │ │ ├── slider_controller.js │ │ │ ├── switch_controller.js │ │ │ ├── tabs_controller.js │ │ │ ├── toast_controller.js │ │ │ ├── toggle_controller.js │ │ │ ├── tooltip_controller.js │ │ │ └── transition_controller.js │ └── utils │ │ ├── bodyScrollLock.js │ │ └── iso_date.js ├── jobs │ └── application_job.rb ├── mailers │ └── application_mailer.rb ├── models │ ├── application_record.rb │ ├── concerns │ │ └── .keep │ └── user.rb └── views │ ├── application │ └── index.html.erb │ ├── components │ └── ui │ │ ├── _accordion.html.erb │ │ ├── _alert.html.erb │ │ ├── _alert_dialog.html.erb │ │ ├── _button.html.erb │ │ ├── _card.html.erb │ │ ├── _checkbox.html.erb │ │ ├── _collapsible.html.erb │ │ ├── _combobox.html.erb │ │ ├── _command.html.erb │ │ ├── _date_picker.html.erb │ │ ├── _dialog.html.erb │ │ ├── _dropdown_menu.html.erb │ │ ├── _dropzone.html.erb │ │ ├── _filter.html.erb │ │ ├── _hover_card.html.erb │ │ ├── _input.html.erb │ │ ├── _label.html.erb │ │ ├── _list.html.erb │ │ ├── _popover.html.erb │ │ ├── _progress.html.erb │ │ ├── _sheet.html.erb │ │ ├── _skeleton.html.erb │ │ ├── _slider.html.erb │ │ ├── _switch.html.erb │ │ ├── _tabs.html.erb │ │ ├── _textarea.html.erb │ │ ├── _toast.html.erb │ │ ├── _toggle.html.erb │ │ ├── _tooltip.html.erb │ │ ├── shared │ │ ├── _backdrop.html.erb │ │ └── _menu_item.html.erb │ │ ├── svg │ │ └── _check.html.erb │ │ └── tabs │ │ ├── _panel.html.erb │ │ └── _tab.html.erb │ ├── documentation │ ├── about.html.md │ ├── generators.html.md │ ├── helpers.html.md │ ├── index.html.erb.bak │ ├── index.html.md │ ├── installation.html.md │ └── javascript.html.md │ ├── examples │ ├── authentication │ │ └── index.html.erb │ └── components │ │ ├── accordion.html.erb │ │ ├── accordion │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _block.html.erb │ │ │ ├── _description.html.erb │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── alert-dialog.html.erb │ │ ├── alert-dialog │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── alert.html.erb │ │ ├── alert │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _attention.erb │ │ │ ├── _destructive.erb │ │ │ ├── _info.erb │ │ │ ├── _no_icon.erb │ │ │ ├── _preview.erb │ │ │ ├── _success.erb │ │ │ └── _usage.erb │ │ ├── badge.html.erb │ │ ├── badge │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── button.html.erb │ │ ├── button │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── card.html.erb │ │ ├── card │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _form.erb │ │ │ ├── _notifications.erb │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── checkbox.html.erb │ │ ├── checkbox │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── collapsible.html.erb │ │ ├── collapsible │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── combobox.html.erb │ │ ├── combobox │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── date-picker.html.erb │ │ ├── date-picker │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── dialog.html.erb │ │ ├── dialog │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _form.erb │ │ │ ├── _notifications.erb │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── dropdown-menu.html.erb │ │ ├── dropdown-menu │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── dropzone.html.erb │ │ ├── dropzone │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── filter.html.erb │ │ ├── filter │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _icon.html.erb │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── forms.html.erb │ │ ├── forms │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── hover-card.html.erb │ │ ├── hover-card │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── input.html.erb │ │ ├── input │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _borderless.erb │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── label.html.erb │ │ ├── label │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── popover.html.erb │ │ ├── popover │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _form.erb │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── progress.html.erb │ │ ├── progress │ │ ├── _usage.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── select.html.erb │ │ ├── select │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── separator.html.erb │ │ ├── separator │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _fancy.erb │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── sheet.html.erb │ │ ├── sheet │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _mobile_menu.erb │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── skeleton.html.erb │ │ ├── skeleton │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── slider.html.erb │ │ ├── slider │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── switch.html.erb │ │ ├── switch │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── table.html.erb │ │ ├── table │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── tabs.html.erb │ │ ├── tabs │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _account.html.erb │ │ │ ├── _password.html.erb │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── textarea.html.erb │ │ ├── textarea │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── toast.html.erb │ │ ├── toast │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _destructive.erb │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── toggle.html.erb │ │ ├── toggle │ │ ├── _usage.html.erb │ │ └── code │ │ │ ├── _preview.erb │ │ │ └── _usage.erb │ │ ├── tooltip.html.erb │ │ └── tooltip │ │ ├── _usage.html.erb │ │ └── code │ │ ├── _preview.erb │ │ └── _usage.erb │ └── layouts │ ├── application.html.erb │ ├── component.html.erb │ ├── documentation.html.erb │ ├── documentation │ ├── _breadcrumb.html.erb │ ├── _component_header.html.erb │ ├── _examples.html.erb │ └── _preview.html.erb │ ├── mailer.html.erb │ ├── mailer.text.erb │ └── shared │ ├── _components.html.erb │ ├── _header.html.erb │ └── _sidebar.html.erb ├── bin ├── bundle ├── console ├── debug ├── dev ├── importmap ├── rails ├── rake └── setup ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── credentials.yml.enc ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── importmap.rb ├── initializers │ ├── assets.rb │ ├── content_security_policy.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── markdown.rb │ └── permissions_policy.rb ├── locales │ └── en.yml ├── puma.rb ├── render.yml ├── routes.rb ├── shadcn.tailwind.js ├── storage.yml └── tailwind.config.js ├── db └── seeds.rb ├── lib ├── assets │ └── .keep ├── components.json ├── generators │ └── shadcn-ui_generator.rb ├── shadcn-ui │ ├── shadcn-ui.rb │ └── version.rb └── tasks │ └── .keep ├── log └── .keep ├── package-lock.json ├── package.json ├── public ├── 404.html ├── 422.html ├── 500.html ├── accordion.png ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── og.jpg └── robots.txt ├── shadcn-ui.gemspec ├── sig └── shadcn-ui.rbs ├── spec ├── generators │ ├── alert_dialog_generator_spec.rb │ ├── dialog_generator_spec.rb │ └── shadcn-ui_generator_spec.rb ├── rails_helper.rb └── spec_helper.rb ├── storage └── .keep ├── tmp ├── .keep ├── pids │ └── .keep └── storage │ └── .keep └── vendor ├── .keep └── javascript └── .keep /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,json,yml}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # See https://git-scm.com/docs/gitattributes for more about git attribute files. 2 | 3 | # Mark the database schema as having been generated. 4 | db/schema.rb linguist-generated 5 | 6 | # Mark any vendored files as having been vendored. 7 | vendor/* linguist-vendored 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-* 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | /tmp/* 17 | !/log/.keep 18 | !/tmp/.keep 19 | 20 | # Ignore pidfiles, but keep the directory. 21 | /tmp/pids/* 22 | !/tmp/pids/ 23 | !/tmp/pids/.keep 24 | 25 | # Ignore uploaded files in development. 26 | /storage/* 27 | !/storage/.keep 28 | /tmp/storage/* 29 | !/tmp/storage/ 30 | !/tmp/storage/.keep 31 | 32 | /public/assets 33 | 34 | # Ignore master key for decrypting credentials and more. 35 | /config/master.key 36 | 37 | /app/assets/builds/* 38 | !/app/assets/builds/.keep 39 | node_modules 40 | /.bundle/ 41 | /.yardoc 42 | /_yardoc/ 43 | /coverage/ 44 | /pkg/ 45 | /spec/reports/ 46 | /tmp/ 47 | 48 | # rspec failure tracking 49 | .rspec_status 50 | bin/deploy 51 | .env 52 | # Rubymine 53 | /.idea/* 54 | 55 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-* 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | /tmp/* 17 | !/log/.keep 18 | !/tmp/.keep 19 | 20 | # Ignore pidfiles, but keep the directory. 21 | /tmp/pids/* 22 | !/tmp/pids/ 23 | !/tmp/pids/.keep 24 | 25 | # Ignore uploaded files in development. 26 | /storage/* 27 | !/storage/.keep 28 | /tmp/storage/* 29 | !/tmp/storage/ 30 | !/tmp/storage/.keep 31 | 32 | /public/assets 33 | 34 | # Ignore master key for decrypting credentials and more. 35 | /config/master.key 36 | 37 | /app/assets/builds/* 38 | !/app/assets/builds/.keep 39 | 40 | exa-taildash 41 | node_modules 42 | yarn-error.log 43 | node_modules 44 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "bracketSameLine": false, 5 | "printWidth": 100, 6 | "singleAttributePerLine": true, 7 | "proseWrap": "always", 8 | "overrides": [ 9 | { 10 | "files": "*.erb", 11 | "options": { 12 | "parser": "html" 13 | } 14 | }, 15 | { 16 | "files": "erb", 17 | "options": { 18 | "parser": "html" 19 | } 20 | }, 21 | { 22 | "files": "*.html.erb", 23 | "options": { 24 | "parser": "html" 25 | } 26 | }, 27 | { 28 | "files": "*.md.html", 29 | "options": { 30 | "parser": "markdown" 31 | } 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-3.2.2 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[html.erb]": { 3 | "editor.defaultFormatter": "aliariff.vscode-erb-beautify" 4 | }, 5 | "[erb]": { 6 | "editor.defaultFormatter": "manuelpuyol.erb-linter" 7 | }, 8 | "[rbs]": { 9 | "editor.defaultFormatter": "esbenp.prettier-vscode" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.0.1 / 2023-07-09 2 | 3 | ## Enhancements: 4 | 5 | - Began to add the gem structure for the gem. Pull request 6 | [#1](https://github.com/aviflombaum/shadcn-rails/pull/1) 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 shadcn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 6 | associated documentation files (the "Software"), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial 12 | portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 15 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 17 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /Procfile.dev: -------------------------------------------------------------------------------- 1 | web: bin/rails server -p 3000 2 | css: bin/rails tailwindcss:watch 3 | -------------------------------------------------------------------------------- /app/assets/builds/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/app/assets/builds/.keep -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | //= link_tree ../../javascript .js 4 | //= link_tree ../../../vendor/javascript .js 5 | //= link_tree ../builds 6 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.tailwind.css: -------------------------------------------------------------------------------- 1 | @import "shadcn.css"; 2 | 3 | @tailwind base; 4 | @tailwind components; 5 | @tailwind utilities; 6 | 7 | @layer base { 8 | .container { 9 | margin-left: auto; 10 | margin-right: auto; 11 | padding-left: 2rem; 12 | padding-right: 2rem; 13 | width: 100%; 14 | } 15 | 16 | :root { 17 | --pink: 301 98% 50% 0.88; 18 | } 19 | } 20 | 21 | article.documentation h1 { 22 | @apply scroll-m-20 text-4xl font-bold tracking-tight; 23 | } 24 | 25 | article.documentation p + h1 { 26 | @apply mt-12; 27 | } 28 | article.documentation h2 { 29 | @apply scroll-m-20 text-3xl font-bold tracking-tight my-8; 30 | } 31 | 32 | article.documentation h3 { 33 | @apply scroll-m-20 text-2xl font-bold tracking-tight my-4; 34 | } 35 | 36 | article.documentation h4 { 37 | @apply scroll-m-20 text-xl font-bold tracking-tight my-2; 38 | } 39 | 40 | article.documentation subtitle { 41 | @apply text-lg text-muted-foreground; 42 | } 43 | article.documentation p { 44 | @apply leading-7 [&:not(:first-child)]:mt-6; 45 | } 46 | 47 | article.documentation :where(code):not(:where([class~="not-prose"] *)):before { 48 | content: ""; 49 | } 50 | 51 | article.documentation :where(code):not(:where([class~="not-prose"] *)):after { 52 | content: ""; 53 | } 54 | 55 | article.documentation p a { 56 | @apply underline hover:text-pink; 57 | } 58 | 59 | article.documentation pre { 60 | @apply max-h-[650px] overflow-x-auto rounded-lg border bg-zinc-950 dark:bg-zinc-900 text-white py-4 px-8 rounded-lg my-4; 61 | } 62 | 63 | article.documentation p > code { 64 | @apply text-white bg-black p-1 rounded; 65 | } 66 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/components/shadcn/select_component.rb: -------------------------------------------------------------------------------- 1 | class Shadcn::SelectComponent 2 | include ComponentsHelper 3 | attr_reader :name, :selected, :view_context 4 | 5 | def initialize(name:, view_context:, selected: nil, **options, &block) 6 | @name = name 7 | @view_context = view_context 8 | @selected = selected 9 | @options = options 10 | @content = view_context.capture(self, &block) if block 11 | end 12 | 13 | def option(value:, label: nil, &block) 14 | content = label || view_context.capture(&block) 15 | option_options = {value: value} 16 | option_options[:selected] = "selected" if value == selected 17 | view_context.content_tag :option, content, option_options 18 | end 19 | 20 | def call 21 | view_context.content_tag :select, @content, name: name, class: tw("rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", @options[:class]) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | def index 3 | end 4 | 5 | def examples 6 | render "examples/authentication/index" 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/controllers/components_controller.rb: -------------------------------------------------------------------------------- 1 | class ComponentsController < ActionController::Base 2 | layout "component" 3 | 4 | def show 5 | @user = User.new # REFACTOR: For the forms example 6 | render "examples/components/#{params[:component]}" 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/documentation_controller.rb: -------------------------------------------------------------------------------- 1 | class DocumentationController < ActionController::Base 2 | layout "documentation" 3 | def index 4 | end 5 | 6 | def show 7 | render "documentation/#{params[:id]}" 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | layout "component" 3 | 4 | def create 5 | params[:component] = "forms" 6 | @user = User.new(user_params) 7 | if @user.valid? 8 | # Toast message 9 | else 10 | render "examples/components/forms", status: 422 11 | end 12 | end 13 | 14 | private 15 | 16 | def user_params 17 | params.require(:user).permit(:email, :password) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | def page_title 3 | @page_title = "" 4 | if request.path.include?("/docs/components") 5 | component_name = params[:component].to_s.titleize 6 | @page_title << "#{component_name} - " if component_name.present? 7 | end 8 | @page_title << "shadcn/ui on Rails" 9 | 10 | set_meta_tags( 11 | title: @page_title 12 | ) 13 | 14 | @page_title 15 | end 16 | 17 | def sidebar_link(text, path) 18 | classes = "group flex w-full items-center rounded-md border border-transparent px-2 py-1 hover:underline" 19 | classes << if request.path == path 20 | " text-foreground font-bold" 21 | else 22 | " text-muted-foreground" 23 | end 24 | link_to text, path, class: classes 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/helpers/components/accordion_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::AccordionHelper 2 | def accordion_title(&block) 3 | content_for :title, capture(&block), flush: true 4 | end 5 | 6 | def accordion_description(&block) 7 | content_for :description, capture(&block), flush: true 8 | end 9 | 10 | def render_accordion(title: nil, description: nil, &block) 11 | if title && !description 12 | content_for :description, capture(&block), flush: true 13 | elsif !title && !description 14 | capture(&block) 15 | end 16 | render "components/ui/accordion", title: title, description: description 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/helpers/components/alert_dialog_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::AlertDialogHelper 2 | def render_alert_dialog(**options, &block) 3 | content = capture(&block) if block 4 | render "components/ui/alert_dialog", content: content, **options 5 | end 6 | 7 | def alert_dialog_trigger(&block) 8 | content_for :alert_dialog_trigger, capture(&block), flush: true 9 | end 10 | 11 | def alert_dialog_content(&block) 12 | content_for :alert_dialog_content, capture(&block), flush: true 13 | end 14 | 15 | def alert_dialog_continue(&block) 16 | content_for :alert_dialog_continue, capture(&block), flush: true 17 | end 18 | 19 | def alert_dialog_cancel(&block) 20 | content_for :alert_dialog_cancel, capture(&block), flush: true 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/helpers/components/alert_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::AlertHelper 2 | def render_alert(title:, description: nil, variant: :default, icon: true, &block) 3 | alert_classes = case variant.to_sym 4 | when :default 5 | "[&>svg]:text-foreground bg-background text-foreground" 6 | when :error, :danger, :alert, :destructive 7 | "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive" 8 | when :success 9 | "border-success/50 text-success dark:border-success [&>svg]:text-success" 10 | when :info 11 | "border-info/50 text-info dark:border-info [&>svg]:text-info" 12 | when :attention 13 | "border-attention/50 text-attention dark:border-attention [&>svg]:text-attention" 14 | end 15 | content = (capture(&block) if block) || description 16 | render "components/ui/alert", title:, content:, alert_classes:, variant:, icon: 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/helpers/components/badge_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::BadgeHelper 2 | def render_badge(label = "", data: "", text: "", variant: :default, **options) 3 | badge_classes = " inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 " 4 | variant_classes = case variant.to_sym 5 | when :default 6 | ComponentsHelper::PRIMARY_CLASSES 7 | when :secondary 8 | ComponentsHelper::SECONDARY_CLASSES 9 | when :error, :danger, :alert, :destructive 10 | ComponentsHelper::DESTRUCTIVE_CLASSES 11 | when :outline 12 | ComponentsHelper::OUTLINE_CLASSES 13 | when :ghost 14 | ComponentsHelper::GHOST_CLASSES 15 | end 16 | badge_classes << " #{variant_classes}" 17 | text = label if label.present? 18 | 19 | content_tag :div, class: badge_classes do 20 | text 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/helpers/components/button_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::ButtonHelper 2 | def render_button(label = "", text: nil, variant: :default, as: :button, href: nil, data: {}, **options, &block) 3 | button_classes = " inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 h-10 px-4 py-2 " 4 | variant_classes = case variant.to_sym 5 | when :default 6 | " bg-primary text-primary-foreground hover:bg-primary/90 " 7 | when :secondary 8 | " bg-secondary text-secondary-foreground hover:bg-secondary/80 " 9 | when :error, :danger, :alert, :destructive 10 | " bg-destructive text-destructive-foreground hover:bg-destructive/90 " 11 | when :outline 12 | " border border-input bg-background hover:bg-accent hover:text-accent-foreground" 13 | when :ghost 14 | " hover:bg-accent hover:text-accent-foreground " 15 | end 16 | button_classes = tw(button_classes, variant_classes, options[:class]) 17 | 18 | text = label if label.present? 19 | text = capture(&block) if block 20 | render "components/ui/button", text:, button_classes:, as:, href:, data:, **options 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/helpers/components/card_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::CardHelper 2 | def render_card(title: nil, subtitle: nil, body: nil, footer: nil, **options, &block) 3 | render "components/ui/card", title: title, subtitle: subtitle, footer: footer, body: (block ? capture(&block) : body), block:, options: options 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/components/checkbox_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::CheckboxHelper 2 | def render_checkbox(label:, name:, **options) 3 | render "components/ui/checkbox", name: name, label: label, options: options 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/components/collapsible_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::CollapsibleHelper 2 | def collapsible_preview(&block) 3 | content_for :preview, capture(&block) if block 4 | end 5 | 6 | def collapsible_body(&block) 7 | content_for :body, capture(&block) if block 8 | end 9 | 10 | def render_collapsible(**options, &block) 11 | content = capture(&block) if block 12 | render "components/ui/collapsible", content: content, **options 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/helpers/components/combobox_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::ComboboxHelper 2 | def render_combobox(items, &block) 3 | content = capture(&block) if block 4 | render "components/ui/combobox", items:, content: 5 | end 6 | 7 | def combobox_trigger(&block) 8 | content_for :trigger, capture(&block) if block 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/helpers/components/date_picker_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::DatePickerHelper 2 | def render_date_picker(name:, id: nil, value: 'Pick a date', **options) 3 | render partial: "components/ui/date_picker", locals: { 4 | name:, 5 | value:, 6 | id:, 7 | options: options 8 | } 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/helpers/components/dialog_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::DialogHelper 2 | def render_dialog(**options, &block) 3 | content = capture(&block) if block 4 | render "components/ui/dialog", content: content, options: options 5 | end 6 | 7 | def dialog_trigger(&block) 8 | content_for :dialog_trigger, capture(&block), flush: true 9 | end 10 | 11 | def dialog_content(&block) 12 | content_for :dialog_content, capture(&block), flush: true 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/helpers/components/dropdown_menu_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::DropdownMenuHelper 2 | def render_dropdown_menu(**options, &block) 3 | content = capture(&block) if block 4 | render "components/ui/dropdown_menu", content: content, **options 5 | end 6 | 7 | def dropdown_menu_trigger(&block) 8 | content_for :dropdown_menu_trigger, capture(&block), flush: true 9 | end 10 | 11 | def dropdown_menu_label(label = nil, &block) 12 | content_for :dropdown_menu_label, (label || capture(&block)), flush: true 13 | end 14 | 15 | def dropdown_menu_content(&block) 16 | content_for :dropdown_menu_content, capture(&block), flush: true 17 | end 18 | 19 | def dropdown_menu_item(label = nil, **options, &block) 20 | content = (label || capture(&block)) 21 | render "components/ui/shared/menu_item", content: content 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/helpers/components/dropzone_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::DropzoneHelper 2 | def render_dropzone 3 | render "components/ui/dropzone" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/components/filter_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::FilterHelper 2 | def filter_icon(&block) 3 | content_for :filter_icon, capture(&block), flush: true 4 | end 5 | 6 | def render_filter(items, **options, &block) 7 | content_for :filter_icon, "", flush: true 8 | content = capture(&block) if block 9 | input_class = content_for?(:filter_icon) ? "pl-1" : "" 10 | render "components/ui/filter", items: items, options: options, input_class: input_class, content: content 11 | end 12 | 13 | def list_item(value:, name:, selected:) 14 | "#{name}" 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/helpers/components/forms_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::FormsHelper 2 | def render_form_with(**opts) 3 | form_with(**opts.merge(builder: Shadcn::FormBuilder)) do |form| 4 | yield form 5 | end 6 | end 7 | 8 | def render_form_for(obj, **opts) 9 | form_for(obj, **opts.merge(builder: Shadcn::FormBuilder), html: opts) do |form| 10 | yield form 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/helpers/components/hover_card_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::HoverCardHelper 2 | def render_hover_card(**options, &block) 3 | content = capture(&block) if block 4 | render "components/ui/hover_card", content: content, **options 5 | end 6 | 7 | def hover_card_trigger(&block) 8 | content_for :hover_card_trigger, capture(&block), flush: true 9 | end 10 | 11 | def hover_card_content(options = {}, &block) 12 | content_for :hover_card_content_class, options[:class], flush: true 13 | content_for :hover_card_content, capture(&block), flush: true 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/helpers/components/input_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::InputHelper 2 | def render_input(name:, label: false, id: nil, type: :text, value: nil, **options) 3 | options[:class] = "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm transition-colors ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 #{options[:class]} " 4 | options[:class] << case options[:variant] 5 | when :borderless 6 | " border-0 focus-visible:outline-none focus-visible:shadow-none focus-visible:ring-transparent" 7 | else 8 | "shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:border-muted" 9 | end 10 | options[:class] = tw(options[:class]) 11 | 12 | options.reverse_merge!( 13 | label: (options[:label] || false), 14 | required: (options[:required] || false), 15 | disabled: (options[:disabled] || false), 16 | readonly: (options[:readonly] || false), 17 | placeholder: (options[:placeholder] || ""), 18 | autocomplete: (options[:autocomplete] || ""), 19 | autocapitalize: (options[:autocapitalize] || nil), 20 | autocorrect: (options[:autocorrect] || nil), 21 | autofocus: (options[:autofocus] || nil) 22 | ) 23 | render partial: "components/ui/input", locals: { 24 | type:, 25 | label:, 26 | name:, 27 | value:, 28 | id:, 29 | options: options 30 | } 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /app/helpers/components/label_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::LabelHelper 2 | def render_label(name:, label:, **options) 3 | render "components/ui/label", name: name, label: label, options: options 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/components/list_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::ListHelper 2 | def list_item(value:, name:, selected: false, as: :div) 3 | content_tag as, value, 4 | class: "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm 5 | outline-none aria-selected:bg-accent aria-selected:text-accent-foreground hover:bg-accent hover:text-accent-foreground 6 | data-[disabled]:pointer-events-none data-[disabled]:opacity-50", 7 | role: "option", 8 | data: {value:, selected:}, 9 | aria: {selected:} 10 | end 11 | 12 | def render_list(items, as: :div, **options) 13 | render "components/ui/list", items:, as:, **options 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/helpers/components/popover_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::PopoverHelper 2 | def render_popover(**options, &block) 3 | content = capture(&block) if block 4 | render "components/ui/popover", content: content, **options 5 | end 6 | 7 | def popover_trigger(&block) 8 | content_for :popover_trigger, capture(&block), flush: true 9 | end 10 | 11 | def popover_content(options = {}, &block) 12 | content_for :popover_content_class, options[:class], flush: true 13 | content_for :popover_content, capture(&block), flush: true 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/helpers/components/progress_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::ProgressHelper 2 | def render_progress(value:, **options) 3 | render "components/ui/progress", value: (100 - value), **options 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/components/select_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::SelectHelper 2 | def render_select(name:, **options, &block) 3 | component = Shadcn::SelectComponent.new(name: name, view_context: self, **options, &block) 4 | component.call 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/helpers/components/separator_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::SeparatorHelper 2 | def render_separator(options = {}) 3 | options = {class: "shrink-0 bg-border h-[1px] w-full #{options[:class]}"}.reverse_merge(options) 4 | content_tag :div, "", options 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/helpers/components/sheet_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::SheetHelper 2 | def render_sheet(**options, &block) 3 | options[:direction] ||= "left" 4 | options[:width] ||= "w-3/4" 5 | options[:state] ||= "closed" 6 | 7 | content_for :sheet_trigger, "", flush: true 8 | content_for :sheet_content, "", flush: true 9 | 10 | content = capture(&block) if block 11 | render "components/ui/sheet", content: content, options: options 12 | end 13 | 14 | def sheet_trigger(&block) 15 | content_for :sheet_trigger, capture(&block), flush: true 16 | end 17 | 18 | def sheet_content(&block) 19 | content_for :sheet_content, capture(&block), flush: true 20 | end 21 | 22 | def direction_class(direction) 23 | mappings = { 24 | left: "left-0", 25 | right: "right-0" 26 | } 27 | 28 | mappings[direction.to_sym] 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/helpers/components/skeleton_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::SkeletonHelper 2 | def render_skeleton 3 | render "components/ui/skeleton" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/components/slider_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::SliderHelper 2 | def render_slider(name:, value: 0, id: nil, **options) 3 | render "components/ui/slider", value:, id:, name:, options: 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/components/switch_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::SwitchHelper 2 | def render_switch(text, id:, name:, state: "unchecked", **options) 3 | render "components/ui/switch", text:, id:, name:, state:, options: 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/components/tabs_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::TabsHelper 2 | def render_tabs(&block) 3 | @_tabs = [] 4 | capture(&block) 5 | render "components/ui/tabs" 6 | end 7 | 8 | def tab_list(&block) 9 | content_for :tab_list, capture(&block), flush: true 10 | end 11 | 12 | def tab(title, **options) 13 | options[:id] ||= "tab_#{title.parameterize}" 14 | options[:state] = options[:active] ? "active" : "inactive" 15 | 16 | @_tabs << {title: title, id: options[:id]} 17 | render("components/ui/tabs/tab", title:, options:) 18 | end 19 | 20 | def tab_panels(&block) 21 | content_for :tab_panels, capture(&block), flush: true 22 | end 23 | 24 | def tab_panel(**options, &block) 25 | options[:state] = options[:active] ? "active" : "inactive" 26 | content_for :panel, capture(&block), flush: true 27 | render("components/ui/tabs/panel", options:) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/helpers/components/textarea_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::TextareaHelper 2 | def render_textarea(name:, label: false, id: nil, value: nil, **options) 3 | options.reverse_merge!(rows: 3, required: false, disabled: false, 4 | readonly: false, class: "", label: false, placeholder: "Type here...") 5 | render partial: "components/ui/textarea", locals: { 6 | label:, 7 | name:, 8 | value:, 9 | id:, 10 | options: options 11 | } 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/helpers/components/toast_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::ToastHelper 2 | def render_toast(header: nil, description: nil, action: nil, class: nil, data: {}, variant: :default, **options, &block) 3 | options[:class] ||= "" 4 | options[:class] << " destructive group border-destructive bg-destructive text-destructive-foreground " if variant == :destructive 5 | 6 | render "components/ui/toast", header:, description:, action:, class:, data:, options: options 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/helpers/components/toggle_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::ToggleHelper 2 | def render_toggle(label = nil, **options, &block) 3 | content = label || capture(&block) 4 | render "components/ui/toggle", content: content, **options 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/helpers/components/tooltip_helper.rb: -------------------------------------------------------------------------------- 1 | module Components::TooltipHelper 2 | def render_tooltip(**options, &block) 3 | content = capture(&block) if block 4 | render "components/ui/tooltip", content: content, **options 5 | end 6 | 7 | def tooltip_trigger(&block) 8 | content_for :tooltip_trigger, capture(&block), flush: true 9 | end 10 | 11 | def tooltip_content(options = {}, &block) 12 | content_for :tooltip_content_class, options[:class], flush: true 13 | content_for :tooltip_content, capture(&block), flush: true 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/helpers/components_helper.rb: -------------------------------------------------------------------------------- 1 | require "tailwind_merge" 2 | 3 | module ComponentsHelper 4 | def tw(*classes) 5 | TailwindMerge::Merger.new.merge(classes.join(" ")) 6 | end 7 | 8 | PRIMARY_CLASSES = " bg-primary text-primary-foreground hover:bg-primary/80 " 9 | SECONDARY_CLASSES = " bg-secondary text-secondary-foreground hover:bg-secondary/80 " 10 | OUTLINE_CLASSES = " border border-input bg-background hover:bg-accent hover:text-accent-foreground " 11 | GHOST_CLASSES = " hover:bg-accent hover:text-accent-foreground " 12 | DESTRUCTIVE_CLASSES = " bg-destructive text-destructive-foreground hover:bg-destructive/90 " 13 | 14 | module Button 15 | PRIMARY = ComponentsHelper::PRIMARY_CLASSES 16 | SECONDARY = ComponentsHelper::SECONDARY_CLASSES 17 | OUTLINE = ComponentsHelper::OUTLINE_CLASSES 18 | GHOST = ComponentsHelper::GHOST_CLASSES 19 | DESTRUCTIVE = ComponentsHelper::DESTRUCTIVE_CLASSES 20 | end 21 | 22 | module Alert 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/helpers/documentation_helper.rb: -------------------------------------------------------------------------------- 1 | module DocumentationHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/examples_helper.rb: -------------------------------------------------------------------------------- 1 | module ExamplesHelper 2 | def render_component_header(title:, description:) 3 | render "layouts/documentation/component_header", title:, description: 4 | end 5 | 6 | def render_example 7 | render "layouts/documentation/examples" 8 | end 9 | 10 | def render_preview 11 | render "layouts/documentation/preview" 12 | end 13 | 14 | def render_usage(name) 15 | render "examples/components/#{name}/usage" 16 | end 17 | 18 | def render_code_preview(name) 19 | render "examples/components/#{name}/code/preview" 20 | end 21 | 22 | def code_partial(name, language) 23 | component, partial = name.split("/") 24 | content_tag :pre, class: "code-sample py-4 px-4", data: {controller: "highlight"} do 25 | content_tag :code, class: "language-#{language}" do 26 | html_escape(File.read(Rails.root.join("app", "views", "examples", "components", "#{component}/code/_#{partial}.erb"))) 27 | end 28 | end 29 | end 30 | 31 | def code_sample(content = "", language:, &block) 32 | content_tag :pre, class: "code-sample px-4 my-2 pb-5 min-h-fit", data: {controller: "highlight"} do 33 | content_tag :code, class: "language-#{language}" do 34 | yield if block 35 | end 36 | end 37 | end 38 | 39 | def inline_code(content = nil, &block) 40 | content_tag :code, class: "relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono font-semibold" do 41 | content || yield(block) 42 | end 43 | end 44 | alias_method :code, :inline_code 45 | end 46 | -------------------------------------------------------------------------------- /app/javascript/application.js: -------------------------------------------------------------------------------- 1 | // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails 2 | import "@hotwired/turbo-rails" 3 | import "controllers" 4 | -------------------------------------------------------------------------------- /app/javascript/controllers/application.js: -------------------------------------------------------------------------------- 1 | import { Application } from "@hotwired/stimulus"; 2 | 3 | const application = Application.start(); 4 | 5 | // Configure Stimulus development experience 6 | application.debug = false; 7 | window.Stimulus = application; 8 | 9 | export { application }; 10 | -------------------------------------------------------------------------------- /app/javascript/controllers/hello_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | connect() { 5 | this.element.textContent = "Hello World!" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/javascript/controllers/highlight_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus"; 2 | import hljs from "highlight.js"; 3 | 4 | export default class extends Controller { 5 | connect() { 6 | hljs.highlightAll(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/javascript/controllers/index.js: -------------------------------------------------------------------------------- 1 | // Import and register all your controllers from the importmap under controllers/* 2 | 3 | import { application } from "controllers/application" 4 | 5 | // Eager load all controllers defined in the import map under controllers/**/*_controller 6 | import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading" 7 | eagerLoadControllersFrom("controllers", application) 8 | 9 | // Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!) 10 | // import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading" 11 | // lazyLoadControllersFrom("controllers", application) 12 | -------------------------------------------------------------------------------- /app/javascript/controllers/theme_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus"; 2 | 3 | export default class extends Controller { 4 | static targets = ["toggleButton"]; 5 | 6 | connect() { 7 | this.loadThemePreference(); 8 | } 9 | 10 | toggle() { 11 | const isDarkMode = document.documentElement.classList.toggle("dark"); 12 | this.saveThemePreference(isDarkMode); 13 | } 14 | 15 | loadThemePreference() { 16 | const isDarkMode = localStorage.getItem("themePreference") === "true"; 17 | if (isDarkMode) { 18 | document.documentElement.classList.add("dark"); 19 | } 20 | } 21 | 22 | saveThemePreference(isDarkMode) { 23 | localStorage.setItem("themePreference", isDarkMode); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/javascript/controllers/ui/checkbox_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus"; 2 | export default class extends Controller { 3 | connect() { 4 | this.button = this.element.querySelector("button"); 5 | this.checkmark = this.button.querySelector("span"); 6 | } 7 | 8 | toggle() { 9 | if (this.checkmark.classList.contains("hidden")) { 10 | this.checkmark.classList.remove("hidden"); 11 | this.button.dataset.state = "checked"; 12 | } else { 13 | this.checkmark.classList.add("hidden"); 14 | this.button.dataset.state = "unchecked"; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/javascript/controllers/ui/collapsible_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus"; 2 | 3 | export default class extends Controller { 4 | static targets = ["item"]; 5 | static classes = ["hidden"]; 6 | 7 | connect() { 8 | this.class = this.hasHiddenClass ? this.hiddenClass : "hidden"; 9 | } 10 | 11 | toggle() { 12 | this.itemTargets.forEach((item) => { 13 | item.classList.toggle(this.class); 14 | }); 15 | } 16 | 17 | show() { 18 | this.itemTargets.forEach((item) => { 19 | item.classList.remove(this.class); 20 | }); 21 | } 22 | 23 | hide() { 24 | this.itemTargets.forEach((item) => { 25 | item.classList.add(this.class); 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/javascript/controllers/ui/dropdown_controller.js: -------------------------------------------------------------------------------- 1 | // Inspired By: https://github.com/stimulus-components/stimulus-dropdown/blob/master/src/index.ts 2 | import UIPopover from "controllers/ui/popover_controller"; 3 | import { useTransition } from "https://ga.jspm.io/npm:stimulus-use@0.51.3/dist/index.js"; 4 | 5 | export default class extends UIPopover {} 6 | -------------------------------------------------------------------------------- /app/javascript/controllers/ui/dropzone_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus"; 2 | 3 | export default class extends Controller { 4 | static targets = ["fileInput"]; 5 | connect() { 6 | this.element.addEventListener("dragover", this.preventDragDefaults); 7 | this.element.addEventListener("dragenter", this.preventDragDefaults); 8 | } 9 | 10 | disconnect() { 11 | this.element.removeEventListener("dragover", this.preventDragDefaults); 12 | this.element.removeEventListener("dragenter", this.preventDragDefaults); 13 | } 14 | 15 | preventDragDefaults(e) { 16 | e.preventDefault(); 17 | e.stopPropagation(); 18 | } 19 | 20 | trigger() { 21 | this.fileInputTarget.click(); 22 | } 23 | 24 | acceptFiles(event) { 25 | event.preventDefault(); 26 | const files = event.dataTransfer ? event.dataTransfer.files : event.target.files; 27 | [...files].forEach((file) => { 28 | this.uploadFile(file); 29 | }); 30 | } 31 | 32 | // Implement your own file upload strategy here... 33 | uploadFile(file) { 34 | console.log("Received file for upload: ", file); 35 | console.log("Implement your own file upload strategy here..."); 36 | // const formData = new FormData(); 37 | // formData.append("file", file); 38 | 39 | // fetch("/upload", { 40 | // method: "POST", 41 | // body: formData, 42 | // }) 43 | // .then((response) => response.json()) 44 | // .then((data) => { 45 | // console.log(data); 46 | // }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/javascript/controllers/ui/filter_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus"; 2 | 3 | export default class UIFilter extends Controller { 4 | static targets = ["source", "item"]; 5 | 6 | connect() {} 7 | 8 | filter(event) { 9 | let lowerCaseFilterTerm = this.sourceTarget.value.toLowerCase(); 10 | let regex = new RegExp("^" + lowerCaseFilterTerm); 11 | if (this.hasItemTarget) { 12 | this.itemTargets.forEach((el, i) => { 13 | let filterableKey = el.innerText.toLowerCase(); 14 | 15 | // Check for consecutive characters match using regex 16 | el.classList.toggle("hidden", !regex.test(filterableKey)); 17 | }); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/javascript/controllers/ui/hover-card_controller.js: -------------------------------------------------------------------------------- 1 | // Inspired by: https://github.com/excid3/tailwindcss-stimulus-components/blob/master/src/popover.js 2 | 3 | import { Controller } from "@hotwired/stimulus"; 4 | import { createPopper } from "https://ga.jspm.io/npm:@popperjs/core@2.11.8/lib/index.js"; 5 | import { useDebounce, useHover } from "https://ga.jspm.io/npm:stimulus-use@0.51.3/dist/index.js"; 6 | 7 | export default class UIHoverCardController extends Controller { 8 | static debounces = ["mouseEnter", "mouseLeave"]; 9 | static targets = ["content", "wrapper", "trigger"]; 10 | 11 | connect() { 12 | useDebounce(this); 13 | useHover(this, { element: this.triggerTarget }); 14 | this.popperInstance = createPopper(this.triggerTarget, this.contentTarget, { 15 | placement: this.contentTarget.dataset.side || "bottom", 16 | modifiers: [ 17 | { 18 | name: "offset", 19 | options: { 20 | offset: [0, 8], 21 | }, 22 | }, 23 | ], 24 | }); 25 | } 26 | 27 | mouseEnter() { 28 | this.popperInstance.update(); 29 | this.contentTarget.dataset.state = "open"; 30 | this.contentTarget.classList.remove("hidden"); 31 | } 32 | 33 | mouseLeave() { 34 | this.popperInstance.update(); 35 | this.contentTarget.dataset.state = "closed"; 36 | this.contentTarget.classList.add("hidden"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/javascript/controllers/ui/popover_controller.js: -------------------------------------------------------------------------------- 1 | // Inspired by: https://github.com/excid3/tailwindcss-stimulus-components/blob/master/src/popover.js 2 | 3 | import { Controller } from "@hotwired/stimulus"; 4 | import { createPopper } from "https://ga.jspm.io/npm:@popperjs/core@2.11.8/lib/index.js"; 5 | import { useClickOutside } from "https://ga.jspm.io/npm:stimulus-use@0.51.3/dist/index.js"; 6 | 7 | export default class UIPopover extends Controller { 8 | static values = { 9 | dismissAfter: Number, 10 | }; 11 | static targets = ["content", "wrapper", "trigger"]; 12 | 13 | connect() { 14 | useClickOutside(this); 15 | this.popperInstance = createPopper(this.triggerTarget, this.contentTarget, { 16 | placement: this.contentTarget.dataset.side || "bottom", 17 | modifiers: [ 18 | { 19 | name: "offset", 20 | options: { 21 | offset: [0, 8], 22 | }, 23 | }, 24 | ], 25 | }); 26 | } 27 | 28 | // Show the popover 29 | show() { 30 | this.contentTarget.classList.remove("hidden"); 31 | this.contentTarget.dataset.state = "open"; 32 | } 33 | 34 | // Hide the popover 35 | hide() { 36 | this.contentTarget.classList.add("hidden"); 37 | this.contentTarget.dataset.state = "closed"; 38 | } 39 | 40 | clickOutside(event) { 41 | this.hide(); 42 | } 43 | 44 | // Toggle the popover on demand 45 | toggle(event) { 46 | this.popperInstance.update(); 47 | if (this.contentTarget.classList.contains("hidden")) { 48 | this.show(); 49 | 50 | if (this.hasDismissAfterValue) { 51 | setTimeout(() => { 52 | this.hide(); 53 | }, this.dismissAfterValue); 54 | } 55 | } else { 56 | this.hide(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/javascript/controllers/ui/sheet_controller.js: -------------------------------------------------------------------------------- 1 | import UIDialog from "controllers/ui/dialog_controller"; 2 | import "https://ga.jspm.io/npm:@kanety/stimulus-static-actions@1.0.1/dist/index.modern.js"; 3 | 4 | export default class extends UIDialog { 5 | // Handles a button triggering the sheet in a different 6 | // controller instance 7 | // REFACTOR: This is the toggle method in dialog_controller with dom elements 8 | // instead of targets. Update the method there to receive dom elements and this 9 | // can be refactored to use those methods instead of reimplementing. 10 | toggleOutlet() { 11 | const sheetTarget = document.querySelector(this.element.dataset.UiSheetOutlet); 12 | const dialogTarget = sheetTarget.querySelector("[data-ui--sheet-target='dialog']"); 13 | const modalTarget = sheetTarget.querySelector("[data-ui--sheet-target='modal']"); 14 | const contentTarget = sheetTarget.querySelector("[data-ui--sheet-target='content']"); 15 | const visible = dialogTarget.dataset.state == "closed" ? false : true; 16 | const mainTarget = document.body; 17 | if (!visible) { 18 | document.body.classList.add("overflow-hidden"); 19 | contentTarget.classList.add("overflow-y-scroll", "h-full"); 20 | dialogTarget.classList.remove("hidden"); 21 | dialogTarget.dataset.state = "open"; 22 | modalTarget.classList.remove("hidden"); 23 | modalTarget.dataset.state = "open"; 24 | } else { 25 | document.body.classList.remove("overflow-hidden"); 26 | contentTarget.classList.remove("overflow-y-scroll", "h-full"); 27 | dialogTarget.classList.add("hidden"); 28 | dialogTarget.dataset.state = "closed"; 29 | modalTarget.classList.add("hidden"); 30 | modalTarget.dataset.state = "closed"; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/javascript/controllers/ui/slider_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus"; 2 | 3 | export default class UISliderController extends Controller { 4 | updateRange() { 5 | const input = this.element; 6 | const min = input.min; 7 | const max = input.max; 8 | const val = input.value; 9 | 10 | const fillRatio = parseInt(((val - min) * 100) / (max - min)); 11 | input.style = `background-size: ${fillRatio}% 100%`; 12 | input.setAttribute("value", fillRatio); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/javascript/controllers/ui/switch_controller.js: -------------------------------------------------------------------------------- 1 | // Inspired by: https://github.com/excid3/tailwindcss-stimulus-components/blob/master/src/popover.js 2 | 3 | import { Controller } from "@hotwired/stimulus"; 4 | 5 | export default class UIToggleController extends Controller { 6 | connect() {} 7 | 8 | toggle() { 9 | const button = this.element.querySelector("button"); 10 | const span = this.element.querySelector("span"); 11 | const input = this.element.querySelector("input[type='hidden']"); 12 | 13 | if (this.element.dataset.state == "checked") { 14 | input.value = "unchecked"; 15 | button.dataset.state = "unchecked"; 16 | span.dataset.state = "unchecked"; 17 | this.element.dataset.state = "unchecked"; 18 | } else { 19 | input.value = "checked"; 20 | button.dataset.state = "checked"; 21 | span.dataset.state = "checked"; 22 | this.element.dataset.state = "checked"; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/javascript/controllers/ui/toggle_controller.js: -------------------------------------------------------------------------------- 1 | // Inspired by: https://github.com/excid3/tailwindcss-stimulus-components/blob/master/src/popover.js 2 | 3 | import { Controller } from "@hotwired/stimulus"; 4 | 5 | export default class UIToggleController extends Controller { 6 | connect() {} 7 | 8 | toggle() { 9 | if (this.element.dataset.state == "on") { 10 | this.element.dataset.state = "off"; 11 | } else { 12 | this.element.dataset.state = "on"; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/javascript/controllers/ui/tooltip_controller.js: -------------------------------------------------------------------------------- 1 | import UIHoverCardController from "controllers/ui/hover-card_controller"; 2 | 3 | export default class UITooltipController extends UIHoverCardController {} 4 | -------------------------------------------------------------------------------- /app/javascript/controllers/ui/transition_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus"; 2 | import { useTransition } from "https://ga.jspm.io/npm:stimulus-use@0.51.3/dist/index.js"; 3 | 4 | export default class extends Controller { 5 | connect() { 6 | useTransition(this); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "from@example.com" 3 | layout "mailer" 4 | end 5 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | primary_abstract_class 3 | end 4 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User 2 | include ActiveModel::API 3 | include ActiveModel::SecurePassword 4 | 5 | attr_accessor :email, :password_digest 6 | 7 | has_secure_password 8 | validates :email, presence: true 9 | end 10 | -------------------------------------------------------------------------------- /app/views/components/ui/_accordion.html.erb: -------------------------------------------------------------------------------- 1 |
4 |
8 |

11 | 17 | <%= title || content_for(:title) %> 28 | 29 | 30 | 31 |

32 |
37 |
<%= description || content_for(:description) %>
38 |
39 |
40 |
41 | -------------------------------------------------------------------------------- /app/views/components/ui/_alert_dialog.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
<%= content_for(:alert_dialog_trigger) %>
3 | <%= render "components/ui/shared/backdrop", as: "backdrop" %> 4 | 5 | 24 |
25 | -------------------------------------------------------------------------------- /app/views/components/ui/_button.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <% case as %> 3 | <% when :link %> 4 | <%= link_to href, class: button_classes, data: data do %> 5 | <%= text %> 6 | <% end %> 7 | <% when :button %> 8 | <%= button_tag class: button_classes, data: data do %> 9 | <%= text %> 10 | <% end %> 11 | <% end %> 12 | -------------------------------------------------------------------------------- /app/views/components/ui/_card.html.erb: -------------------------------------------------------------------------------- 1 |
"> 2 | <% if title || subtitle %> 3 |
4 | <% if title %> 5 |

<%= title %>

6 | <% end %> <% if subtitle %> 7 |

<%= subtitle %>

8 | <% end %> 9 |
10 | <% end %> 11 | <% if !block && !title %> 12 | <%= content_tag( :div, class: "p-6" ){ body } %> 13 | <% elsif !block %> 14 | <%= content_tag( :div, class: "px-6" ){ body } %> 15 | <% else %> 16 | <%= body %> 17 | <% end %> 18 | <% if footer %><% end %> 19 |
20 | -------------------------------------------------------------------------------- /app/views/components/ui/_checkbox.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 31 | <%= render_label name: name, label: label, data: {action: "click->ui--checkbox#toggle"} %> 32 |
33 |
34 | -------------------------------------------------------------------------------- /app/views/components/ui/_collapsible.html.erb: -------------------------------------------------------------------------------- 1 |
5 |
8 |

<%= trigger %>

9 | <%= render_button variant: :ghost, data: {state: "closed"}, aria: {expanded: false}, class: "px-[0.75em] " do %> 10 | 21 | 22 | 23 | 24 | Toggle 25 | <% end %> 26 |
27 | <%= yield :preview %> 28 | 34 |
35 | -------------------------------------------------------------------------------- /app/views/components/ui/_combobox.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_popover do %> 2 | <%= popover_trigger do %> 3 | 7 | <% end %> 8 | <%= popover_content class: "border-none p-0" do %> 9 | <%= render_filter items %> 10 | <% end %> 11 | <% end %> 12 | -------------------------------------------------------------------------------- /app/views/components/ui/_command.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/app/views/components/ui/_command.html.erb -------------------------------------------------------------------------------- /app/views/components/ui/_date_picker.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 13 |
14 | -------------------------------------------------------------------------------- /app/views/components/ui/_dropdown_menu.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_popover do %> 2 | <%= popover_trigger do %> 3 | <%= content_for(:dropdown_menu_trigger) %> 4 | <% end %> 5 | <%= popover_content class: "p-1 w-56" do %> 6 | <% if content_for?(:dropdown_menu_label) %>
<%= content_for(:dropdown_menu_label) %>
<% end %> 7 | <%= content_for(:dropdown_menu_content) %> 8 | <% end %> 9 | <% end %> 10 | -------------------------------------------------------------------------------- /app/views/components/ui/_dropzone.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 9 | 10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /app/views/components/ui/_filter.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render_card do %> 3 |
4 | <%= content_for :filter_icon %> 5 | <%= render_input name: "filter", placeholder: "Filter items...", variant: :borderless, class: input_class, data: {"ui--filter-target": "source", action: "input->ui--filter#filter"} %> 6 |
7 | <%= render_separator %> 8 |
9 | <%= content_tag :div, role: "group" do %> 10 | <% items.each do |item| %> 11 |
12 | <%= list_item(value: item[:value], name: item[:name], selected: item[:selected]) %> 13 |
14 | <% end %> 15 | <% end %> 16 |
17 | <% end %> 18 |
19 | -------------------------------------------------------------------------------- /app/views/components/ui/_hover_card.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
4 | <%= content_for(:hover_card_trigger) %> 5 |
6 | 7 |
8 | 17 |
18 |
19 | -------------------------------------------------------------------------------- /app/views/components/ui/_input.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= text_field_tag name, value, type: type, id: id, 3 | class: options[:class], 4 | placeholder: options[:placeholder], 5 | autocomplete: options[:autocomplete], 6 | autocapitalize: options[:autocapitalize], 7 | autocorrect: options[:autocorrect], 8 | disabled: options[:disabled], 9 | required: options[:required], 10 | readonly: options[:readonly], 11 | data: options[:data] %> 12 | -------------------------------------------------------------------------------- /app/views/components/ui/_label.html.erb: -------------------------------------------------------------------------------- 1 | <%= label_tag name, label, 2 | data: options[:data], 3 | class: tw("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 #{options[:class]}") %> 4 | -------------------------------------------------------------------------------- /app/views/components/ui/_list.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_tag as, role: "group" do %> 2 | <% items.each do |item| %> 3 | <%= list_item(value: item[:value], name: item[:name], selected: item[:selected]) %> 4 | <% end %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/views/components/ui/_popover.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <%= content_for(:popover_trigger) %> 4 |
5 | 6 |
7 | 18 |
19 |
20 | -------------------------------------------------------------------------------- /app/views/components/ui/_progress.html.erb: -------------------------------------------------------------------------------- 1 |
9 | 10 |
15 |
16 | -------------------------------------------------------------------------------- /app/views/components/ui/_skeleton.html.erb: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /app/views/components/ui/_slider.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/views/components/ui/_switch.html.erb: -------------------------------------------------------------------------------- 1 |
6 | 7 | 19 | 25 |
26 | -------------------------------------------------------------------------------- /app/views/components/ui/_tabs.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
9 | <%= content_for :tab_list %> 10 |
11 | 12 | <%= content_for :tab_panels %> 13 |
14 | -------------------------------------------------------------------------------- /app/views/components/ui/_textarea.html.erb: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /app/views/components/ui/_toggle.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_button(content, 2 | variant: :ghost, data: {controller: "ui--toggle", action: "ui--toggle#toggle"}, 3 | class: "data-[state=on]:bg-accent data-[state=on]:text-accent-foreground") %> 4 | -------------------------------------------------------------------------------- /app/views/components/ui/_tooltip.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
4 | <%= content_for(:tooltip_trigger) %> 5 |
6 | 7 |
8 | 17 |
18 |
19 | -------------------------------------------------------------------------------- /app/views/components/ui/shared/_backdrop.html.erb: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /app/views/components/ui/shared/_menu_item.html.erb: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /app/views/components/ui/svg/_check.html.erb: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /app/views/components/ui/tabs/_panel.html.erb: -------------------------------------------------------------------------------- 1 |
7 | <%= content_for :panel %> 8 |
9 | -------------------------------------------------------------------------------- /app/views/components/ui/tabs/_tab.html.erb: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /app/views/documentation/about.html.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | Powered by amazing open source projects. 4 | 5 | [ui.shadcn.com](https://ui.shadcn.com) is a project by [shadcn](https://shadcn.com). 6 | 7 | [shadcn/ui on Rails](https://shadcn-ui-on-rails.avi.nyc) is a project by 8 | [aviflombaum](https://avi.nyc). 9 | 10 | ## Credits from shadcn/ui 11 | 12 | - [Radix UI](https://radix-ui.com) - For the primitives. 13 | - [Vercel](https://vercel.com) - Where I host all my projects. 14 | - [Shu Ding](https://shud.in) - The typography style is adapted from his work on Nextra. 15 | - [Cal](https://cal.com) - Where I copied the styles for the first component: the `Button`. 16 | - [cmdk](https://cmdk.paco.me) - For the `` component. 17 | 18 | ## License 19 | 20 | MIT © [shadcn](https://shadcn.com) 21 | -------------------------------------------------------------------------------- /app/views/documentation/generators.html.md: -------------------------------------------------------------------------------- 1 | # Component Generators 2 | 3 | The gem adds generators to your application to help you copy the code from this application into 4 | yours. 5 | 6 | Each time you run the generator it will check to make sure you have the prerequisites, like Tailwind 7 | and the shadcn stylesheet, and if not, do its best to reconcile that for you. 8 | 9 | After that it will copy the component files into your application. If you edit the component, 10 | re-running the generator for it will remove your edits. **Re-running a component generator is 11 | basically reinstalling it and overwriting any changes you might have made.** 12 | 13 | The generator will show up in `rails g` and it is called `shadcn-ui` 14 | 15 | ## Usage 16 | 17 | ``` 18 | rails generate shadcn-ui 19 | ``` 20 | 21 | You can list out the available components from `rails g shadcn-ui`. 22 | -------------------------------------------------------------------------------- /app/views/documentation/javascript.html.md: -------------------------------------------------------------------------------- 1 | # Stimulus Controllers 2 | 3 | Many components are paired with a stimulus controller that lives in 4 | `app/javascript/controllers/ui/_controller.js`. As much as possible I try to avoid adding 5 | depedencies and currently this application is managing them with importmaps and I'm pretty sure if 6 | you're not using importmaps, this breaks. 7 | 8 | The Stimulus controllers frankly need a lot of work. 9 | -------------------------------------------------------------------------------- /app/views/examples/components/accordion.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "layouts/documentation/component_header", 2 | title: "Accordion", 3 | description: "A vertically stacked set of interactive headings that each reveal a section of content." %> 4 | 5 | <% content_for :preview, flush: true do %> 6 |
7 | <%= render_code_preview('accordion') %> 8 |
9 | <% end %> 10 | 11 | <% content_for :code, flush: true do %> 12 | <%= code_partial("accordion/preview", :erb) %> 13 | <% end %> 14 | 15 | <%= render_preview %> 16 | 17 |

Installation

18 | 19 | <%= code_sample(language: "sh") do %> 20 | rails generate shadcn-ui accordion 21 | <% end %> 22 | 23 |

Usage

24 | 25 | <%= code_partial("accordion/usage", :erb) %> 26 | 27 | <%= render_usage("accordion") %> 28 | 29 | <% content_for :examples, flush: true do %> 30 |
31 | <%= render "examples/components/accordion/code/description" %> 32 |
33 | <% end %> 34 | 35 | <% content_for :code, flush: true do %> 36 | <%= code_partial("accordion/description.html", :erb) %> 37 | <% end %> 38 | 39 | <%= render_example %> 40 | 41 | <% content_for :examples, flush: true do %> 42 |
43 | <%= render "examples/components/accordion/code/block" %> 44 |
45 | <% end %> 46 | 47 | <% content_for :code, flush: true do %> 48 | <%= code_partial("accordion/block.html", :erb) %> 49 | <% end %> 50 | 51 | <%= render_example %> 52 | -------------------------------------------------------------------------------- /app/views/examples/components/accordion/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Accordion component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/accordion_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_accordion.html.erb") %>
  • 6 |
  • <%= code("app/javascript/controllers/ui/accordion_controller.js") %>
  • 7 |
8 | 9 |

10 | The method <%= code("render_accordion") %> defined in <%= code("app/helpers/components/accordion_helper.rb") %> 11 | accepts a <%= code("title:") %> and <%= code("description:") %> keyword arguments in the inline usage.

12 | 13 |

When passed only a <%= code("title") %> argument, the helper will accept a block to be rendered as the content for the description.

14 | 15 |

When passed no arguments, the block passed to the accordion helper must call <%= code("accordion_title") %> and <%= code("accordion_description") %> both of which accept blocks for the content for those portals within the component.

16 | -------------------------------------------------------------------------------- /app/views/examples/components/accordion/code/_block.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_accordion do %> 2 | <%= accordion_title do %> 3 | If you need to customize the title 4 | <% end %> 5 | <%= accordion_description do %> 6 | Use the full block format. 7 | <% end %> 8 | <% end %> 9 | -------------------------------------------------------------------------------- /app/views/examples/components/accordion/code/_description.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_accordion title: "Embed components in the description of an accordion" do %> 2 | <%= accordion_description do %> 3 | <%= render_card title: "A Simple Card", class: "w-full" do %> 4 |

But it could be anything really, give it a try.

5 | <% end %> 6 | <% end %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/examples/components/accordion/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_accordion title: "Did you know?", 2 | description: "You can wrap shadcn helpers in any component library you want!" %> 3 | 4 | <%= render_accordion do %> 5 | <%= accordion_title do %> 6 | Use the generators 7 | <% end %> 8 | <%= accordion_description do %> 9 | Add components with <%= code("rails g shadcn_ui add accordion") %> 10 | <% end %> 11 | <% end %> 12 | -------------------------------------------------------------------------------- /app/views/examples/components/accordion/code/_usage.erb: -------------------------------------------------------------------------------- 1 | # Inline 2 | <%= render_accordion(title:, description:) %> 3 | 4 | # Description Block 5 | <%= render_accordion(title:) do %> 6 | 7 | <% end %> 8 | 9 | # Title and Description Block 10 | <%= render_accordion do %> 11 | <%= accordion_title do %> 12 | <% end %> 13 | 14 | <%= accordion_description do %> 15 | <% end %> 16 | <% end %> 17 | -------------------------------------------------------------------------------- /app/views/examples/components/alert-dialog.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= render "layouts/documentation/component_header", 3 | title: "Alert Dialog", 4 | description: "A modal dialog that interrupts the user with important content and expects a response." %> 5 | 6 | <% content_for :preview, flush: true do %> 7 |
8 | <%= render_code_preview('alert-dialog') %> 9 |
10 | <% end %> 11 | 12 | <% content_for :code, flush: true do %> 13 | <%= code_partial("alert-dialog/preview", :erb) %> 14 | <% end %> 15 | 16 | <%= render_preview %> 17 | 18 |

Installation

19 | <%= code_sample(language: "sh") do %> 20 | rails generate shadcn-ui alert-dialog 21 | <% end %> 22 | 23 |

Usage

24 | 25 | <%= code_partial("alert-dialog/usage", :erb) %> 26 | 27 | <%= render_usage("alert-dialog") %> 28 | -------------------------------------------------------------------------------- /app/views/examples/components/alert-dialog/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Alert Dialog component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/alert_dialog_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_alert_dialog.html.erb") %>
  • 6 |
7 | 8 | And relies on <%= code("app/javascript/controllers/ui/dialog_controller.js") %>. 9 | 10 |

11 | The method <%= code("render_alert_dialog") %> defined in <%= code("app/helpers/components/alert_dialog_helper.rb") %> 12 | accepts a block for the inner components of the dialog. It renders the partial 13 | <%= code("app/views/components/ui/_alert_dialog.html.erb") %> which contains the model structure and 14 | and the 2 actions buttons of continue and cancel. 15 |

16 | 17 |

18 | The modal is composed of 4 components with corresponding helper methods, <%= code("alert_dialog_trigger") %>, 19 | which accepts a block for the element that will trigger the modal, and <%= code("alert_dialog_content") %>, 20 | which accepts a block for the body of the modal and the two actions for the modal, <%= code("alert_dialog_cancel") %> for the element that you would like to cancel the dialog interaction, for example a button, and <%= code("alert_dialog_continue") %> for the element you would like to use to continue to interaction. Note, you should feel free to bind your own data actions to these buttons as they are enclosed in an element that will handle closing the modal on click. 21 |

22 | 23 |

24 | See Dialog for more context as this component inherits functionality from that one.

25 | -------------------------------------------------------------------------------- /app/views/examples/components/alert-dialog/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_alert_dialog do %> 2 | <%= alert_dialog_trigger do %> 3 | <%= render_button("Open Dialog", variant: :outline) %> 4 | <% end %> 5 | 6 | <%= alert_dialog_content do %> 7 |
8 |

Are you absolutely sure?

9 |

10 | This action cannot be undone. This will permanently delete your account and remove your data from our servers. 11 |

12 |
13 | <% end %> 14 | 15 | <%= alert_dialog_cancel do %> 16 | <%= render_button("Cancel", variant: :outline) %> 17 | <% end %> 18 | 19 | <%= alert_dialog_continue do %> 20 | <%= render_button("Continue") %> 21 | <% end %> 22 | <% end %> 23 | -------------------------------------------------------------------------------- /app/views/examples/components/alert-dialog/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_alert_dialog do %> 2 | <%= alert_dialog_trigger do %> 3 | <% end %> 4 | 5 | <%= alert_dialog_content do %> 6 | <% end %> 7 | 8 | <%= alert_dialog_cancel do %> 9 | <% end %> 10 | 11 | <%= alert_dialog_continue do %> 12 | <% end %> 13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/examples/components/alert/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Alert component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/alert_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_alert.html.erb") %>
  • 6 |
7 | 8 |

9 | The method <%= code("render_alert") %> defined in <%= code("app/helpers/components/alert_helper.rb") %> 10 | accepts a <%= code("title:") %> and <%= code("description:") %> required keyword arguments along with an optional <%= code("variant:") %> argument for the kind of alert to render. Skip the icon by setting the optional argument <%= code("icon:") %> to <%= code("false") %>.

11 | -------------------------------------------------------------------------------- /app/views/examples/components/alert/code/_attention.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= render_alert(variant: :attention, title: "Attention", 3 | description: "This variant is :attention") %> 4 | -------------------------------------------------------------------------------- /app/views/examples/components/alert/code/_destructive.erb: -------------------------------------------------------------------------------- 1 | <%= render_alert(variant: :error, title: "Error", 2 | description: "This variant is :error, :danger, :alert, :destructive") %> 3 | -------------------------------------------------------------------------------- /app/views/examples/components/alert/code/_info.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= render_alert(variant: :info, title: "Info", 3 | description: "This variant is :info") %> 4 | -------------------------------------------------------------------------------- /app/views/examples/components/alert/code/_no_icon.erb: -------------------------------------------------------------------------------- 1 | <%= render_alert icon: false, 2 | title: "Skip the Icon", 3 | description: "By setting icon: to false in your render_alert call." %> 4 | -------------------------------------------------------------------------------- /app/views/examples/components/alert/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_alert title: "Did you know?", 2 | description: "You can wrap shadcn helpers in any component library you want!" %> 3 | -------------------------------------------------------------------------------- /app/views/examples/components/alert/code/_success.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= render_alert(variant: :success, title: "Success", 3 | description: "This variant is :success") %> 4 | -------------------------------------------------------------------------------- /app/views/examples/components/alert/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_alert title:, description:, variant: %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/badge.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= render "layouts/documentation/component_header", 4 | title: "Badge", 5 | description: "Displays a badge or a component that looks like a badge." %> 6 | 7 | <%= content_for :preview, flush: true do %> 8 | <%= render_code_preview('badge') %> 9 | <% end %> 10 | 11 | <% content_for :code, flush: true do %> 12 | <%= code_partial("badge/preview", :erb) %> 13 | <% end %> 14 | 15 | <%= render_preview %> 16 | 17 |

Installation

18 | <%= code_sample(language: "sh") do %> 19 | rails generate shadcn-ui badge 20 | <% end %> 21 | 22 |

Usage

23 | <%= code_partial("badge/usage", :erb) %> 24 | 25 | <%= render_usage("badge") %> 26 | -------------------------------------------------------------------------------- /app/views/examples/components/badge/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Badge component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/badge_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_badge.html.erb") %>
  • 6 |
7 | 8 |

9 | The method <%= code("render_badge") %> defined in <%= code("app/helpers/components/badge_helper.rb") %> 10 | accepts a <%= code("text:") %> required keyword argument along with an optional <%= code("variant:") %> argument for the kind of badge to render.

11 | -------------------------------------------------------------------------------- /app/views/examples/components/badge/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_badge text: "Badge" %> 2 | <%= render_badge text: "Secondary", variant: :secondary %> 3 | <%= render_badge text: "Destructive", variant: :destructive %> 4 | <%= render_badge text: "Outline", variant: :outline %> 5 | <%= render_badge text: "Ghost", variant: :ghost %> 6 | -------------------------------------------------------------------------------- /app/views/examples/components/badge/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_badge text:, variant: %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/button.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= render "layouts/documentation/component_header", 4 | title: "Button", 5 | description: "Displays a button or a component that looks like a button." %> 6 | 7 | <%= content_for :preview, flush: true do %> 8 | <%= render_code_preview('button') %> 9 | <% end %> 10 | 11 | <% content_for :code, flush: true do %> 12 | <%= code_partial("button/preview", :erb) %> 13 | <% end %> 14 | 15 | <%= render_preview %> 16 | 17 |

Installation

18 | <%= code_sample(language: "sh") do %> 19 | rails generate shadcn-ui button 20 | <% end %> 21 | 22 |

Usage

23 | <%= code_partial("button/usage", :erb) %> 24 | 25 | <%= render_usage("button") %> 26 | -------------------------------------------------------------------------------- /app/views/examples/components/button/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Button component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/button_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_button.html.erb") %>
  • 6 |
7 | 8 |

9 | 10 |

The method <%= code("render_button") %> defined in <%= code("app/helpers/components/button_helper.rb") %> 11 | accepts a first argument for the text of the button or as a <%= code("text:") %> keyword argument.

12 | 13 |

Optional arguments for the button include: 14 | 15 |

20 | -------------------------------------------------------------------------------- /app/views/examples/components/button/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_button "Button" %> 2 | 3 | <%= render_button variant: :secondary do %> 4 | Secondary with Icon 7 | <% end %> 8 | 9 | <%= render_button text: "Destructive", variant: :destructive %> 10 | <%= render_button text: "Outline", variant: :outline %> 11 | <%= render_button text: "Ghost", variant: :ghost %> 12 | <%= render_button "Link", as: :link, href: "/" %> 13 | <%= render_button text: "Ghost Link", variant: :ghost, as: :link %> 14 | -------------------------------------------------------------------------------- /app/views/examples/components/button/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_button(label, text: nil, 2 | variant: :default, 3 | as: :button, 4 | href: nil, 5 | data: {}, 6 | **options, &block) %> 7 | -------------------------------------------------------------------------------- /app/views/examples/components/card.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= render "layouts/documentation/component_header", 3 | title: "Card", 4 | description: "Displays a card with header, content, and footer." %> 5 | 6 | <%= content_for :preview, flush: true do %> 7 | <%= render_code_preview('card') %> 8 | <% end %> 9 | 10 | <% content_for :code, flush: true do %> 11 | <%= code_partial("card/preview", :erb) %> 12 | <% end %> 13 | 14 | <%= render_preview %> 15 | 16 |

Installation

17 | <%= code_sample(language: "sh") do %> 18 | rails generate shadcn-ui card 19 | <% end %> 20 | 21 |

Usage

22 | <%= code_partial("card/usage", :erb) %> 23 | 24 | <%= render_usage("card") %> 25 | 26 | <% content_for :examples, flush: true do %> 27 | <%= render "examples/components/card/code/form" %> 28 | <% end %> 29 | 30 | <% content_for :code, flush: true do %> 31 | <%= code_partial("card/form", :erb) %> 32 | <% end %> 33 | 34 | <%= render_example %> 35 | 36 | <% content_for :examples, flush: true do %> 37 | <%= render "examples/components/card/code/notifications" %> 38 | <% end %> 39 | 40 | <% content_for :code, flush: true do %> 41 | <%= code_partial("card/notifications", :erb) %> 42 | <% end %> 43 | 44 | <%= render_example %> 45 | -------------------------------------------------------------------------------- /app/views/examples/components/card/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Card component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/card_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_card.html.erb") %>
  • 6 |
7 | 8 |

9 | 10 |

The method <%= code("render_card") %> defined in <%= code("app/helpers/components/card_helper.rb") %> 11 | accepts optional keyword arguments for each section of content in the card, offering maximum flexibility.

12 | 13 |

The sections are.

14 | 15 |
    16 |
  • <%= code("title:") %>, for the header of the card, will be rendered in a large bold font.
  • 17 |
  • <%= code("subtitle:") %> for a muted title under the main title..
  • 18 |
  • A <%= code("body:") %> The main content section. Without providing either a keyword argument or a block, an empty card will be rendered.
  • 19 |
20 | 21 |

Note: Padding to the body is applied logically leaving you to define it when a block is passed with the presence of a title, otherwise, a default padding is provided to simplify the markup. Feel free to edit this in <%= code("app/views/components/ui/_card.html.erb") %>, 22 | -------------------------------------------------------------------------------- /app/views/examples/components/card/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_card body: "The Most Basic of Cards" %> 2 | 3 | <%= render_card title: "Did you know?", 4 | subtitle: "This is kinda cool.", 5 | body: "You can embed any HTML in the body with a block.", 6 | footer: "Have a great day!" %> 7 | -------------------------------------------------------------------------------- /app/views/examples/components/card/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_card(title: nil, subtitle: nil, body: nil, footer: nil) do %> 2 | 3 | <% end %> 4 | -------------------------------------------------------------------------------- /app/views/examples/components/checkbox.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Checkbox", 2 | description: "A control that allows the user to toggle between checked and not checked." %> 3 | 4 | <%= content_for :preview, flush: true do %> 5 | <%= render_code_preview('checkbox') %> 6 | <% end %> 7 | 8 | <% content_for :code, flush: true do %> 9 | <%= code_partial("checkbox/preview", :erb) %> 10 | <% end %> 11 | 12 | <%= render_preview %> 13 | 14 |

Installation

15 | <%= code_sample(language: "sh") do %> 16 | rails generate shadcn-ui checkbox 17 | <% end %> 18 | 19 |

Usage

20 | <%= code_partial("checkbox/usage", :erb) %> 21 | 22 | <%= render_usage("checkbox") %> 23 | -------------------------------------------------------------------------------- /app/views/examples/components/checkbox/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Checkbox component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/checkbox_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_checkbox.html.erb") %>
  • 6 |
  • <%= code("app/javascript/controllers/ui/checkbox_controller.js") %>
  • 7 |
8 | 9 |

<%= code("render_checkbox") %> accepts two required arguments, one for the <%= code("name:") %> of the checkbox which will pair with <%= code("label:") %> of the checkbox. The toggle functionality is controller by <%= code("app/javascript/controllers/ui/checkbox_controller.js") %>.

10 | -------------------------------------------------------------------------------- /app/views/examples/components/checkbox/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_checkbox name: "terms", 2 | label: "Accept terms and conditions" %> 3 | -------------------------------------------------------------------------------- /app/views/examples/components/checkbox/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_checkbox(label:, name:) %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/collapsible.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= render "layouts/documentation/component_header", 3 | title: "Collapsible", 4 | description: "An interactive component which expands/collapses a panel." %> 5 | 6 | <%= content_for :preview, flush: true do %> 7 |
8 | <%= render_code_preview('collapsible') %> 9 |
10 | <% end %> 11 | 12 | <% content_for :code, flush: true do %> 13 | <%= code_partial("collapsible/preview", :erb) %> 14 | <% end %> 15 | 16 | <%= render_preview %> 17 | 18 |

Installation

19 | <%= code_sample(language: "sh") do %> 20 | rails generate shadcn-ui collapsible 21 | <% end %> 22 | 23 |

Usage

24 | <%= code_partial("collapsible/usage", :erb) %> 25 | 26 | <%= render_usage("collapsible") %> 27 | -------------------------------------------------------------------------------- /app/views/examples/components/collapsible/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Collapsible component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/collapsible_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_collapsible.html.erb") %>
  • 6 |
  • <%= code("app/javascript/controllers/ui/collapsible_controller.js") %>
  • 7 |
8 | 9 |

10 | Collapsible introduces the <%= code("render_collapsible") %> method that accepts an argument 11 | <%= code("trigger:") %> which is the text you want to show that will trigger the collapsible menu and 12 | a block. Within the block of collapsible, you can call <%= code("collapsible_preview") %> to 13 | define an element that is visible from the collapsible menu via a block. Similarly, you can call 14 | <%= code("collapsible_body") %> and pass it a block for the elements that are hidden that will be 15 | made visible when the trigger is pressed. 16 |

17 | -------------------------------------------------------------------------------- /app/views/examples/components/collapsible/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_collapsible trigger: "@peduarte starred 3 repositories" do %> 2 | <% collapsible_preview do %> 3 |
@radix-ui/primitives
4 | <% end %> 5 | <% collapsible_body do %> 6 |
@radix-ui/colors
7 |
@stitches/react
8 | <% end %> 9 | <% end %> 10 | -------------------------------------------------------------------------------- /app/views/examples/components/collapsible/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_collapsible trigger: do %> 2 | <% collapsible_preview do %> 3 | <% end %> 4 | 5 | <% collapsible_body do %> 6 | <% end %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/examples/components/combobox.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "layouts/documentation/component_header", 2 | title: "Combobox", 3 | description: "" %> 4 | 5 | <% content_for :preview, flush: true do %> 6 |
7 | <%= render_code_preview('combobox') %> 8 |
9 | <% end %> 10 | 11 | <% content_for :code, flush: true do %> 12 |
13 | <%= code_partial("combobox/preview", :erb) %> 14 |
15 | <% end %> 16 | 17 | <%= render_preview %> 18 | 19 |

Installation

20 | <%= code_sample(language: "sh") do %> 21 | rails generate shadcn-ui combobox 22 | <% end %> 23 | 24 |

Usage

25 | <%= code_partial("combobox/usage", :erb) %> 26 | 27 | <%= render_usage("combobox") %> 28 | -------------------------------------------------------------------------------- /app/views/examples/components/combobox/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Combobox component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/combobox_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_combobox.html.erb") %>
  • 6 |
7 | 8 |

9 | The method <%= code("render_combobox") %> method accepts one mandtory argument, an array of hashes that each have a <%= code("value") %> and name <%= code("name") %>.

10 | 11 |

This componnet is the combination of a popover and a filter.

12 | -------------------------------------------------------------------------------- /app/views/examples/components/combobox/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <% items = [{:value => "Ruby on Rails", name: "Ruby on Rails"}, {:value => "Next.js", name: "Next.js"}, {:value => "Remix.run", name: "Remix.run"}] %> 2 | 3 | <%= render_combobox items do %> 4 | <%= combobox_trigger do %> 5 | Select framework 6 | <% end %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/examples/components/combobox/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_combobox [{name:, value:}] do %> 2 | <%= combobox_trigger do %> 3 | <% end %> 4 | <% end %> 5 | -------------------------------------------------------------------------------- /app/views/examples/components/date-picker.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Date Picker", 2 | description: "Displays a calendar to pick a date — triggered by a button." %> 3 | 4 | <%= content_for :preview, flush: true do %> 5 | <%= render_date_picker name: 'date', value:'2024-05-01' %> 6 | <% end %> 7 | 8 | <% content_for :code, flush: true do %> 9 | <%= code_partial("date-picker/preview", :erb) %> 10 | <% end %> 11 | 12 | <%= render_preview %> 13 | 14 |

Installation

15 | <%= code_sample(language: "sh") do %> 16 | rails generate shadcn-ui date-picker 17 | <% end %> 18 | 19 |

Usage

20 | <%= code_partial("date-picker/usage", :erb) %> 21 | 22 | <%= render_usage("date-picker") %> 23 | -------------------------------------------------------------------------------- /app/views/examples/components/date-picker/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Dropdown Menu component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/date_picker.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_date_picker.html.erb") %>
  • 6 |
  • <%= code("app/javascript/controllers/ui/date-picker_controller.js") %>
  • 7 |
8 | 9 |

10 | The method <%= code("render_date_picker") %> defined in <%= code("app/helpers/components/date_picker.rb") %> 11 | accepts a block for the two inner components. Each of those accepts a block for their respective content.

12 | 13 |

14 | Within the block, you can use the following methods:

15 | 16 |
    17 |
  • - accepts an argument for the label section of the dropdown menu. This is optional.
  • 18 |
  • - an arbitrary amount of these can be used for each menu item you want to include. This method either accepts a string or a block for the content you want.
  • 19 |
20 | -------------------------------------------------------------------------------- /app/views/examples/components/date-picker/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_date_picker name: 'date', value:'2024-05-01' %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/date-picker/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_date_picker name: 'date', value:'2024-05-01' %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/dialog/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Dialog component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/dialog_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_dialog.html.erb") %>
  • 6 |
  • <%= code("app/javascript/controllers/ui/dialog_controller.js") %>
  • 7 |
8 | 9 |

10 | The method <%= code("render_dialog") %> defined in <%= code("app/helpers/components/dialog_helper.rb") %> 11 | accepts a blog for the inner components of the dialog.It renders the partial 12 | <%= code("app/views/components/ui/_dialog.html.erb") %> which contains the model structure and 13 | a close button. 14 |

15 | 16 |

17 | The modal is composed of two components with corresponding helper methods, <%= code("dialog_trigger") %>, 18 | which accepts a block for the element that will trigger the modal, and <%= code("dialog_content") %>, 19 | which accepts a block for the body of the modal. 20 |

21 | 22 |

23 | <%= code("dialog_trigger") %> and <%= code("dialog_content") %> must be called nested within 24 | <%= code("render_dialog") %> 25 | 26 |

27 | <%= code("app/javascript/controllers/ui/dialog_controller.js") %> is a stimulus controller that provides 28 | the functionality of the modal.

29 | -------------------------------------------------------------------------------- /app/views/examples/components/dialog/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_dialog do %> 2 | <%= dialog_trigger do %> 3 | <%= render_button("Open Typography", variant: :outline) %> 4 | <% end %> 5 | <%= dialog_content do %> 6 |

The King's Plan

7 |

8 | The king thought long and hard, and finally came up with 9 | a brilliant plan 10 | : he would tax the jokes in the kingdom. 11 |

12 |
"After all," he said, "everyone enjoys a good joke, so it's only fair that they should pay for the privilege."
13 | <% end %> 14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/views/examples/components/dialog/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_dialog do %> 2 | <%= dialog_trigger do %> 3 | 4 | <% end %> 5 | <%= dialog_content do %> 6 | 7 | <% end %> 8 | <% end %> 9 | -------------------------------------------------------------------------------- /app/views/examples/components/dropdown-menu.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Dropdown Menu", 2 | description: "Displays a menu to the user — such as a set of actions or functions — triggered by a button." %> 3 | 4 | <%= content_for :preview, flush: true do %> 5 | <%= render_dropdown_menu do %> 6 | <%= dropdown_menu_trigger do %> 7 | <%= render_button "Open Dropdown", variant: :outline %> 8 | <% end %> 9 | 10 | <%= dropdown_menu_content do %> 11 | <%= dropdown_menu_label "My Account" %> 12 | <%= render_separator class: "my-1" %> 13 | <%= dropdown_menu_item "Profile" %> 14 | <%= dropdown_menu_item do %> 15 | 16 | 17 | 18 | Billing 19 | ⌘B 20 | <% end %> 21 | <%= dropdown_menu_item "Settings" %> 22 | <% end %> 23 | <% end %> 24 | <% end %> 25 | 26 | <% content_for :code, flush: true do %> 27 | <%= code_partial("dropdown-menu/preview", :erb) %> 28 | <% end %> 29 | 30 | <%= render_preview %> 31 | 32 |

Installation

33 | <%= code_sample(language: "sh") do %> 34 | rails generate shadcn-ui dropdown-menu 35 | <% end %> 36 | 37 |

Usage

38 | <%= code_partial("dropdown-menu/usage", :erb) %> 39 | 40 | <%= render_usage("dropdown-menu") %> 41 | -------------------------------------------------------------------------------- /app/views/examples/components/dropdown-menu/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Dropdown Menu component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/dropdown_menu_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_dropdown_menu.html.erb") %>
  • 6 |
  • <%= code("app/javascript/controllers/ui/dropdown-menu_controller.js") %>
  • 7 |
8 | 9 |

10 | The method <%= code("render_dropdown_menu") %> defined in <%= code("app/helpers/components/dropdown_menu_helper.rb") %> 11 | accepts a block for the two inner components, <%= code("dropdown_menu_trigger") %> and <%= code("dropdown_menu_content") %>. Each of those accepts a block for their respective content.

12 | 13 |

14 | Within the <%= code("dropdown_menu_content") %> block, you can use the following methods:

15 | 16 |
    17 |
  • <%= code("dropdown_menu_label") %> - accepts an argument for the label section of the dropdown menu. This is optional.
  • 18 |
  • <%= code("dropdown_menu_item") %> - an arbitrary amount of these can be used for each menu item you want to include. This method either accepts a string or a block for the content you want.
  • 19 |
20 | -------------------------------------------------------------------------------- /app/views/examples/components/dropdown-menu/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_dropdown_menu do %> 2 | <%= dropdown_menu_trigger do %> 3 | <%= render_button "Open Dropdown", variant: :outline %> 4 | <% end %> 5 | 6 | <%= dropdown_menu_content do %> 7 | <%= dropdown_menu_label "My Account" %> 8 | <%= render_separator class: "my-1" %> 9 | <%= dropdown_menu_item "Profile" %> 10 | <%= dropdown_menu_item do %> 11 | 12 | 13 | 14 | Billing 15 | ⌘B 16 | <% end %> 17 | <%= dropdown_menu_item "Settings" %> 18 | <% end %> 19 | <% end %> 20 | -------------------------------------------------------------------------------- /app/views/examples/components/dropdown-menu/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_dropdown_menu do %> 2 | <%= dropdown_menu_trigger do %> 3 | <% end %> 4 | 5 | <%= dropdown_menu_content do %> 6 | <%= dropdown_menu_label %> 7 | 8 | <%= dropdown_menu_item %> 9 | <%= dropdown_menu_item do %> 10 | <% end %> 11 | <%= dropdown_menu_item %> 12 | <% end %> 13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/examples/components/dropzone.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "layouts/documentation/component_header", 2 | title: "Dropzone", 3 | description: "A dropzone component that accepts multiple files via drag and drop." %> 4 | 5 | <% content_for :preview, flush: true do %> 6 |
7 | <%= render_code_preview('dropzone') %> 8 |
9 | <% end %> 10 | 11 | <% content_for :code, flush: true do %> 12 |
13 | <%= code_partial("dropzone/preview", :erb) %> 14 |
15 | <% end %> 16 | 17 | <%= render_preview %> 18 | 19 |

Installation

20 | <%= code_sample(language: "sh") do %> 21 | rails generate shadcn-ui dropzone 22 | <% end %> 23 | 24 |

Usage

25 | <%= code_partial("dropzone/usage", :erb) %> 26 | 27 | <%= render_usage("dropzone") %> 28 | -------------------------------------------------------------------------------- /app/views/examples/components/dropzone/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Dropzone component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/dropzone_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_dropzone.html.erb") %>
  • 6 |
  • <%= code("app/javascript/controllers/ui/dropzone_controller.js") %>
  • 7 |
8 | 9 |

10 | The method <%= code("render_dropzone") %> defined in <%= code("app/helpers/components/dropzone_helper.rb") %> will render the dropzone widget that is bound to the Stimulus controller. 11 | Upon triggering the file dialog or dragging and dropping files, the controller will delegate the files individually to a function, <%= code("uploadFile") %>. It is up to you to define what to do when a a file is to be uploaded. 12 |

13 | 14 |

15 | To implement fully, edit the <%= code("uploadFile") %> function in <%= code("app/javascript/controllers/ui/dropzone_controller.js") %>. This function is called for each file that is to be uploaded. The function is passed the file object. 16 |

17 | 18 |

19 | If you try the example component, you will see that it is logging the file object to the console. 20 |

21 | -------------------------------------------------------------------------------- /app/views/examples/components/dropzone/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_dropzone %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/dropzone/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_dropzone %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/filter.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | items = [{:value => "Ruby on Rails", name: "Ruby on Rails"}, {:value => "Next.js", name: "Next.js"}, {:value => "Remix.run", name: "Remix.run"}] 3 | %> 4 | 5 | <%= render_component_header title: "Filter", 6 | description: "Displays a list that is filterable via the included input." %> 7 | 8 | <%= content_for :preview, flush: true do %> 9 |
10 | <%= render_code_preview('filter') %> 11 |
12 | <% end %> 13 | 14 | <% content_for :code, flush: true do %> 15 | <%= code_partial("filter/preview", :erb) %> 16 | <% end %> 17 | 18 | <%= render_preview %> 19 | 20 |

Installation

21 | <%= code_sample(language: "sh") do %> 22 | rails generate shadcn-ui filter 23 | <% end %> 24 | 25 |

Usage

26 | <%= code_partial("filter/usage", :erb) %> 27 | 28 | <%= render_usage("filter") %> 29 | 30 | <% content_for :examples, flush: true do %> 31 |
32 | <%= render_filter items, class: "p-1" do %> 33 | <%= filter_icon do %> 34 | 35 | <% end %> 36 | <% end %> 37 |
38 | <% end %> 39 | 40 | <% content_for :code, flush: true do %> 41 | <%= code_partial("filter/icon.html", :erb) %> 42 | <% end %> 43 | 44 | 45 | <%= render_example %> 46 | -------------------------------------------------------------------------------- /app/views/examples/components/filter/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Filter component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/filter_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_filter.html.erb") %>
  • 6 |
  • <%= code("app/javascript/controllers/ui/filter_controller.js") %>
  • 7 |
8 | 9 |

10 | The method <%= code("render_filter") %> method accepts one mandtory argument, an array of hashes that each have a <%= code("value") %> and name <%= code("name") %>.

11 | 12 |

You can optionally also call <%= code("filter_icon") %> within a block passed to <%= code("render_filter") %> to render an icon to the left of the filter input.

13 | 14 |

This component gets used in the Combobox and Command components as well as potentially others.

15 | -------------------------------------------------------------------------------- /app/views/examples/components/filter/code/_icon.html.erb: -------------------------------------------------------------------------------- 1 | <% items = [{:value => "Ruby on Rails", name: "Ruby on Rails"}, {:value => "Next.js", name: "Next.js"}, {:value => "Remix.run", name: "Remix.run"}] %> 2 | 3 | <%= render_filter items, class: "p-1" do %> 4 | <%= filter_icon do %> 5 | 6 | <% end %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/examples/components/filter/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <% items = [{:value => "Ruby on Rails", name: "Ruby on Rails"}, {:value => "Next.js", name: "Next.js"}, {:value => "Remix.run", name: "Remix.run"}] %> 2 | 3 | <%= render_filter items %> 4 | -------------------------------------------------------------------------------- /app/views/examples/components/filter/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_filter items do %> 2 | <%# Optional %> 3 | <%= filter_icon do %> 4 | <% end %> 5 | <% end > 6 | -------------------------------------------------------------------------------- /app/views/examples/components/forms.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Forms", 2 | description: "Creates an ActiveModel backed form with error " %> 3 | 4 | <% content_for :preview, flush: true do %> 5 |
6 | <%= render_code_preview('forms') %> 7 |
8 | <% end %> 9 | 10 | <% content_for :code, flush: true do %> 11 | <%= code_partial("forms/preview", :erb) %> 12 | <% end %> 13 | 14 | <%= render_preview %> 15 | 16 |

Installation

17 | <%= code_sample(language: "sh") do %> 18 | rails generate shadcn-ui forms 19 | <% end %> 20 | 21 |

Usage

22 | <%= code_partial("forms/usage", :erb) %> 23 | <%= render_usage("forms") %> 24 | -------------------------------------------------------------------------------- /app/views/examples/components/forms/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Forms component introduces:

2 | 3 |
    4 |
  • <%= code("app/components/shadcn/form_builder.rb") %>
  • 5 |
  • <%= code("app/helpers/components/forms_helper.rb") %>
  • 6 |
7 | 8 |

9 | The Shadcn::FormBuilder is a custom form builder that extends the 10 | FormBuilder class. It adds the following methods that provide the form with inputs and controls: 11 |

12 | 13 |

    14 |
  • <%= code("label") %>
  • 15 |
  • <%= code("text_field") %>
  • 16 |
  • <%= code("password_field") %>
  • 17 |
  • <%= code("email_field") %>
  • 18 |
19 |

20 | 21 |

22 | To use these methods which generate the styled, ActiveModel backed form, you use the <%= code("render_form_for") %> or <%= code("render_form_with") %> methods provided by <%= code("app/helpers/components/forms_helper.rb") %>. 23 | These methods yield the common FormBuilder instance that you can use to generate the controls and inputs above. 24 |

25 | 26 |

27 | If a form field or attribute fails validation, its label and input will be given an error class to allow for styling. The default error style is defined in <%= code("app/assets/stylesheets/shadcn.css") %> 28 |

29 | -------------------------------------------------------------------------------- /app/views/examples/components/forms/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_form_for(@user, class: "w-full") do |form| %> 2 |
3 | <%= form.label :email, class: "text-right mr-4 text-lg" %> 4 | <%= form.text_field :email, class: "col-span-2" %> 5 |
6 |
7 | <%= form.label :password, class: "text-right mr-4 text-lg" %> 8 | <%= form.password_field :password, class: "col-span-2 mb-8" %> 9 |
10 |
11 | <%= form.submit "Create Account" %> 12 |
13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/examples/components/forms/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_form_for() do |form| %> 2 | <%= form.label :email %> 3 | <%= form.text_field %> 4 | <%= form.password_field %> 5 | <%= form.submit "Submit" %> 6 | <% end %> 7 | -------------------------------------------------------------------------------- /app/views/examples/components/hover-card.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Hover Card", 2 | description: "For sighted users to preview content available behind a link." %> 3 | 4 | <% content_for :preview, flush: true do %> 5 | <%= render_code_preview('hover-card') %> 6 | <% end %> 7 | 8 | <% content_for :code, flush: true do %> 9 | <%= code_partial("hover-card/preview", :erb) %> 10 | <% end %> 11 | 12 | <%= render_preview %> 13 | 14 |

Installation

15 | <%= code_sample(language: "sh") do %> 16 | rails generate shadcn-ui hover-card 17 | <% end %> 18 | 19 |

Usage

20 | <%= code_partial("hover-card/usage", :erb) %> 21 | 22 | <%= render_usage("hover-card") %> 23 | -------------------------------------------------------------------------------- /app/views/examples/components/hover-card/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Hover Card component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/hover_card_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_hover_card.html.erb") %>
  • 6 |
  • <%= code("app/javascript/controllers/ui/hover-card_controller.js") %>
  • 7 |
8 | 9 |

10 | The method <%= code("render_hover_card") %> defined in <%= code("app/helpers/components/hover_card_helper.rb") %> 11 | accepts a block for the two inner components, <%= code("hover_card_trigger") %> and <%= code("hover_card_content") %>. Each of those accepts a block for their respective content. 12 | 13 |

14 | <%= code("app/javascript/controllers/ui/hover-card_controller.js") %> is a stimulus controller that provides 15 | the functionality of the popover.

16 | -------------------------------------------------------------------------------- /app/views/examples/components/hover-card/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_hover_card do %> 2 | <%= hover_card_trigger do %> 3 | <%= content_tag :span, "@rails" %> 4 | <% end %> 5 | <%= hover_card_content do %> 6 |
7 |
8 |

Ruby on Rails

9 |

The Web framework that changed the world.

10 |
11 |
12 | <% end %> 13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/examples/components/hover-card/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_hover_card do %> 2 | <%= hover_card_trigger do %> 3 | <% end %> 4 | <%= hover_card_content do %> 5 | <% end %> 6 | <% end %> 7 | -------------------------------------------------------------------------------- /app/views/examples/components/input.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Input", 2 | description: "Displays a form input field or a component that looks like an input field." %> 3 | 4 | <% content_for :preview, flush: true do %> 5 |
6 | <%= render_code_preview('input') %> 7 |
8 | <% end %> 9 | 10 | <% content_for :code, flush: true do %> 11 | <%= code_partial("input/preview", :erb) %> 12 | <% end %> 13 | 14 | <%= render_preview %> 15 | 16 |

Installation

17 | <%= code_sample(language: "sh") do %> 18 | rails generate shadcn-ui input 19 | <% end %> 20 | 21 |

Usage

22 | <%= code_partial("input/usage", :erb) %> 23 | 24 | <%= content_for :examples, flush: true do %> 25 | <%= render_input name: "sample_input", id: "sample_input", variant: :borderless %> 26 | <% end %> 27 | 28 | <% content_for :code, flush: true do %> 29 | <%= code_partial("input/borderless", :erb) %> 30 | <% end %> 31 | 32 | <%= render_example %> 33 | -------------------------------------------------------------------------------- /app/views/examples/components/input/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Input component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/input_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_input.html.erb") %>
  • 6 |
7 | 8 |

9 | <%= code("render_input") %> accepts a <%= code("name:") %> argument for the name of the text field along with optional <%= code("id:") %> and <%= code("variant:") %> (only supporting <%= code(":borderless") %> currently) 10 |

11 | -------------------------------------------------------------------------------- /app/views/examples/components/input/code/_borderless.erb: -------------------------------------------------------------------------------- 1 | <%= render_input name: "sample_input", 2 | id: "sample_input", 3 | variant: :borderless %> 4 | -------------------------------------------------------------------------------- /app/views/examples/components/input/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_input name: "sample_input", id: "sample_input" %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/input/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_input name:, id: %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/label.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Label", 2 | description: "Displays a clickable label that will focus the associated form field." %> 3 | 4 | <% content_for :preview, flush: true do %> 5 | <%= render_code_preview('label') %> 6 | <% end %> 7 | 8 | <% content_for :code, flush: true do %> 9 | <%= code_partial("label/preview", :erb) %> 10 | <% end %> 11 | 12 | <%= render_preview %> 13 | 14 |

Installation

15 | <%= code_sample(language: "sh") do %> 16 | rails generate shadcn-ui label 17 | <% end %> 18 | 19 |

Usage

20 | <%= code_partial("label/usage", :erb) %> 21 | 22 | <%= render_usage("label") %> 23 | -------------------------------------------------------------------------------- /app/views/examples/components/label/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Label component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/label_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_label.html.erb") %>
  • 6 |
7 | 8 |

9 | The method <%= code("render_label") %> defined in <%= code("app/helpers/components/label_helper.rb") %> accepts two keyword arguments, <%= code("name") %> and <%= code("label") %>. The <%= code("name") %> argument is used to generate the <%= code("for") %> attributes. The <%= code("label") %> argument is used as the text for the label.

10 | -------------------------------------------------------------------------------- /app/views/examples/components/label/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_label name: "sample_input", 2 | label: "Sample Input", 3 | class: "block text-left w-full" %> 4 | 5 | <%= render_input name: "sample_input", id: "sample_input" %> 6 | -------------------------------------------------------------------------------- /app/views/examples/components/label/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_label name:, label: %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/popover.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Popover", 2 | description: "Displays rich content in a portal, triggered by a button." %> 3 | 4 | <% content_for :preview, flush: true do %> 5 |
6 | <%= render_code_preview('popover') %> 7 |
8 | <% end %> 9 | 10 | <% content_for :code, flush: true do %> 11 |
12 | <%= code_partial("popover/preview", :erb) %> 13 |
14 | <% end %> 15 | 16 | <%= render_preview %> 17 | 18 |

Installation

19 | <%= code_sample(language: "sh") do %> 20 | rails generate shadcn-ui popover 21 | <% end %> 22 | 23 |

Usage

24 | <%= code_partial("popover/usage", :erb) %> 25 | 26 | <%= render_usage("popover") %> 27 | 28 | <% content_for :examples, flush: true do %> 29 |
30 | <%= render "examples/components/popover/code/form" %> 31 |
32 | <% end %> 33 | 34 | <% content_for :code, flush: true do %> 35 | <%= code_partial("popover/form", :erb) %> 36 | <% end %> 37 | 38 | <%= render_example %> 39 | -------------------------------------------------------------------------------- /app/views/examples/components/popover/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Popover component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/popover_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_popover.html.erb") %>
  • 6 |
  • <%= code("app/javascript/controllers/ui/popover_controller.js") %>
  • 7 |
8 | 9 |

10 | <%= code("render_popover") %> accepts a block where you call <%= code("popover_trigger") %>, whose block will be the element that triggers the popover and 11 | <%= code("popover_content") %> whose block will be the content of the popover. 12 |

13 | -------------------------------------------------------------------------------- /app/views/examples/components/popover/code/_form.erb: -------------------------------------------------------------------------------- 1 | <%= render_popover do %> 2 | <%= popover_trigger do %> 3 | <%= render_button "Open Popover", variant: :outline %> 4 | <% end %> 5 | <%= popover_content do %> 6 |

7 |
8 |

Dimensions

9 |

Set the dimensions for the layer.

10 |
11 |
12 |
13 | 19 |
20 |
21 |
22 | <% end %> 23 | <% end %> 24 | -------------------------------------------------------------------------------- /app/views/examples/components/popover/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_popover do %> 2 | <%= popover_trigger do %> 3 | <%= render_button "Open Popover", variant: :outline %> 4 | <% end %> 5 | <%= popover_content do %> 6 |
7 |
8 |

Heading

9 |

Popovers stay open until you click the button or anywhere else on the document.

10 |
11 |
12 | <% end %> 13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/examples/components/popover/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_popover do %> 2 | <%= popover_trigger do %> 3 | <% end %> 4 | 5 | <%= popover_content do %> 6 | <% end %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/examples/components/progress.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Progress", 2 | description: "Displays an indicator showing the completion progress of a task, typically displayed as a progress bar." %> 3 | 4 | <% content_for :preview, flush: true do %> 5 | <%= render_code_preview('progress') %> 6 | <% end %> 7 | 8 | <% content_for :code, flush: true do %> 9 | <%= code_partial("progress/preview", :erb) %> 10 | <% end %> 11 | 12 | <%= render_preview %> 13 | 14 |

Installation

15 | <%= code_sample(language: "sh") do %> 16 | rails generate shadcn-ui progress 17 | <% end %> 18 | 19 |

Usage

20 | <%= code_partial("progress/usage", :erb) %> 21 | 22 | <%= render_usage("progress") %> 23 | -------------------------------------------------------------------------------- /app/views/examples/components/progress/_usage.erb: -------------------------------------------------------------------------------- 1 |

The Progress component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/progress_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_progress.html.erb") %>
  • 6 |
7 | 8 |

9 | The method <%= code("render_progress") %> defined in <%= code("app/helpers/components/progress_helper.rb") %> accepts one keyword arguments for the percentage of the progress bar.

10 | -------------------------------------------------------------------------------- /app/views/examples/components/progress/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_progress value: 37 %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/progress/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_progress value: %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/select.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "layouts/documentation/component_header", 2 | title: "Select", 3 | description: "A standard but styled select component." %> 4 | 5 | <% content_for :preview, flush: true do %> 6 |
7 | <%= render_code_preview('select') %> 8 |
9 | <% end %> 10 | 11 | <% content_for :code, flush: true do %> 12 |
13 | <%= code_partial("select/preview", :erb) %> 14 |
15 | <% end %> 16 | 17 | <%= render_preview %> 18 | 19 |

Installation

20 | <%= code_sample(language: "sh") do %> 21 | rails generate shadcn-ui select 22 | <% end %> 23 | 24 |

Usage

25 | <%= code_partial("select/usage", :erb) %> 26 | 27 | <%= render_usage("select") %> 28 | -------------------------------------------------------------------------------- /app/views/examples/components/select/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Select component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/select_helper.rb") %>
  • 5 |
  • <%= code("app/components/shadcn/select_component.rb") %>
  • 6 |
7 | 8 |

9 | The select component is implemented as a component object defined in <%= code("app/components/shadcn/select_component.rb") %>. 10 | This ruby class is initialized from the helper defined in <%= code("app/helpers/components/select_helper.rb") %>, <%= code "render_select" %>. 11 |

12 | 13 |

14 | The <%= code("render_select") %> method accepts a <%= code("name") %> keyword argument for the name of the form field. 15 | An optional argument of <%= code "selected" %> can contain the value of the option to be selected by default. 16 |

17 | 18 |

19 | <%= code("render_select") %> yields an instance of the <%= code "Shadcn::SelectComponent" %> that can be used to build <%= code "option" %>s for the select. 20 | <%= code "option" %> on the component instance accepts a <%= code "label" %> argument for the label text to be displayed for the option whose value will be the <%= code "value" %> on the component instance yields a new option object that accepts a <%= code "label" %> and <%= code "value" %> argument. 21 | <%= code "option" %> also accepts a block that can be used to create the label. 22 |

23 | -------------------------------------------------------------------------------- /app/views/examples/components/select/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_select name: "fruits", selected: "Apple" do |select| %> 2 | <%= select.option value: "Banana" do %> 3 | Banana 4 | <% end %> 5 | <%= select.option label: "Apple", value: "Apple" %> 6 | <%= select.option label: "Strawberry", value: "Strawberry" %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/examples/components/select/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_select name:, selected:, do |select| %> 2 | <%= select.option value: do %> 3 | <% end %> 4 | <%= select.option label:, value: %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/views/examples/components/separator.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= render "layouts/documentation/component_header", 4 | title: "Separator", 5 | description: "Visually or semantically separates content." %> 6 | <%= content_for :preview, flush: true do %> 7 |
8 | <%= render_code_preview('separator') %> 9 |
10 | <% end %> 11 | 12 | <% content_for :code, flush: true do %> 13 |
14 | <%= code_partial("separator/preview", :erb) %> 15 |
16 | <% end %> 17 | 18 | <%= render_preview %> 19 | 20 |

Installation

21 | <%= code_sample(language: "sh") do %> 22 | rails generate shadcn-ui separator 23 | <% end %> 24 | 25 |

Usage

26 | <%= code_partial("separator/usage", :erb) %> 27 | 28 | <%= render_usage("separator") %> 29 | 30 | <% content_for :examples, flush: true do %> 31 |
32 | <%= render "examples/components/separator/code/fancy" %> 33 |
34 | <% end %> 35 | 36 | <% content_for :code, flush: true do %> 37 | <%= code_partial("separator/fancy", :erb) %> 38 | <% end %> 39 | 40 | <%= render_example %> 41 | -------------------------------------------------------------------------------- /app/views/examples/components/separator/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Seperator component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/seperator_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_seperator.html.erb") %>
  • 6 |
7 | 8 |

9 | <%= code("render_seperator") %> will render a seperator. 10 |

11 | -------------------------------------------------------------------------------- /app/views/examples/components/separator/code/_fancy.erb: -------------------------------------------------------------------------------- 1 |

2 |
3 |

Shadcn on Rails

4 |
5 | 6 | <%= render_separator class: "my-4" %> 7 | 8 |
9 |
Blog
10 |
14 |
Docs
15 |
19 |
Source
20 |
21 |
22 | -------------------------------------------------------------------------------- /app/views/examples/components/separator/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_separator class: "my-4" %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/separator/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_separator %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/sheet.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Sheet", 2 | description: "Extends the Dialog component to display content that complements the main content of the screen." %> 3 | 4 | <% content_for :preview, flush: true do %> 5 |
6 | <%= render_code_preview('sheet') %> 7 |
8 | <% end %> 9 | 10 | <% content_for :code, flush: true do %> 11 |
12 | <%= code_partial("sheet/preview", :erb) %> 13 |
14 | <% end %> 15 | 16 | <%= render_preview %> 17 | 18 |

Installation

19 | <%= code_sample(language: "sh") do %> 20 | rails generate shadcn-ui sheet 21 | <% end %> 22 | 23 |

Usage

24 | <%= code_partial("sheet/usage", :erb) %> 25 | 26 | <%= render_usage("sheet") %> 27 | -------------------------------------------------------------------------------- /app/views/examples/components/sheet/code/_mobile_menu.erb: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | <%= render_sheet id: "sidebar_sheet", direction: "left" do %> 12 |
13 | <%= sheet_content do %> 14 | 15 | <% end %> 16 |
17 | <% end %> 18 | -------------------------------------------------------------------------------- /app/views/examples/components/sheet/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_sheet direction: "right" do %> 2 | <%= sheet_trigger do %> 3 | <%= render_button("Open Sidebar", variant: :outline) %> 4 | <% end %> 5 | <%= sheet_content do %> 6 |

8 | The King's Plan 9 |

10 |

11 | The king thought long and hard, and finally came up with 12 | 13 | a brilliant plan 14 | : 15 | he would tax the jokes in the kingdom. 16 |

17 |
18 | "After all," he said, "everyone enjoys a good joke, 19 | so it's only fair that they should pay for the privilege." 20 |
21 | <% end %> 22 | <% end %> 23 | -------------------------------------------------------------------------------- /app/views/examples/components/sheet/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_sheet direction: "right" do %> 2 | <%= sheet_trigger do %> 3 | <% end %> 4 | 5 | <%= sheet_content do %> 6 | <% end %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/examples/components/skeleton.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Skeleton", 2 | description: "Use to show a placeholder while content is loading." %> 3 | 4 | <% content_for :preview, flush: true do %> 5 |
6 | <%= render_code_preview('skeleton') %> 7 |
8 | <% end %> 9 | 10 | <% content_for :code, flush: true do %> 11 |
12 | <%= code_partial("skeleton/preview", :erb) %> 13 |
14 | <% end %> 15 | 16 | <%= render_preview %> 17 | 18 |

Installation

19 | <%= code_sample(language: "sh") do %> 20 | rails generate shadcn-ui skeleton 21 | <% end %> 22 | 23 |

Usage

24 | <%= code_partial("skeleton/usage", :erb) %> 25 | 26 | <%= render_usage("skeleton") %> 27 | -------------------------------------------------------------------------------- /app/views/examples/components/skeleton/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Skeleton component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/skeleton_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_skeleton.html.erb") %>
  • 6 |
7 | 8 |

9 | <%= code("render_skeleton") %> will output the skeleton component.

10 | -------------------------------------------------------------------------------- /app/views/examples/components/skeleton/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_skeleton %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/skeleton/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_skeleton %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/slider.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Slider", 2 | description: "An input where the user selects a value from within a given range." %> 3 | 4 | <% content_for :preview, flush: true do %> 5 |
6 | <%= render_code_preview('slider') %> 7 |
8 | <% end %> 9 | 10 | <% content_for :code, flush: true do %> 11 |
12 | <%= code_partial("slider/preview", :erb) %> 13 |
14 | <% end %> 15 | 16 | <%= render_preview %> 17 | 18 |

Installation

19 | <%= code_sample(language: "sh") do %> 20 | rails generate shadcn-ui slider 21 | <% end %> 22 | 23 |

Usage

24 | <%= code_partial("slider/usage", :erb) %> 25 | 26 | <%= render_usage("slider") %> 27 | -------------------------------------------------------------------------------- /app/views/examples/components/slider/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Slider component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/slider_helper.rb") %>
  • 5 |
  • <%= code("app/javascript/controllers/ui/slider_controller.js") %>
  • 6 |
  • <%= code("app/views/components/ui/_slider.html.erb") %>
  • 7 |
8 | 9 |

10 | <%= code("render_slider") %> accepts one keyword argument for the <%= code("name") %> of the slider element. It optionally accepts a <%= code("value") %> keyword for the starting value of the slider. 11 | The javascript controller is responsible for maintaining the state and visual of the slider from the underlying range input.

12 | -------------------------------------------------------------------------------- /app/views/examples/components/slider/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_slider name: "sample_slider", 2 | id: "sample_slider", 3 | value: 23, 4 | class: "w-full" %> 5 | -------------------------------------------------------------------------------- /app/views/examples/components/slider/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_slider name: %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/switch.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "layouts/documentation/component_header", 2 | title: "Switch", 3 | description: "" %> 4 | 5 | <% content_for :preview, flush: true do %> 6 |
7 | <%= render_code_preview('switch') %> 8 |
9 | <% end %> 10 | 11 | <% content_for :code, flush: true do %> 12 |
13 | <%= code_partial("switch/preview", :erb) %> 14 |
15 | <% end %> 16 | 17 | <%= render_preview %> 18 | 19 |

Installation

20 | <%= code_sample(language: "sh") do %> 21 | rails generate shadcn-ui switch 22 | <% end %> 23 | 24 |

Usage

25 | <%= code_partial("switch/usage", :erb) %> 26 | 27 | <%= render_usage("switch") %> 28 | -------------------------------------------------------------------------------- /app/views/examples/components/switch/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Switch component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/switch_helper.rb") %>
  • 5 |
  • <%= code("app/javascript/controllers/ui/switch_controller.js") %>
  • 6 |
  • <%= code("app/views/components/ui/_switch.html.erb") %>
  • 7 |
8 | 9 |

10 | <%= code("render_switch") %> accepts one argument for the text near the switch and two keyword arguments for the <%= code("id") %> of the button and <%= code("name") %> of the hidden input element used to pass the form value of the state. It optionally accepts a <%= code("state") %> keyword for the starting value of the switch. 11 | The javascript controller is responsible for the toggle effect by changing the <%= code("data-state") %> of the underlying elements.

12 | -------------------------------------------------------------------------------- /app/views/examples/components/switch/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_switch "Ruby on Rails", 2 | id: "ruby_on_rails", 3 | name: "language" 4 | %> 5 | -------------------------------------------------------------------------------- /app/views/examples/components/switch/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_switch text, id:, name: %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/table.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "layouts/documentation/component_header", 2 | title: "Table", 3 | description: "A simple table component for displaying data in rows and columns." %> 4 | 5 | <% content_for :preview, flush: true do %> 6 |
7 | <%= render_code_preview('table') %> 8 |
9 | <% end %> 10 | 11 | <% content_for :code, flush: true do %> 12 |
13 | <%= code_partial("table/preview", :erb) %> 14 |
15 | <% end %> 16 | 17 | <%= render_preview %> 18 | 19 |

Installation

20 | <%= code_sample(language: "sh") do %> 21 | rails generate shadcn-ui table 22 | <% end %> 23 | 24 |

Usage

25 | <%= code_partial("table/usage", :erb) %> 26 | 27 | <%= render_usage("table") %> 28 | -------------------------------------------------------------------------------- /app/views/examples/components/table/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Table component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/table_helper.rb") %>
  • 5 |
6 | 7 |

8 | The table component is so far unique in that it is entirely provided by <%= code("app/helpers/components/table_helper.rb") %>. This helper introduces methods that rely on <%= code("content_tag") %> to create the markup for the table. To edit anything about the rendered table, look there. 9 |

10 | 11 |

12 | <%= code("app/helpers/components/table_helper.rb") %> introduces the following methods that can be used to create a table. 13 | 14 |

    15 |
  • <%= code("render_table") %> accepts an optional caption as the first argument along with the block for the rest of the table components.
  • 16 |
  • <%= code("table_head") %> accepts a block for the content of the <%= code("thead") %> row.
  • 17 |
  • <%= code("table_header") %> accepts the header content inline or in a block. This will be rendered within the <%= code("thead") %> for that column.
  • 18 |
  • <%= code("table_body") %> accepts a block for the content of the <%= code("tbody") %>.
  • 19 |
  • <%= code("table_row") %> accepts a block for the content of the <%= code("tr") %>.
  • 20 |
  • <%= code("table_column") %> accepts the column content inline or in a block. This will be rendered within the <%= code("td") %> for the column.
  • 21 |
22 | 23 |

24 | -------------------------------------------------------------------------------- /app/views/examples/components/table/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <% @tracks = [ 2 | {title: "Total Eclipse of the Heart", artist: "Bonnie Tyler"}, 3 | {title: "I Will Always Love You", artist: "Whitney Houston"}, 4 | {title: "I Wanna Dance with Somebody", artist: "Whitney Houston"}, 5 | {title: "I Will Survive", artist: "Gloria Gaynor"}, 6 | {title: "My Heart Will Go On", artist: "Celine Dion"}, 7 | ] %> 8 | 9 | <%= render_table do %> 10 | <%= table_head do %> 11 | <%= table_header "", class: "w-1" %> 12 | <%= table_header class: "" do %> 13 | Title 14 | <% end %> 15 | <%= table_header "Artist" %> 16 | <% end %> 17 | <%= table_body do %> 18 | <% @tracks.each.with_index do |track, i| %> 19 | <%= table_row do %> 20 | <%= table_column do %> 21 | 24 | 25 | 26 | 27 | <% end %> 28 | <%= table_column do %> 29 | <%= track[:title] %> 30 | <% end %> 31 | <%= table_column track[:artist] %> 32 | <% end %> 33 | <% end %> 34 | <% end %> 35 | <% end %> 36 | -------------------------------------------------------------------------------- /app/views/examples/components/table/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_table do %> 2 | <%= table_head do %> 3 | <%= table_header %> 4 | <%= table_header %> 5 | <% end %> 6 | <%= table_body do %> 7 | <%= table_row do %> 8 | <%= table_column %> 9 | <%= table_column %> 10 | <% end %> 11 | <% end %> 12 | <% end %> 13 | -------------------------------------------------------------------------------- /app/views/examples/components/tabs.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "layouts/documentation/component_header", 2 | title: "Tabs", 3 | description: "" %> 4 | 5 | <% content_for :preview, flush: true do %> 6 |
7 | <%= render_code_preview('tabs') %> 8 |
9 | <% end %> 10 | 11 | <% content_for :code, flush: true do %> 12 |
13 | <%= code_partial("tabs/preview", :erb) %> 14 |
15 | <% end %> 16 | 17 | <%= render_preview %> 18 | 19 |

Installation

20 | <%= code_sample(language: "sh") do %> 21 | rails generate shadcn-ui tabs 22 | <% end %> 23 | 24 |

Usage

25 | <%= code_partial("tabs/usage", :erb) %> 26 | 27 | <%= render_usage("tabs") %> 28 | -------------------------------------------------------------------------------- /app/views/examples/components/tabs/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Tabs component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/tabs_helper.rb") %>
  • 5 |
  • <%= code("app/javascript/controllers/ui/tabs_controllers.rb") %>
  • 6 |
  • <%= code("app/views/components/ui/_tabs.html.erb") %>
  • 7 |
  • <%= code("app/views/components/ui/tabs/_tab.html.erb") %>
  • 8 |
  • <%= code("app/views/components/ui/tabs/_panel.html.erb") %>
  • 9 |
10 | 11 |

12 | <%= code("render_tabs") %> accepts two key blocks, <%= code("tab_list") %> and <%= code("tab_panel") %>. 13 | Within <%= code("tab_list") %> call <%= code("tab") %> for each tab, accepting the text you want for the tab and making one of them <%= code("active: true") %> to determine the starting active state of the tab. 14 | Within <%= code("tab_panel") %> call <%= code("tab_panel") %> for each tab panel, accepting a block for the content of the panel and making one of them <%= code("active: true") %> to determine the starting active state of the panels (generally corresponding to the active tab). 15 |

16 | -------------------------------------------------------------------------------- /app/views/examples/components/tabs/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_tabs do %> 2 | <%= tab_list do %> 3 | <%= tab "Account" %> 4 | <%= tab "Password", active: true %> 5 | <% end %> 6 | <%= tab_panels do %> 7 | <%= tab_panel do %> 8 | <%= render "examples/components/tabs/code/account" %> 9 | <% end %> 10 | <%= tab_panel active: true do %> 11 | <%= render "examples/components/tabs/code/password" %> 12 | <% end %> 13 | <% end %> 14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/views/examples/components/tabs/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_tabs do %> 2 | <%= tab_list do %> 3 | <%= tab title %> 4 | <%= tab title, active: true %> 5 | <% end %> 6 | <%= tab_panels do %> 7 | <%= tab_panel do %> 8 | <% end %> 9 | <%= tab_panel active: true do %> 10 | <% end %> 11 | <% end %> 12 | <% end %> 13 | -------------------------------------------------------------------------------- /app/views/examples/components/textarea.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Textarea", 2 | description: "Displays a form textarea or a component that looks like a textarea." %> 3 | 4 | <% content_for :preview, flush: true do %> 5 |
6 | <%= render_code_preview('textarea') %> 7 |
8 | <% end %> 9 | 10 | <% content_for :code, flush: true do %> 11 |
12 | <%= code_partial("textarea/preview", :erb) %> 13 |
14 | <% end %> 15 | 16 | <%= render_preview %> 17 | 18 |

Installation

19 | <%= code_sample(language: "sh") do %> 20 | rails generate shadcn-ui textarea 21 | <% end %> 22 | 23 |

Usage

24 | <%= code_partial("textarea/usage", :erb) %> 25 | 26 | <%= render_usage("textarea") %> 27 | -------------------------------------------------------------------------------- /app/views/examples/components/textarea/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Textarea component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/textarea_helper.rb") %>
  • 5 |
  • <%= code("app/views/components/ui/_textarea.html.erb") %>
  • 6 |
7 | 8 |

9 | <%= code("render_textarea") %> accepts one keyword argument for the <%= code("name") %> of the textarea element.

10 | -------------------------------------------------------------------------------- /app/views/examples/components/textarea/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_textarea name: "sample_textarea" %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/textarea/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_textarea name: %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/toast.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Toast", 2 | description: "A succinct message that is displayed temporarily." %> 3 | 4 | <% content_for :preview, flush: true do %> 5 |
6 | <%= render_button "Trigger Toast", variant: "outline", data: {controller: "ui--toast", action: "ui--toast#trigger", target: "#default_toast"} %> 7 | <%= render_code_preview('toast') %> 8 |
9 | <% end %> 10 | 11 | <% content_for :code, flush: true do %> 12 | <%= code_partial("toast/preview", :erb) %> 13 | <% end %> 14 | 15 | <%= render_preview %> 16 | 17 |

Installation

18 | <%= code_sample(language: "sh") do %> 19 | rails generate shadcn-ui toast 20 | <% end %> 21 | 22 |

Usage

23 | <%= code_partial("toast/usage", :erb) %> 24 | 25 | <%= render_usage("toast") %> 26 | 27 | <% content_for :examples, flush: true do %> 28 |
29 | <%= render_button "Trigger Destructive Toast", variant: "outline", data: {controller: "ui--toast", action: "ui--toast#trigger", target: "#alert_toast"} %> 30 | <%= render "examples/components/toast/code/destructive" %> 31 |
32 | <% end %> 33 | 34 | <% content_for :code, flush: true do %> 35 | <%= code_partial("toast/destructive", :erb) %> 36 | <% end %> 37 | 38 | <%= render_example %> 39 | -------------------------------------------------------------------------------- /app/views/examples/components/toast/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Toast component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/toast_helper.rb") %>
  • 5 |
  • <%= code("app/javascript/controllers/ui/toast_controller.js") %>
  • 6 |
  • <%= code("app/views/components/ui/_toast.html.erb") %>
  • 7 |
8 | 9 |

10 | <%= code("render_toast") %> accepts two keyword arguments, one for the <%= code("header") %> and the second for the <%= code("description") %> 11 | or body of the toast. An optional 3rd argument can be provided for an <%= code("action") %> element like a button.

12 | -------------------------------------------------------------------------------- /app/views/examples/components/toast/code/_destructive.erb: -------------------------------------------------------------------------------- 1 | <%= render_toast header: "Invalid Login", 2 | id: "alert_toast", 3 | description: "Could not find email password combination.", 4 | variant: :destructive, 5 | auto: false, 6 | class: "hidden" %> 7 | -------------------------------------------------------------------------------- /app/views/examples/components/toast/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_toast header: "Scheduled: Catch up", 2 | description: "Friday, February 10, 2023 at 5:57 PM", 3 | action: render_button("Undo", variant: :outline), 4 | id: "default_toast" %> 5 | -------------------------------------------------------------------------------- /app/views/examples/components/toast/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_toast header: , 2 | description: %> 3 | -------------------------------------------------------------------------------- /app/views/examples/components/toggle.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Toggle", 2 | description: "A two-state button that can be either on or off." %> 3 | 4 | <% content_for :preview, flush: true do %> 5 |
6 | <%= render_code_preview('toggle') %> 7 |
8 | <% end %> 9 | 10 | <% content_for :code, flush: true do %> 11 |
12 | <%= code_partial("toggle/preview", :erb) %> 13 |
14 | <% end %> 15 | 16 | <%= render_preview %> 17 | 18 |

Installation

19 | <%= code_sample(language: "sh") do %> 20 | rails generate shadcn-ui toggle 21 | <% end %> 22 | 23 |

Usage

24 | <%= code_partial("toggle/usage", :erb) %> 25 | 26 | <%= render_usage("toggle") %> 27 | -------------------------------------------------------------------------------- /app/views/examples/components/toggle/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Toggle component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/toggle_helper.rb") %>
  • 5 |
  • <%= code("app/javascript/controllers/ui/toggle_controller.js") %>
  • 6 |
  • <%= code("app/views/components/ui/_toggle.html.erb") %>
  • 7 |
8 | 9 |

10 | <%= code("render_toggle") %> an argument for the content for the button that will become a toggle.

11 | -------------------------------------------------------------------------------- /app/views/examples/components/toggle/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_toggle "𝐁" %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/toggle/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_toggle 'content' %> 2 | -------------------------------------------------------------------------------- /app/views/examples/components/tooltip.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_component_header title: "Tooltip", 2 | description: "A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it." %> 3 | 4 | <% content_for :preview, flush: true do %> 5 |
6 | <%= render_code_preview('tooltip') %> 7 |
8 | <% end %> 9 | 10 | <% content_for :code, flush: true do %> 11 |
12 | <%= code_partial("tooltip/preview", :erb) %> 13 |
14 | <% end %> 15 | 16 | <%= render_preview %> 17 | 18 |

Installation

19 | <%= code_sample(language: "sh") do %> 20 | rails generate shadcn-ui tooltip 21 | <% end %> 22 | 23 |

Usage

24 | <%= code_partial("tooltip/usage", :erb) %> 25 | 26 | <%= render_usage("tooltip") %> 27 | -------------------------------------------------------------------------------- /app/views/examples/components/tooltip/_usage.html.erb: -------------------------------------------------------------------------------- 1 |

The Tooltip component introduces:

2 | 3 |
    4 |
  • <%= code("app/helpers/components/tooltip_helper.rb") %>
  • 5 |
  • <%= code("app/javascript/controllers/ui/tooltip_controller.js") %>
  • 6 |
  • <%= code("app/views/components/ui/_tooltip.html.erb") %>
  • 7 |
8 | 9 |

10 | <%= code("render_tooltip") %> accepts a block that contains calls to <%= code("tooltip_trigger") %> and <%= code("tooltip_content") %> each accepting a block for their respective content. 11 | The <%= code("tooltip_trigger") %> is the element that upon hovering over, will reveal the <%= code("tooltip_content") %>. 12 | -------------------------------------------------------------------------------- /app/views/examples/components/tooltip/code/_preview.erb: -------------------------------------------------------------------------------- 1 | <%= render_tooltip do %> 2 | <%= tooltip_trigger do %> 3 | <%= render_button "Hover", variant: :outline %> 4 | <% end %> 5 | <%= tooltip_content do %> 6 |

7 |
8 |

More will be revealed.

9 |
10 |
11 | <% end %> 12 | <% end %> 13 | -------------------------------------------------------------------------------- /app/views/examples/components/tooltip/code/_usage.erb: -------------------------------------------------------------------------------- 1 | <%= render_tooltip do %> 2 | <%= tooltip_trigger do %> 3 | <% end %> 4 | 5 | <%= tooltip_content do %> 6 | <% end %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/layouts/component.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= page_title %> 5 | 8 | 9 | <%= csrf_meta_tags %> 10 | <%= csp_meta_tag %> 11 | <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %> 12 | 13 | <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> 14 | 15 | <%= javascript_importmap_tags %> 16 | 17 | 18 | 19 |
20 | <%= render 'layouts/shared/header' %> 21 |
22 |
23 |
25 | 26 | <%= render 'layouts/shared/sidebar' %> 27 |
28 |
<%= yield %>
29 |
30 |
31 |
32 |
33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /app/views/layouts/documentation.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= page_title %> 5 | 8 | 9 | <%= csrf_meta_tags %> 10 | <%= csp_meta_tag %> 11 | <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %> 12 | 13 | <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> 14 | 15 | <%= javascript_importmap_tags %> 16 | 17 | 18 | 19 |
20 | <%= render 'layouts/shared/header' %> 21 |
22 |
23 |
25 | 26 | <%= render 'layouts/shared/sidebar' %> 27 |
28 |
29 |
30 | <%= yield %> 31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | 39 | 40 | -------------------------------------------------------------------------------- /app/views/layouts/documentation/_breadcrumb.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
Docs
3 | 10 | 15 | 16 |
Alert
17 |
18 | -------------------------------------------------------------------------------- /app/views/layouts/documentation/_component_header.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

3 | <%= title %> 4 | <%= render_button "Edit", variant: :ghost, class: "text-sm", as: :link, href: "https://github.com/aviflombaum/shadcn-rails/blob/main/app/views/examples/components/#{params[:component]}.html.erb" %> 5 |

6 |

7 | <%= description %> 11 |

12 |
13 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/views/layouts/shared/_sidebar.html.erb: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "bundler/setup" 5 | require "shadnc-ui" 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | require "irb" 11 | IRB.start(__FILE__) 12 | -------------------------------------------------------------------------------- /bin/debug: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | overmind connect web 4 | -------------------------------------------------------------------------------- /bin/dev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | overmind start -f Procfile.dev 4 | -------------------------------------------------------------------------------- /bin/importmap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative "../config/application" 4 | require "importmap/commands" 5 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path("..", __dir__) 6 | 7 | def system!(*args) 8 | system(*args) || abort("\n== Command #{args} failed ==") 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to set up or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts "== Installing dependencies ==" 17 | system! "gem install bundler --conservative" 18 | system("bundle check") || system!("bundle install") 19 | 20 | # puts "\n== Copying sample files ==" 21 | # unless File.exist?("config/database.yml") 22 | # FileUtils.cp "config/database.yml.sample", "config/database.yml" 23 | # end 24 | 25 | puts "\n== Preparing database ==" 26 | system! "bin/rails db:prepare" 27 | 28 | puts "\n== Removing old logs and tempfiles ==" 29 | system! "bin/rails log:clear tmp:clear" 30 | 31 | puts "\n== Restarting application server ==" 32 | system! "bin/rails restart" 33 | end 34 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative "config/environment" 4 | 5 | run Rails.application 6 | Rails.application.load_server 7 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_job/railtie" 7 | require "active_record/railtie" 8 | require "active_storage/engine" 9 | require "action_controller/railtie" 10 | require "action_mailer/railtie" 11 | require "action_mailbox/engine" 12 | require "action_text/engine" 13 | require "action_view/railtie" 14 | require "action_cable/engine" 15 | # require "rails/test_unit/railtie" 16 | 17 | # Require the gems listed in Gemfile, including any gems 18 | # you've limited to :test, :development, or :production. 19 | Bundler.require(*Rails.groups) 20 | 21 | module ShadcnRails 22 | class Application < Rails::Application 23 | # Initialize configuration defaults for originally generated Rails version. 24 | config.load_defaults 7.0 25 | 26 | # Configuration for the application, engines, and railties goes here. 27 | # 28 | # These settings can be overridden in specific environments using the files 29 | # in config/environments, which are processed later. 30 | # 31 | # config.time_zone = "Central Time (US & Canada)" 32 | # config.eager_load_paths << Rails.root.join("extras") 33 | 34 | # Don't generate system test files. 35 | config.generators.system_tests = :rspec 36 | config.generators.test_framework :rspec 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 2 | 3 | require "bundler/setup" # Set up gems listed in the Gemfile. 4 | require "bootsnap/setup" # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: redis 3 | url: redis://localhost:6379/1 4 | 5 | test: 6 | adapter: test 7 | 8 | production: 9 | adapter: redis 10 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 11 | channel_prefix: shadcn_rails_production 12 | -------------------------------------------------------------------------------- /config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | BsVjYHKGdjdGxAYw+a5vYXdla/aivdh/9DktuEnJmCgyaULJpH0YqU/T+p078Gyxy7Pq6olkGj86S0WUUC4WVV8vWkH8B6ayONGyxxw71H/lFA0MGyxlWrVAAiJSQiG+2TlVGWH6KyEIYlpiXm/rQ4minYA4pYcLnI13/tGA2GHqtEMmVtaGmir3gzm0MgtlrqwfGYwV+i+Fp079Ci6GJODPJ9L9TkCkeFAaaJHr06HIkuJo0e/tB0Wsv5bYeYXRAp4gxbOnAgigWlBdblbmjSL5BQuhUV623r87A4HCQCuUayWEcWLePk0FMyAql6OlLbd0bWX835JeT1nJ2sukwPVsduGVfTMlvAyMg9j/zz8sKAdW63nR3RpPKw3YvKLt4mBe+Bs//u8e+ApNnxrQ/ro/pu3/Be5aYvtw--G4FDerS9avMFHcsB--9jJ0fjil7Q3SYWoF6dC9rw== -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite. Versions 3.8.0 and up are supported. 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem "sqlite3" 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/importmap.rb: -------------------------------------------------------------------------------- 1 | # Pin npm packages by running ./bin/importmap 2 | 3 | pin "application", preload: true 4 | pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true 5 | pin "@hotwired/stimulus", to: "https://ga.jspm.io/npm:@hotwired/stimulus@3.2.1/dist/stimulus.js" 6 | pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true 7 | 8 | pin_all_from "app/javascript/controllers", under: "controllers" 9 | pin_all_from "app/javascript/utils", under: "utils" 10 | 11 | pin "@kanety/stimulus-static-actions", to: "https://ga.jspm.io/npm:@kanety/stimulus-static-actions@1.0.1/dist/index.modern.js", preload: true 12 | pin "highlight.js", to: "https://ga.jspm.io/npm:highlight.js@11.8.0/es/index.js", preload: true 13 | pin "stimulus-use", to: "https://ga.jspm.io/npm:stimulus-use@0.52.2/dist/index.js", preload: true 14 | pin "stimulus-dropdown", to: "https://ga.jspm.io/npm:stimulus-dropdown@2.1.0/dist/stimulus-dropdown.mjs", preload: true 15 | pin "hotkeys-js", to: "https://ga.jspm.io/npm:hotkeys-js@3.10.4/dist/hotkeys.esm.js", preload: true 16 | pin "@popperjs/core", to: "https://ga.jspm.io/npm:@popperjs/core@2.11.8/lib/index.js", preload: true 17 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = "1.0" 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in the app/assets 11 | # folder are already added. 12 | Rails.application.config.assets.precompile += %w[lambda.light.scss lambda.dark.scss] 13 | -------------------------------------------------------------------------------- /config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy. 4 | # See the Securing Rails Applications Guide for more information: 5 | # https://guides.rubyonrails.org/security.html#content-security-policy-header 6 | 7 | # Rails.application.configure do 8 | # config.content_security_policy do |policy| 9 | # policy.default_src :self, :https 10 | # policy.font_src :self, :https, :data 11 | # policy.img_src :self, :https, :data 12 | # policy.object_src :none 13 | # policy.script_src :self, :https 14 | # policy.style_src :self, :https 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | # 19 | # # Generate session nonces for permitted importmap and inline scripts 20 | # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } 21 | # config.content_security_policy_nonce_directives = %w(script-src) 22 | # 23 | # # Report violations without enforcing the policy. 24 | # # config.content_security_policy_report_only = true 25 | # end 26 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure parameters to be filtered from the log file. Use this to limit dissemination of 4 | # sensitive information. See the ActiveSupport::ParameterFilter documentation for supported 5 | # notations and behaviors. 6 | Rails.application.config.filter_parameters += [ 7 | :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn 8 | ] 9 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, "\\1en" 8 | # inflect.singular /^(ox)en/i, "\\1" 9 | # inflect.irregular "person", "people" 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym "RESTful" 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/markdown.rb: -------------------------------------------------------------------------------- 1 | require "redcarpet" 2 | require "rouge" 3 | require "rouge/plugins/redcarpet" 4 | 5 | class HTML < Redcarpet::Render::HTML 6 | include Rouge::Plugins::Redcarpet # yep, that's it. 7 | end 8 | 9 | # config/initializers/markdown.rb 10 | 11 | # We define a module that can parse markdown to HTML with its `call` method 12 | module MarkdownHandler 13 | def self.erb 14 | @erb ||= ActionView::Template.registered_template_handler(:erb) 15 | end 16 | 17 | def self.call(template, source) 18 | compiled_source = erb.call(template, source) 19 | "Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(with_toc_data: true), extensions = {fenced_code_blocks: true}).render(begin;#{compiled_source};end).html_safe" 20 | end 21 | end 22 | 23 | # Now we tell Rails to process any files with the `.md` extension using our new MarkdownHandler 24 | ActionView::Template.register_template_handler :md, MarkdownHandler 25 | -------------------------------------------------------------------------------- /config/initializers/permissions_policy.rb: -------------------------------------------------------------------------------- 1 | # Define an application-wide HTTP permissions policy. For further 2 | # information see https://developers.google.com/web/updates/2018/06/feature-policy 3 | # 4 | # Rails.application.config.permissions_policy do |f| 5 | # f.camera :none 6 | # f.gyroscope :none 7 | # f.microphone :none 8 | # f.usb :none 9 | # f.fullscreen :self 10 | # f.payment :self, "https://secure.example.com" 11 | # end 12 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t "hello" 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t("hello") %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # "true": "foo" 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at https://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /config/render.yml: -------------------------------------------------------------------------------- 1 | services: 2 | - type: web 3 | name: shadcn-rails 4 | runtime: ruby 5 | buildCommand: "./bin/render-build.sh" 6 | startCommand: "bundle exec puma -C config/puma.rb" 7 | envVars: 8 | - key: RAILS_MASTER_KEY 9 | sync: false 10 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html 3 | 4 | get "/examples/:example", to: "application#examples", as: :example 5 | get "/docs/components/:component", to: "components#show", as: :component 6 | get "/docs/components", to: redirect("/docs/components/accordion") 7 | get "/components", to: redirect("/docs/components/accordion"), as: :components 8 | get "/docs/:id" => "documentation#show", :as => :documentation, :format => false 9 | get "/docs", to: "documentation#index", as: :documentation_index 10 | 11 | # This is just for the forms component 12 | resources :users 13 | 14 | # Defines the root path route ("/") 15 | root "application#index" 16 | end 17 | -------------------------------------------------------------------------------- /config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket-<%= Rails.env %> 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket-<%= Rails.env %> 23 | 24 | # Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name-<%= Rails.env %> 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /config/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const shadcnConfig = require("./shadcn.tailwind.js"); 2 | 3 | module.exports = { 4 | ...shadcnConfig, 5 | theme: { 6 | extend: { 7 | colors: { 8 | ...shadcnConfig.theme.extend.colors, 9 | pink: "hsl(var(--pink))", 10 | success: { 11 | DEFAULT: "hsl(var(--success))", 12 | foreground: "hsl(var(--success-foreground))", 13 | }, 14 | info: { 15 | DEFAULT: "hsl(var(--info))", 16 | foreground: "hsl(var(--info-foreground))", 17 | }, 18 | attention: { 19 | DEFAULT: "hsl(var(--attention))", 20 | foreground: "hsl(var(--attention-foreground))", 21 | }, 22 | }, 23 | }, 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: "Star Wars" }, { name: "Lord of the Rings" }]) 7 | # Character.create(name: "Luke", movie: movies.first) 8 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/lib/assets/.keep -------------------------------------------------------------------------------- /lib/shadcn-ui/shadcn-ui.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "version" 4 | 5 | module ShadncUi 6 | class Error < StandardError; end 7 | # Your code goes here... 8 | end 9 | -------------------------------------------------------------------------------- /lib/shadcn-ui/version.rb: -------------------------------------------------------------------------------- 1 | module ShadcnUi 2 | VERSION = "0.0.14" 3 | end 4 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/lib/tasks/.keep -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/log/.keep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "tailwindcss-animate": "^1.0.6" 4 | }, 5 | "devDependencies": { 6 | "@tailwindcss/aspect-ratio": "^0.4.2", 7 | "@tailwindcss/container-queries": "^0.1.1", 8 | "@tailwindcss/forms": "^0.5.3", 9 | "@tailwindcss/typography": "^0.5.9", 10 | "prettier": "^3.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /public/accordion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/public/accordion.png -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/public/favicon.ico -------------------------------------------------------------------------------- /public/og.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/public/og.jpg -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /shadcn-ui.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "lib/shadcn-ui/shadcn-ui" 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "shadcn-ui" 7 | spec.version = ShadcnUi::VERSION 8 | spec.authors = ["Avi Flombaum"] 9 | spec.email = ["git@avi.nyc"] 10 | 11 | spec.homepage = "https://shadcn.rails-components.com" 12 | spec.summary = "Provides the shadcn-ui component library to a Ruby on Rails application." 13 | spec.description = "This gem is a documentation site and gem that will copy components from the shadcn-ui library into a Ruby on Rails application." 14 | spec.homepage = "https://github.com/aviflombaum/shadcn-rails" 15 | spec.license = "MIT" 16 | spec.required_ruby_version = ">= 2.6.0" 17 | 18 | spec.metadata["homepage_uri"] = spec.homepage 19 | spec.metadata["source_code_uri"] = "https://github.com/aviflombaum/shadcn-rails" 20 | spec.metadata["changelog_uri"] = "https://github.com/aviflombaum/shadcn-rails/tree/main/CHANGELOG.md" 21 | 22 | # Specify which files should be added to the gem when it is released. 23 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 24 | spec.files = Dir.chdir(__dir__) do 25 | `git ls-files -z`.split("\x0").reject do |f| 26 | (File.expand_path(f) == __FILE__) || 27 | f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile]) 28 | end 29 | end 30 | spec.bindir = "bin" 31 | spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } 32 | spec.require_paths = ["lib"] 33 | 34 | # Uncomment to register a new dependency of your gem 35 | spec.add_dependency "tailwind_merge", "~> 0.12" 36 | 37 | # For more information and examples about making a new gem, check out our 38 | # guide at: https://bundler.io/guides/creating_gem.html 39 | end 40 | -------------------------------------------------------------------------------- /sig/shadcn-ui.rbs: -------------------------------------------------------------------------------- 1 | module ShadncUi 2 | VERSION: String 3 | # See the writing guide of rbs: https://github.com/ruby/rbs#guides 4 | end 5 | -------------------------------------------------------------------------------- /spec/generators/alert_dialog_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | require_relative "../../lib/generators/shadcn-ui_generator" 3 | 4 | RSpec.describe ShadcnUiGenerator, type: :generator do 5 | let(:component_name) { "alert-dialog" } 6 | let(:rails_root) { "#{Rails.root}/spec/dummy_app/tmp" } 7 | 8 | before(:each) do 9 | FileUtils.mkdir_p("#{rails_root}/app") 10 | FileUtils.mkdir_p("#{rails_root}/config") 11 | FileUtils.mkdir_p("#{rails_root}/app/views/components/ui") 12 | FileUtils.mkdir_p("#{rails_root}/app/helpers/components") 13 | FileUtils.mkdir_p("#{rails_root}/app/javascript/controllers/ui") 14 | FileUtils.mkdir_p("#{rails_root}/app/assets/stylesheets") 15 | FileUtils.touch("#{rails_root}/app/assets/stylesheets/application.tailwind.css") 16 | end 17 | 18 | after(:all) do 19 | FileUtils.rm_rf("#{Rails.root}/spec/dummy_app/tmp") 20 | end 21 | 22 | it "copies the component files to the correct destination" do 23 | described_class.start([component_name, rails_root]) 24 | 25 | expect(File).to exist("#{rails_root}/app/views/components/ui/_alert_dialog.html.erb") 26 | expect(File).to exist("#{rails_root}/app/helpers/components/alert_dialog_helper.rb") 27 | end 28 | 29 | it "copies the dependency files to the correct destination" do 30 | expect(File).to exist("#{rails_root}/app/views/components/ui/shared/_backdrop.html.erb") 31 | expect(File).to exist("#{rails_root}/app/helpers/components/dialog_helper.rb") 32 | expect(File).to exist("#{rails_root}/app/views/components/ui/_dialog.html.erb") 33 | expect(File).to exist("#{rails_root}/app/javascript/controllers/ui/dialog_controller.js") 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/generators/dialog_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | require_relative "../../lib/generators/shadcn-ui_generator" 3 | 4 | RSpec.describe ShadcnUiGenerator, type: :generator do 5 | let(:component_name) { "dialog" } 6 | let(:rails_root) { "#{Rails.root}/spec/dummy_app/tmp" } 7 | 8 | before(:each) do 9 | FileUtils.mkdir_p("#{rails_root}/app") 10 | FileUtils.mkdir_p("#{rails_root}/config") 11 | FileUtils.mkdir_p("#{rails_root}/app/views/components/ui") 12 | FileUtils.mkdir_p("#{rails_root}/app/helpers/components") 13 | FileUtils.mkdir_p("#{rails_root}/app/javascript/controllers/ui") 14 | FileUtils.mkdir_p("#{rails_root}/app/assets/stylesheets") 15 | FileUtils.touch("#{rails_root}/app/assets/stylesheets/application.tailwind.css") 16 | end 17 | 18 | after(:all) do 19 | FileUtils.rm_rf("#{Rails.root}/spec/dummy_app/tmp") 20 | end 21 | 22 | it "copies the component files to the correct destination" do 23 | described_class.start([component_name, rails_root]) 24 | 25 | expect(File).to exist("#{rails_root}/app/views/components/ui/_#{component_name}.html.erb") 26 | expect(File).to exist("#{rails_root}/app/javascript/controllers/ui/#{component_name}_controller.js") 27 | expect(File).to exist("#{rails_root}/app/helpers/components/#{component_name}_helper.rb") 28 | end 29 | 30 | it "copies the dependency files to the correct destination" do 31 | expect(File).to exist("#{rails_root}/app/views/components/ui/shared/_backdrop.html.erb") 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /storage/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/storage/.keep -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/tmp/.keep -------------------------------------------------------------------------------- /tmp/pids/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/tmp/pids/.keep -------------------------------------------------------------------------------- /tmp/storage/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/tmp/storage/.keep -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/vendor/.keep -------------------------------------------------------------------------------- /vendor/javascript/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aviflombaum/shadcn-rails/d39354ce32e527061d4fbfe4958e6025626cdece/vendor/javascript/.keep --------------------------------------------------------------------------------