├── .babelrc ├── .browserslistrc ├── .gitattributes ├── .github └── workflows │ ├── release.yml │ └── rspec.yml ├── .gitignore ├── .rspec ├── CONTRIBUTING.md ├── Gemfile ├── Guardfile ├── LICENSE ├── README.md ├── Rakefile ├── app ├── assets │ ├── bundle │ │ └── trestle │ │ │ ├── admin.css │ │ │ ├── admin.js │ │ │ ├── fa-brands-400.ttf │ │ │ ├── fa-brands-400.woff2 │ │ │ ├── fa-regular-400.ttf │ │ │ ├── fa-regular-400.woff2 │ │ │ ├── fa-solid-900.ttf │ │ │ ├── fa-solid-900.woff2 │ │ │ ├── locale │ │ │ ├── cs.js │ │ │ ├── de.js │ │ │ ├── en.js │ │ │ ├── es-MX.js │ │ │ ├── es.js │ │ │ ├── flatpickr │ │ │ │ ├── ar-dz.js │ │ │ │ ├── ar.js │ │ │ │ ├── at.js │ │ │ │ ├── az.js │ │ │ │ ├── be.js │ │ │ │ ├── bg.js │ │ │ │ ├── bn.js │ │ │ │ ├── bs.js │ │ │ │ ├── cat.js │ │ │ │ ├── ckb.js │ │ │ │ ├── cs.js │ │ │ │ ├── cy.js │ │ │ │ ├── da.js │ │ │ │ ├── de.js │ │ │ │ ├── default.js │ │ │ │ ├── eo.js │ │ │ │ ├── es.js │ │ │ │ ├── et.js │ │ │ │ ├── fa.js │ │ │ │ ├── fi.js │ │ │ │ ├── fo.js │ │ │ │ ├── fr.js │ │ │ │ ├── ga.js │ │ │ │ ├── gr.js │ │ │ │ ├── he.js │ │ │ │ ├── hi.js │ │ │ │ ├── hr.js │ │ │ │ ├── hu.js │ │ │ │ ├── hy.js │ │ │ │ ├── id.js │ │ │ │ ├── is.js │ │ │ │ ├── it.js │ │ │ │ ├── ja.js │ │ │ │ ├── ka.js │ │ │ │ ├── km.js │ │ │ │ ├── ko.js │ │ │ │ ├── kz.js │ │ │ │ ├── lt.js │ │ │ │ ├── lv.js │ │ │ │ ├── mk.js │ │ │ │ ├── mn.js │ │ │ │ ├── ms.js │ │ │ │ ├── my.js │ │ │ │ ├── nl.js │ │ │ │ ├── nn.js │ │ │ │ ├── no.js │ │ │ │ ├── pa.js │ │ │ │ ├── pl.js │ │ │ │ ├── pt.js │ │ │ │ ├── ro.js │ │ │ │ ├── ru.js │ │ │ │ ├── si.js │ │ │ │ ├── sk.js │ │ │ │ ├── sl.js │ │ │ │ ├── sq.js │ │ │ │ ├── sr-cyr.js │ │ │ │ ├── sr.js │ │ │ │ ├── sv.js │ │ │ │ ├── th.js │ │ │ │ ├── tr.js │ │ │ │ ├── uk.js │ │ │ │ ├── uz.js │ │ │ │ ├── uz_latn.js │ │ │ │ ├── vn.js │ │ │ │ ├── zh-tw.js │ │ │ │ └── zh.js │ │ │ ├── fr.js │ │ │ ├── ko.js │ │ │ ├── lv.js │ │ │ ├── nl.js │ │ │ ├── pl.js │ │ │ ├── pt-BR.js │ │ │ ├── vi.js │ │ │ └── zh-CN.js │ │ │ └── photoswipe-2d522a3abaa59f8a8f73.digested.js │ ├── javascripts │ │ └── trestle │ │ │ └── custom.js │ ├── sprockets │ │ └── trestle │ │ │ ├── _custom.css │ │ │ ├── custom.css │ │ │ ├── icons │ │ │ └── font-awesome.css.erb │ │ │ └── manifest.js │ └── stylesheets │ │ └── trestle │ │ └── custom.css ├── controllers │ ├── concerns │ │ └── trestle │ │ │ ├── controller │ │ │ ├── breadcrumbs.rb │ │ │ ├── callbacks.rb │ │ │ ├── helpers.rb │ │ │ ├── layout.rb │ │ │ ├── location.rb │ │ │ ├── modal.rb │ │ │ ├── title.rb │ │ │ ├── toolbars.rb │ │ │ └── turbo_stream.rb │ │ │ └── resource │ │ │ └── controller │ │ │ ├── actions.rb │ │ │ ├── data_methods.rb │ │ │ ├── redirection.rb │ │ │ └── toolbar.rb │ └── trestle │ │ ├── admin_controller.rb │ │ ├── application_controller.rb │ │ ├── dashboard_controller.rb │ │ └── resource_controller.rb ├── helpers │ └── trestle │ │ ├── avatar_helper.rb │ │ ├── card_helper.rb │ │ ├── container_helper.rb │ │ ├── display_helper.rb │ │ ├── flash_helper.rb │ │ ├── form_helper.rb │ │ ├── format_helper.rb │ │ ├── gravatar_helper.rb │ │ ├── grid_helper.rb │ │ ├── headings_helper.rb │ │ ├── hook_helper.rb │ │ ├── i18n_helper.rb │ │ ├── icon_helper.rb │ │ ├── layout_helper.rb │ │ ├── modal_helper.rb │ │ ├── navigation_helper.rb │ │ ├── pagination_helper.rb │ │ ├── params_helper.rb │ │ ├── sort_helper.rb │ │ ├── status_helper.rb │ │ ├── tab_helper.rb │ │ ├── table_helper.rb │ │ ├── timestamp_helper.rb │ │ ├── title_helper.rb │ │ ├── toolbars_helper.rb │ │ ├── turbo │ │ ├── frame_helper.rb │ │ ├── stream_helper.rb │ │ └── tag_builder.rb │ │ └── url_helper.rb └── views │ ├── kaminari │ └── trestle │ │ ├── _first_page.html.erb │ │ ├── _gap.html.erb │ │ ├── _last_page.html.erb │ │ ├── _page.html.erb │ │ └── _paginator.html.erb │ ├── layouts │ └── trestle │ │ ├── admin.html.erb │ │ └── modal.html.erb │ └── trestle │ ├── _i18n.html.erb │ ├── _theme.html.erb │ ├── admin │ └── index.html.erb │ ├── application │ ├── _header.html.erb │ ├── _layout.html.erb │ ├── _modal.html.erb │ ├── _tabs.html.erb │ └── _utilities.html.erb │ ├── dashboard │ └── index.html.erb │ ├── flash │ ├── _alert.html.erb │ ├── _debug.html.erb │ └── _flash.html.erb │ ├── resource │ ├── _form.html.erb │ ├── _scopes.html.erb │ ├── _table.html.erb │ ├── create.turbo_stream.erb │ ├── destroy.turbo_stream.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ ├── show.html.erb │ └── update.turbo_stream.erb │ ├── shared │ ├── _footer.html.erb │ ├── _header.html.erb │ ├── _sidebar.html.erb │ └── _title.html.erb │ └── table │ ├── _pagination.html.erb │ └── _table.html.erb ├── bin ├── console └── setup ├── config ├── locales │ ├── cs.rb │ ├── cs.yml │ ├── de.rb │ ├── de.yml │ ├── en.rb │ ├── en.yml │ ├── es-MX.yml │ ├── es.yml │ ├── fr.rb │ ├── fr.yml │ ├── ko.rb │ ├── ko.yml │ ├── lv.rb │ ├── lv.yml │ ├── nl.rb │ ├── nl.yml │ ├── pl.rb │ ├── pl.yml │ ├── pt-BR.rb │ ├── pt-BR.yml │ ├── vi.rb │ ├── vi.yml │ ├── zh-CN.rb │ └── zh-CN.yml └── routes.rb ├── frontend ├── css │ ├── _support.scss │ ├── components │ │ ├── _alerts.scss │ │ ├── _avatar.scss │ │ ├── _background.scss │ │ ├── _badges.scss │ │ ├── _breadcrumbs.scss │ │ ├── _buttons.scss │ │ ├── _datepicker.scss │ │ ├── _dropdown.scss │ │ ├── _forms.scss │ │ ├── _grid.scss │ │ ├── _media-grid.scss │ │ ├── _modal.scss │ │ ├── _pagination.scss │ │ ├── _photoswipe.scss │ │ ├── _popover.scss │ │ ├── _scopes.scss │ │ ├── _select.scss │ │ ├── _sort.scss │ │ ├── _table.scss │ │ ├── _tabs.scss │ │ ├── _tags.scss │ │ ├── _timestamp.scss │ │ ├── _toolbars.scss │ │ └── _turbo.scss │ ├── core │ │ ├── _bootstrap.scss │ │ ├── _dependencies.scss │ │ ├── _functions.scss │ │ ├── _mixins.scss │ │ ├── _theme.scss │ │ └── _typography.scss │ ├── icons │ │ └── _fontawesome.scss │ ├── index.scss │ ├── layout │ │ ├── _base.scss │ │ ├── _content-header.scss │ │ ├── _footer.scss │ │ ├── _header.scss │ │ ├── _main-content.scss │ │ ├── _navigation.scss │ │ └── _sidebar.scss │ ├── support │ │ ├── _sprockets.scss │ │ └── _webpack.scss │ └── variables │ │ ├── _bootstrap.scss │ │ ├── _maps.scss │ │ └── _trestle.scss ├── images │ └── bright-squares.png ├── index.js └── js │ ├── controllers │ ├── application_controller.js │ ├── batch_action_controller.js │ ├── checkbox_select_controller.js │ ├── confirm_controller.js │ ├── confirm_delete_controller.js │ ├── datepicker_controller.js │ ├── datetimepicker_controller.js │ ├── deprecated │ │ └── init_controller.js │ ├── flatpickr_controller.js │ ├── follow_url_controller.js │ ├── form_error_controller.js │ ├── form_loading_controller.js │ ├── gallery_controller.js │ ├── index.js │ ├── keyboard_submit_controller.js │ ├── lightbox_controller.js │ ├── mobile_sidebar_controller.js │ ├── modal_controller.js │ ├── modal_frame_controller.js │ ├── modal_trigger_controller.js │ ├── navigation_controller.js │ ├── navigation_tooltip_controller.js │ ├── popover_controller.js │ ├── reloadable_controller.js │ ├── select_controller.js │ ├── sidebar_controller.js │ ├── tab_errors_controller.js │ ├── tabs_controller.js │ ├── timepicker_controller.js │ ├── toggle_attr_controller.js │ ├── toggle_class_controller.js │ ├── tooltip_controller.js │ └── wrapper_controller.js │ ├── core │ ├── backdrop.js │ ├── cookie.js │ ├── error_modal.js │ ├── fetch.js │ ├── i18n.js │ ├── modal.js │ ├── stream_actions.js │ └── turbo_errors.js │ ├── deprecated │ ├── events.js │ └── tooltip.js │ ├── index.js │ ├── mixins │ ├── index.js │ └── photoswipe.js │ └── util │ └── bootstrap.js ├── gemfiles ├── rails-6.0.gemfile ├── rails-6.1.gemfile ├── rails-7.0-propshaft.gemfile ├── rails-7.0.gemfile ├── rails-7.1-propshaft.gemfile ├── rails-7.1-sassc-rails.gemfile ├── rails-7.1.gemfile ├── rails-7.2-propshaft.gemfile ├── rails-7.2.gemfile ├── rails-8.0-propshaft.gemfile └── rails-8.0.gemfile ├── i18n ├── config.yml ├── environment.rb ├── export └── template.erb ├── lib ├── generators │ └── trestle │ │ ├── admin │ │ ├── admin_generator.rb │ │ └── templates │ │ │ ├── admin.rb.erb │ │ │ └── index.html.erb │ │ ├── install │ │ ├── install_generator.rb │ │ └── templates │ │ │ ├── _custom.css │ │ │ ├── _custom.scss │ │ │ ├── custom.js │ │ │ └── trestle.rb.erb │ │ └── resource │ │ ├── resource_generator.rb │ │ └── templates │ │ └── admin.rb.erb ├── trestle.rb └── trestle │ ├── adapters.rb │ ├── adapters │ ├── active_record_adapter.rb │ ├── adapter.rb │ ├── draper_adapter.rb │ └── sequel_adapter.rb │ ├── admin.rb │ ├── admin │ └── builder.rb │ ├── attribute.rb │ ├── breadcrumb.rb │ ├── builder.rb │ ├── color.rb │ ├── configurable.rb │ ├── configuration.rb │ ├── debug_errors.rb │ ├── display.rb │ ├── engine.rb │ ├── evaluation_context.rb │ ├── form.rb │ ├── form │ ├── automatic.rb │ ├── builder.rb │ ├── field.rb │ ├── fields.rb │ ├── fields │ │ ├── check_box.rb │ │ ├── check_box_helpers.rb │ │ ├── collection_check_boxes.rb │ │ ├── collection_radio_buttons.rb │ │ ├── collection_select.rb │ │ ├── color_field.rb │ │ ├── date_field.rb │ │ ├── date_picker.rb │ │ ├── date_select.rb │ │ ├── datetime_field.rb │ │ ├── datetime_select.rb │ │ ├── email_field.rb │ │ ├── file_field.rb │ │ ├── form_control.rb │ │ ├── form_group.rb │ │ ├── grouped_collection_select.rb │ │ ├── month_field.rb │ │ ├── number_field.rb │ │ ├── password_field.rb │ │ ├── radio_button.rb │ │ ├── radio_button_helpers.rb │ │ ├── range_field.rb │ │ ├── search_field.rb │ │ ├── select.rb │ │ ├── static_field.rb │ │ ├── tag_select.rb │ │ ├── telephone_field.rb │ │ ├── text_area.rb │ │ ├── text_field.rb │ │ ├── time_field.rb │ │ ├── time_select.rb │ │ ├── time_zone_select.rb │ │ ├── url_field.rb │ │ └── week_field.rb │ └── renderer.rb │ ├── hook.rb │ ├── hook │ ├── helpers.rb │ └── set.rb │ ├── lazy.rb │ ├── model_name.rb │ ├── navigation.rb │ ├── navigation │ ├── block.rb │ ├── group.rb │ └── item.rb │ ├── options.rb │ ├── registry.rb │ ├── reloader.rb │ ├── resource.rb │ ├── resource │ ├── adapter_methods.rb │ ├── builder.rb │ ├── collection.rb │ └── toolbar.rb │ ├── scopes.rb │ ├── scopes │ ├── block.rb │ ├── definition.rb │ └── scope.rb │ ├── sprockets_compressor.rb │ ├── tab.rb │ ├── table.rb │ ├── table │ ├── actions_column.rb │ ├── automatic.rb │ ├── builder.rb │ ├── column.rb │ ├── row.rb │ └── select_column.rb │ ├── toolbar.rb │ ├── toolbar │ ├── builder.rb │ ├── context.rb │ ├── item.rb │ └── menu.rb │ └── version.rb ├── package.json ├── sandbox ├── Rakefile ├── app │ ├── admin │ │ ├── articles_admin.rb │ │ ├── categories_admin.rb │ │ ├── components │ │ │ ├── alerts_admin.rb │ │ │ ├── buttons_admin.rb │ │ │ ├── forms_admin.rb │ │ │ ├── grid_admin.rb │ │ │ ├── icons_admin.rb │ │ │ ├── media_admin.rb │ │ │ ├── miscellaneous_admin.rb │ │ │ ├── theme_admin.rb │ │ │ └── typography_admin.rb │ │ ├── offices_admin.rb │ │ └── users_admin.rb │ ├── assets │ │ ├── config │ │ │ └── manifest.js │ │ ├── images │ │ │ ├── .keep │ │ │ ├── logo-small.svg │ │ │ └── logo.svg │ │ ├── javascripts │ │ │ ├── application.js │ │ │ └── trestle │ │ │ │ ├── custom.js │ │ │ │ └── custom │ │ │ │ └── controllers │ │ │ │ ├── modal_demo │ │ │ │ ├── modal_controller.js │ │ │ │ └── trigger_controller.js │ │ │ │ └── theme_controller.js │ │ └── stylesheets │ │ │ ├── application.css │ │ │ └── trestle │ │ │ └── _custom.css │ ├── channels │ │ └── application_cable │ │ │ ├── channel.rb │ │ │ └── connection.rb │ ├── controllers │ │ ├── application_controller.rb │ │ └── concerns │ │ │ └── .keep │ ├── fields │ │ └── custom_field.rb │ ├── helpers │ │ ├── application_helper.rb │ │ └── image_helper.rb │ ├── jobs │ │ └── application_job.rb │ ├── mailers │ │ └── application_mailer.rb │ ├── models │ │ ├── application_record.rb │ │ ├── article.rb │ │ ├── category.rb │ │ ├── color.rb │ │ ├── concerns │ │ │ └── .keep │ │ ├── font_awesome.rb │ │ ├── office.rb │ │ └── user.rb │ └── views │ │ ├── admin │ │ └── components │ │ │ ├── alerts │ │ │ └── index.html.erb │ │ │ ├── buttons │ │ │ ├── _basic.html.erb │ │ │ ├── _dropdowns.html.erb │ │ │ ├── _groups.html.erb │ │ │ ├── _icon.html.erb │ │ │ ├── _outline.html.erb │ │ │ ├── _sizes.html.erb │ │ │ └── index.html.erb │ │ │ ├── forms │ │ │ └── index.html.erb │ │ │ ├── grid │ │ │ └── index.html.erb │ │ │ ├── icons │ │ │ └── index.html.erb │ │ │ ├── media │ │ │ └── index.html.erb │ │ │ ├── miscellaneous │ │ │ ├── _avatars.html.erb │ │ │ ├── _badges.html.erb │ │ │ ├── _cards.html.erb │ │ │ ├── _modals.html.erb │ │ │ ├── _progress.html.erb │ │ │ ├── _tabs_example.html.erb │ │ │ ├── _tags.html.erb │ │ │ ├── _timestamps.html.erb │ │ │ ├── _tooltips.html.erb │ │ │ ├── index.html.erb │ │ │ ├── modal.html.erb │ │ │ └── modal_post.html.erb │ │ │ ├── theme │ │ │ ├── _accordion.html.erb │ │ │ ├── _alerts.html.erb │ │ │ ├── _backgrounds.html.erb │ │ │ ├── _badges.html.erb │ │ │ ├── _buttons.html.erb │ │ │ ├── _fields.html.erb │ │ │ ├── _modal.html.erb │ │ │ ├── _table.html.erb │ │ │ ├── _typography.html.erb │ │ │ └── index.html.erb │ │ │ └── typography │ │ │ ├── _blockquotes.html.erb │ │ │ ├── _code.html.erb │ │ │ ├── _colors.html.erb │ │ │ ├── _headings.html.erb │ │ │ ├── _inline.html.erb │ │ │ ├── _lists.html.erb │ │ │ └── index.html.erb │ │ └── layouts │ │ ├── application.html.erb │ │ ├── mailer.html.erb │ │ └── mailer.text.erb ├── bin │ ├── bundle │ ├── rails │ ├── rake │ ├── setup │ └── update ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── cable.yml │ ├── database.yml │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── application_controller_renderer.rb │ │ ├── assets.rb │ │ ├── backtrace_silencers.rb │ │ ├── cookies_serializer.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ ├── mime_types.rb │ │ ├── session_store.rb │ │ ├── trestle.rb │ │ └── wrap_parameters.rb │ ├── locales │ │ └── en.yml │ ├── puma.rb │ ├── routes.rb │ ├── secrets.yml │ └── spring.rb ├── db │ ├── migrate │ │ ├── 20210712064032_create_offices.rb │ │ ├── 20210712071021_create_users.rb │ │ ├── 20210712104832_create_categories.rb │ │ ├── 20210713021857_create_articles.rb │ │ └── 20210714014917_create_articles_categories.rb │ ├── schema.rb │ └── seeds.rb ├── lib │ └── assets │ │ └── .keep ├── log │ └── .keep └── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ ├── apple-touch-icon-precomposed.png │ ├── apple-touch-icon.png │ └── favicon.ico ├── spec ├── dummy │ ├── Rakefile │ ├── app │ │ ├── admin │ │ │ ├── automatic_admin.rb │ │ │ ├── modal_admin.rb │ │ │ ├── posts_admin.rb │ │ │ ├── scopes_admin.rb │ │ │ └── singular_post_admin.rb │ │ ├── assets │ │ │ ├── config │ │ │ │ └── manifest.js │ │ │ ├── images │ │ │ │ └── .keep │ │ │ ├── javascripts │ │ │ │ ├── application.js │ │ │ │ └── trestle │ │ │ │ │ └── custom.js │ │ │ └── stylesheets │ │ │ │ ├── application.css │ │ │ │ └── trestle │ │ │ │ └── _custom.css │ │ ├── controllers │ │ │ ├── application_controller.rb │ │ │ └── concerns │ │ │ │ └── .keep │ │ ├── helpers │ │ │ └── application_helper.rb │ │ ├── models │ │ │ ├── application_record.rb │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ └── post.rb │ │ └── views │ │ │ └── layouts │ │ │ ├── application.html.erb │ │ │ ├── mailer.html.erb │ │ │ └── mailer.text.erb │ ├── bin │ │ ├── bundle │ │ ├── rails │ │ ├── rake │ │ ├── setup │ │ └── update │ ├── config.ru │ ├── config │ │ ├── application.rb │ │ ├── boot.rb │ │ ├── cable.yml │ │ ├── database.yml │ │ ├── environment.rb │ │ ├── environments │ │ │ ├── development.rb │ │ │ ├── production.rb │ │ │ └── test.rb │ │ ├── initializers │ │ │ ├── application_controller_renderer.rb │ │ │ ├── assets.rb │ │ │ ├── backtrace_silencers.rb │ │ │ ├── cookies_serializer.rb │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── inflections.rb │ │ │ ├── mime_types.rb │ │ │ ├── session_store.rb │ │ │ ├── trestle.rb │ │ │ └── wrap_parameters.rb │ │ ├── locales │ │ │ └── en.yml │ │ ├── puma.rb │ │ ├── routes.rb │ │ ├── secrets.yml │ │ └── spring.rb │ ├── db │ │ ├── migrate │ │ │ └── 20170915062615_create_posts.rb │ │ └── schema.rb │ ├── lib │ │ └── assets │ │ │ └── .keep │ ├── log │ │ └── .keep │ ├── public │ │ ├── 404.html │ │ ├── 422.html │ │ ├── 500.html │ │ ├── apple-touch-icon-precomposed.png │ │ ├── apple-touch-icon.png │ │ └── favicon.ico │ └── tmp │ │ └── .keep ├── feature │ ├── automatic_resource_spec.rb │ ├── modal_form_spec.rb │ ├── resource_spec.rb │ ├── scopes_spec.rb │ ├── shakedown_spec.rb │ └── singular_resource_spec.rb ├── generators │ ├── admin_generator_spec.rb │ └── resource_generator_spec.rb ├── integration │ ├── resource_controller_spec.rb │ └── routes_spec.rb ├── spec_helper.rb ├── support │ ├── capybara.rb │ ├── contexts │ │ ├── countries.rb │ │ ├── form.rb │ │ └── template.rb │ ├── feature_helper.rb │ ├── i18n_helper.rb │ └── matchers │ │ └── have_accessor.rb └── trestle │ ├── admin │ └── builder_spec.rb │ ├── admin_spec.rb │ ├── attribute_spec.rb │ ├── breadcrumb_spec.rb │ ├── color_spec.rb │ ├── configurable_spec.rb │ ├── configuration_spec.rb │ ├── debug_errors_spec.rb │ ├── display_spec.rb │ ├── evaluation_context_spec.rb │ ├── form │ ├── builder_spec.rb │ ├── fields │ │ ├── check_box_spec.rb │ │ ├── collection_check_boxes_spec.rb │ │ ├── collection_radio_buttons_spec.rb │ │ ├── collection_select_spec.rb │ │ ├── color_field_spec.rb │ │ ├── date_field_spec.rb │ │ ├── date_picker_examples.rb │ │ ├── date_select_spec.rb │ │ ├── datetime_field_spec.rb │ │ ├── datetime_select_spec.rb │ │ ├── email_field_spec.rb │ │ ├── file_field_spec.rb │ │ ├── form_control_examples.rb │ │ ├── form_field_examples.rb │ │ ├── form_group_spec.rb │ │ ├── grouped_collection_select_spec.rb │ │ ├── month_field_spec.rb │ │ ├── number_field_spec.rb │ │ ├── password_field_spec.rb │ │ ├── radio_button_spec.rb │ │ ├── range_field_spec.rb │ │ ├── search_field_spec.rb │ │ ├── select_spec.rb │ │ ├── static_field_spec.rb │ │ ├── tag_select_spec.rb │ │ ├── telephone_field.rb │ │ ├── text_area_spec.rb │ │ ├── text_field_spec.rb │ │ ├── time_field_spec.rb │ │ ├── time_select_spec.rb │ │ ├── time_zone_select_spec.rb │ │ ├── url_field_spec.rb │ │ └── week_field_spec.rb │ └── renderer_spec.rb │ ├── form_spec.rb │ ├── helpers │ ├── avatar_helper_spec.rb │ ├── card_helper_spec.rb │ ├── container_helper_spec.rb │ ├── form_helper_spec.rb │ ├── format_helper_spec.rb │ ├── gravatar_helper_spec.rb │ ├── grid_helper_spec.rb │ ├── hook_helper_spec.rb │ ├── i18n_helper_spec.rb │ ├── icon_helper_spec.rb │ ├── modal_helper_spec.rb │ ├── navigation_helper_spec.rb │ ├── params_helper_spec.rb │ ├── sort_helper_spec.rb │ ├── status_helper_spec.rb │ ├── table_helper_spec.rb │ ├── timestamp_helper_spec.rb │ ├── title_helper_spec.rb │ └── url_helper_spec.rb │ ├── hook_spec.rb │ ├── model_name_spec.rb │ ├── navigation │ ├── block_spec.rb │ ├── group_spec.rb │ └── item_spec.rb │ ├── navigation_spec.rb │ ├── options_spec.rb │ ├── registry_spec.rb │ ├── resource │ ├── builder_spec.rb │ └── toolbar_spec.rb │ ├── resource_spec.rb │ ├── scopes │ ├── block_spec.rb │ └── scope_spec.rb │ ├── scopes_spec.rb │ ├── tab_spec.rb │ ├── table │ ├── actions_column_spec.rb │ ├── builder_spec.rb │ ├── column_spec.rb │ └── select_column_spec.rb │ ├── table_spec.rb │ ├── toolbar │ └── item_spec.rb │ ├── toolbar_spec.rb │ └── trestle_spec.rb ├── trestle.gemspec ├── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env"] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 0.25% 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | app/assets/bundle/**/* -diff 2 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish gem 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | push: 7 | runs-on: ubuntu-latest 8 | 9 | permissions: 10 | contents: write 11 | id-token: write 12 | 13 | # If you configured a GitHub environment on RubyGems, you must use it here. 14 | environment: release 15 | 16 | steps: 17 | # Set up 18 | - uses: actions/checkout@v4 19 | - name: Set up Ruby 20 | uses: ruby/setup-ruby@v1 21 | with: 22 | bundler-cache: true 23 | ruby-version: ruby 24 | 25 | # Release 26 | - uses: rubygems/release-gem@v1 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /gemfiles/*.lock 5 | /_yardoc/ 6 | /coverage/ 7 | /doc/ 8 | /pkg/ 9 | /spec/reports/ 10 | /tmp/ 11 | /sandbox/db/*.sqlite3 12 | /sandbox/db/*.sqlite3-shm 13 | /sandbox/db/*.sqlite3-wal 14 | /sandbox/*.sqlite3-journal 15 | /sandbox/log/*.log 16 | /sandbox/tmp/ 17 | /spec/dummy/db/*.sqlite3 18 | /spec/dummy/db/*.sqlite3-shm 19 | /spec/dummy/db/*.sqlite3-wal 20 | /spec/dummy/*.sqlite3-journal 21 | /spec/dummy/log/*.log 22 | /spec/dummy/tmp/* 23 | /spec/dummy/config/credentials.yml.enc 24 | /spec/dummy/config/master.key 25 | /spec/dummy/public/assets/ 26 | /node_modules 27 | /i18n/locale/*.json 28 | *.sqlite3-shm 29 | *.sqlite3-wal -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :development do 4 | gem 'bcrypt' 5 | gem 'faker' 6 | end 7 | 8 | group :test do 9 | gem 'coveralls_reborn', require: false 10 | gem 'guard-rspec', require: false 11 | 12 | gem 'capybara' 13 | gem 'selenium-webdriver' 14 | gem 'puma' 15 | gem 'launchy' 16 | gem 'capybara-screenshot' 17 | end 18 | 19 | # gem 'rails', '~> 6.0.0' 20 | # gem 'rails', '~> 6.1.0' 21 | # gem 'rails', '~> 7.0.0' 22 | # gem 'rails', '~> 7.1.0' 23 | gem 'rails', '~> 7.2.0' 24 | # gem 'rails', '~> 8.0.0' 25 | 26 | gem 'haml' 27 | gem 'sqlite3' 28 | 29 | gem 'sprockets-rails' 30 | # gem 'sassc-rails' 31 | # gem 'propshaft' 32 | 33 | gem 'i18n-js' 34 | 35 | # Specify your gem's dependencies in trestle.gemspec 36 | gemspec 37 | 38 | gem "rake", "~> 13.0" 39 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard :rspec, cmd: "bundle exec rspec", all_on_start: true, all_after_pass: true do 2 | require "guard/rspec/dsl" 3 | dsl = Guard::RSpec::Dsl.new(self) 4 | 5 | # RSpec files 6 | rspec = dsl.rspec 7 | watch(rspec.spec_helper) { rspec.spec_dir } 8 | watch(rspec.spec_support) { rspec.spec_dir } 9 | watch(rspec.spec_files) 10 | 11 | # Ruby files 12 | ruby = dsl.ruby 13 | dsl.watch_spec_files_for(ruby.lib_files) 14 | 15 | # Rails files 16 | rails = dsl.rails(view_extensions: %w(erb)) 17 | dsl.watch_spec_files_for(rails.app_files) 18 | dsl.watch_spec_files_for(rails.views) 19 | end 20 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /app/assets/bundle/trestle/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/app/assets/bundle/trestle/fa-brands-400.ttf -------------------------------------------------------------------------------- /app/assets/bundle/trestle/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/app/assets/bundle/trestle/fa-brands-400.woff2 -------------------------------------------------------------------------------- /app/assets/bundle/trestle/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/app/assets/bundle/trestle/fa-regular-400.ttf -------------------------------------------------------------------------------- /app/assets/bundle/trestle/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/app/assets/bundle/trestle/fa-regular-400.woff2 -------------------------------------------------------------------------------- /app/assets/bundle/trestle/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/app/assets/bundle/trestle/fa-solid-900.ttf -------------------------------------------------------------------------------- /app/assets/bundle/trestle/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/app/assets/bundle/trestle/fa-solid-900.woff2 -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/bg.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).bg={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["Нд","Пн","Вт","Ср","Чт","Пт","Сб"],longhand:["Неделя","Понеделник","Вторник","Сряда","Четвъртък","Петък","Събота"]},months:{shorthand:["Яну","Фев","Март","Апр","Май","Юни","Юли","Авг","Сеп","Окт","Ное","Дек"],longhand:["Януари","Февруари","Март","Април","Май","Юни","Юли","Август","Септември","Октомври","Ноември","Декември"]},time_24hr:!0,firstDayOfWeek:1};n.l10ns.bg=o;var t=n.l10ns;e.Bulgarian=o,e.default=t,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/bn.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).bn={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["রবি","সোম","মঙ্গল","বুধ","বৃহস্পতি","শুক্র","শনি"],longhand:["রবিবার","সোমবার","মঙ্গলবার","বুধবার","বৃহস্পতিবার","শুক্রবার","শনিবার"]},months:{shorthand:["জানু","ফেব্রু","মার্চ","এপ্রিল","মে","জুন","জুলাই","আগ","সেপ্টে","অক্টো","নভে","ডিসে"],longhand:["জানুয়ারী","ফেব্রুয়ারী","মার্চ","এপ্রিল","মে","জুন","জুলাই","আগস্ট","সেপ্টেম্বর","অক্টোবর","নভেম্বর","ডিসেম্বর"]}};n.l10ns.bn=o;var d=n.l10ns;e.Bangla=o,e.default=d,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/bs.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).bs={})}(this,(function(e){"use strict";var t="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},n={firstDayOfWeek:1,weekdays:{shorthand:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"],longhand:["Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota"]},months:{shorthand:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],longhand:["Januar","Februar","Mart","April","Maj","Juni","Juli","Avgust","Septembar","Oktobar","Novembar","Decembar"]},time_24hr:!0};t.l10ns.bs=n;var a=t.l10ns;e.Bosnian=n,e.default=a,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/da.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).da={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},a={weekdays:{shorthand:["søn","man","tir","ons","tors","fre","lør"],longhand:["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"]},months:{shorthand:["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"],longhand:["januar","februar","marts","april","maj","juni","juli","august","september","oktober","november","december"]},ordinal:function(){return"."},firstDayOfWeek:1,rangeSeparator:" til ",weekAbbreviation:"uge",time_24hr:!0};n.l10ns.da=a;var r=n.l10ns;e.Danish=a,e.default=r,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/es.js: -------------------------------------------------------------------------------- 1 | !function(e,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports):"function"==typeof define&&define.amd?define(["exports"],o):o((e="undefined"!=typeof globalThis?globalThis:e||self).es={})}(this,(function(e){"use strict";var o="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},n={weekdays:{shorthand:["Dom","Lun","Mar","Mié","Jue","Vie","Sáb"],longhand:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"]},months:{shorthand:["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"],longhand:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"]},ordinal:function(){return"º"},firstDayOfWeek:1,rangeSeparator:" a ",time_24hr:!0};o.l10ns.es=n;var i=o.l10ns;e.Spanish=n,e.default=i,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/fa.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).fa={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["یک","دو","سه","چهار","پنج","جمعه","شنبه"],longhand:["یک‌شنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنچ‌شنبه","جمعه","شنبه"]},months:{shorthand:["ژانویه","فوریه","مارس","آوریل","مه","ژوئن","ژوئیه","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"],longhand:["ژانویه","فوریه","مارس","آوریل","مه","ژوئن","ژوئیه","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"]},firstDayOfWeek:6,ordinal:function(){return""}};n.l10ns.fa=o;var t=n.l10ns;e.Persian=o,e.default=t,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/fi.js: -------------------------------------------------------------------------------- 1 | !function(e,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i((e="undefined"!=typeof globalThis?globalThis:e||self).fi={})}(this,(function(e){"use strict";var i="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},u={firstDayOfWeek:1,weekdays:{shorthand:["su","ma","ti","ke","to","pe","la"],longhand:["sunnuntai","maanantai","tiistai","keskiviikko","torstai","perjantai","lauantai"]},months:{shorthand:["tammi","helmi","maalis","huhti","touko","kesä","heinä","elo","syys","loka","marras","joulu"],longhand:["tammikuu","helmikuu","maaliskuu","huhtikuu","toukokuu","kesäkuu","heinäkuu","elokuu","syyskuu","lokakuu","marraskuu","joulukuu"]},ordinal:function(){return"."},time_24hr:!0};i.l10ns.fi=u;var n=i.l10ns;e.Finnish=u,e.default=n,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/ga.js: -------------------------------------------------------------------------------- 1 | !function(e,a){"object"==typeof exports&&"undefined"!=typeof module?a(exports):"function"==typeof define&&define.amd?define(["exports"],a):a((e="undefined"!=typeof globalThis?globalThis:e||self).ga={})}(this,(function(e){"use strict";var a="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},i={firstDayOfWeek:1,weekdays:{shorthand:["Dom","Lua","Mái","Céa","Déa","Aoi","Sat"],longhand:["Dé Domhnaigh","Dé Luain","Dé Máirt","Dé Céadaoin","Déardaoin","Dé hAoine","Dé Sathairn"]},months:{shorthand:["Ean","Fea","Már","Aib","Bea","Mei","Iúi","Lún","MFo","DFo","Sam","Nol"],longhand:["Eanáir","Feabhra","Márta","Aibreán","Bealtaine","Meitheamh","Iúil","Lúnasa","Meán Fómhair","Deireadh Fómhair","Samhain","Nollaig"]},time_24hr:!0};a.l10ns.hr=i;var n=a.l10ns;e.Irish=i,e.default=n,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/he.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).he={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["א","ב","ג","ד","ה","ו","ש"],longhand:["ראשון","שני","שלישי","רביעי","חמישי","שישי","שבת"]},months:{shorthand:["ינו׳","פבר׳","מרץ","אפר׳","מאי","יוני","יולי","אוג׳","ספט׳","אוק׳","נוב׳","דצמ׳"],longhand:["ינואר","פברואר","מרץ","אפריל","מאי","יוני","יולי","אוגוסט","ספטמבר","אוקטובר","נובמבר","דצמבר"]},rangeSeparator:" אל ",time_24hr:!0};n.l10ns.he=o;var t=n.l10ns;e.Hebrew=o,e.default=t,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/hi.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).hi={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["रवि","सोम","मंगल","बुध","गुरु","शुक्र","शनि"],longhand:["रविवार","सोमवार","मंगलवार","बुधवार","गुरुवार","शुक्रवार","शनिवार"]},months:{shorthand:["जन","फर","मार्च","अप्रेल","मई","जून","जूलाई","अग","सित","अक्ट","नव","दि"],longhand:["जनवरी ","फरवरी","मार्च","अप्रेल","मई","जून","जूलाई","अगस्त ","सितम्बर","अक्टूबर","नवम्बर","दिसम्बर"]}};n.l10ns.hi=o;var d=n.l10ns;e.Hindi=o,e.default=d,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/hr.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).hr={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={firstDayOfWeek:1,weekdays:{shorthand:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"],longhand:["Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota"]},months:{shorthand:["Sij","Velj","Ožu","Tra","Svi","Lip","Srp","Kol","Ruj","Lis","Stu","Pro"],longhand:["Siječanj","Veljača","Ožujak","Travanj","Svibanj","Lipanj","Srpanj","Kolovoz","Rujan","Listopad","Studeni","Prosinac"]},time_24hr:!0};n.l10ns.hr=o;var a=n.l10ns;e.Croatian=o,e.default=a,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/id.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).id={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},i={weekdays:{shorthand:["Min","Sen","Sel","Rab","Kam","Jum","Sab"],longhand:["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"]},months:{shorthand:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agu","Sep","Okt","Nov","Des"],longhand:["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember"]},firstDayOfWeek:1,ordinal:function(){return""},time_24hr:!0,rangeSeparator:" - "};n.l10ns.id=i;var a=n.l10ns;e.Indonesian=i,e.default=a,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/ja.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).ja={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},a={weekdays:{shorthand:["日","月","火","水","木","金","土"],longhand:["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"]},months:{shorthand:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],longhand:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"]},time_24hr:!0,rangeSeparator:" から ",monthAriaLabel:"月",amPM:["午前","午後"],yearAriaLabel:"年",hourAriaLabel:"時間",minuteAriaLabel:"分"};n.l10ns.ja=a;var o=n.l10ns;e.Japanese=a,e.default=o,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/ko.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).ko={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["일","월","화","수","목","금","토"],longhand:["일요일","월요일","화요일","수요일","목요일","금요일","토요일"]},months:{shorthand:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],longhand:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"]},ordinal:function(){return"일"},rangeSeparator:" ~ ",amPM:["오전","오후"]};n.l10ns.ko=o;var t=n.l10ns;e.Korean=o,e.default=t,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/lv.js: -------------------------------------------------------------------------------- 1 | !function(e,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i((e="undefined"!=typeof globalThis?globalThis:e||self).lv={})}(this,(function(e){"use strict";var i="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},n={firstDayOfWeek:1,weekdays:{shorthand:["Sv","Pr","Ot","Tr","Ce","Pk","Se"],longhand:["Svētdiena","Pirmdiena","Otrdiena","Trešdiena","Ceturtdiena","Piektdiena","Sestdiena"]},months:{shorthand:["Jan","Feb","Mar","Apr","Mai","Jūn","Jūl","Aug","Sep","Okt","Nov","Dec"],longhand:["Janvāris","Februāris","Marts","Aprīlis","Maijs","Jūnijs","Jūlijs","Augusts","Septembris","Oktobris","Novembris","Decembris"]},rangeSeparator:" līdz ",time_24hr:!0};i.l10ns.lv=n;var t=i.l10ns;e.Latvian=n,e.default=t,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/mk.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).mk={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["Не","По","Вт","Ср","Че","Пе","Са"],longhand:["Недела","Понеделник","Вторник","Среда","Четврток","Петок","Сабота"]},months:{shorthand:["Јан","Фев","Мар","Апр","Мај","Јун","Јул","Авг","Сеп","Окт","Ное","Дек"],longhand:["Јануари","Февруари","Март","Април","Мај","Јуни","Јули","Август","Септември","Октомври","Ноември","Декември"]},firstDayOfWeek:1,weekAbbreviation:"Нед.",rangeSeparator:" до ",time_24hr:!0};n.l10ns.mk=o;var t=n.l10ns;e.Macedonian=o,e.default=t,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/ms.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).ms={})}(this,(function(e){"use strict";var n=("undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}}).l10ns;e.Malaysian={weekdays:{shorthand:["Aha","Isn","Sel","Rab","Kha","Jum","Sab"],longhand:["Ahad","Isnin","Selasa","Rabu","Khamis","Jumaat","Sabtu"]},months:{shorthand:["Jan","Feb","Mac","Apr","Mei","Jun","Jul","Ogo","Sep","Okt","Nov","Dis"],longhand:["Januari","Februari","Mac","April","Mei","Jun","Julai","Ogos","September","Oktober","November","Disember"]},firstDayOfWeek:1,ordinal:function(){return""}},e.default=n,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/my.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).my={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["နွေ","လာ","ဂါ","ဟူး","ကြာ","သော","နေ"],longhand:["တနင်္ဂနွေ","တနင်္လာ","အင်္ဂါ","ဗုဒ္ဓဟူး","ကြာသပတေး","သောကြာ","စနေ"]},months:{shorthand:["ဇန်","ဖေ","မတ်","ပြီ","မေ","ဇွန်","လိုင်","သြ","စက်","အောက်","နို","ဒီ"],longhand:["ဇန်နဝါရီ","ဖေဖော်ဝါရီ","မတ်","ဧပြီ","မေ","ဇွန်","ဇူလိုင်","သြဂုတ်","စက်တင်ဘာ","အောက်တိုဘာ","နိုဝင်ဘာ","ဒီဇင်ဘာ"]},firstDayOfWeek:1,ordinal:function(){return""},time_24hr:!0};n.l10ns.my=o;var t=n.l10ns;e.Burmese=o,e.default=t,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/pa.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).pa={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["ਐਤ","ਸੋਮ","ਮੰਗਲ","ਬੁੱਧ","ਵੀਰ","ਸ਼ੁੱਕਰ","ਸ਼ਨਿੱਚਰ"],longhand:["ਐਤਵਾਰ","ਸੋਮਵਾਰ","ਮੰਗਲਵਾਰ","ਬੁੱਧਵਾਰ","ਵੀਰਵਾਰ","ਸ਼ੁੱਕਰਵਾਰ","ਸ਼ਨਿੱਚਰਵਾਰ"]},months:{shorthand:["ਜਨ","ਫ਼ਰ","ਮਾਰ","ਅਪ੍ਰੈ","ਮਈ","ਜੂਨ","ਜੁਲਾ","ਅਗ","ਸਤੰ","ਅਕ","ਨਵੰ","ਦਸੰ"],longhand:["ਜਨਵਰੀ","ਫ਼ਰਵਰੀ","ਮਾਰਚ","ਅਪ੍ਰੈਲ","ਮਈ","ਜੂਨ","ਜੁਲਾਈ","ਅਗਸਤ","ਸਤੰਬਰ","ਅਕਤੂਬਰ","ਨਵੰਬਰ","ਦਸੰਬਰ"]},time_24hr:!0};n.l10ns.pa=o;var t=n.l10ns;e.Punjabi=o,e.default=t,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/pt.js: -------------------------------------------------------------------------------- 1 | !function(e,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports):"function"==typeof define&&define.amd?define(["exports"],o):o((e="undefined"!=typeof globalThis?globalThis:e||self).pt={})}(this,(function(e){"use strict";var o="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},a={weekdays:{shorthand:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],longhand:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"]},months:{shorthand:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],longhand:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"]},rangeSeparator:" até ",time_24hr:!0};o.l10ns.pt=a;var n=o.l10ns;e.Portuguese=a,e.default=n,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/ro.js: -------------------------------------------------------------------------------- 1 | !function(e,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i((e="undefined"!=typeof globalThis?globalThis:e||self).ro={})}(this,(function(e){"use strict";var i="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},n={weekdays:{shorthand:["Dum","Lun","Mar","Mie","Joi","Vin","Sâm"],longhand:["Duminică","Luni","Marți","Miercuri","Joi","Vineri","Sâmbătă"]},months:{shorthand:["Ian","Feb","Mar","Apr","Mai","Iun","Iul","Aug","Sep","Oct","Noi","Dec"],longhand:["Ianuarie","Februarie","Martie","Aprilie","Mai","Iunie","Iulie","August","Septembrie","Octombrie","Noiembrie","Decembrie"]},firstDayOfWeek:1,time_24hr:!0,ordinal:function(){return""}};i.l10ns.ro=n;var o=i.l10ns;e.Romanian=n,e.default=o,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/si.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).si={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["ඉ","ස","අ","බ","බ්‍ර","සි","සෙ"],longhand:["ඉරිදා","සඳුදා","අඟහරුවාදා","බදාදා","බ්‍රහස්පතින්දා","සිකුරාදා","සෙනසුරාදා"]},months:{shorthand:["ජන","පෙබ","මාර්","අප්‍රේ","මැයි","ජුනි","ජූලි","අගෝ","සැප්","ඔක්","නොවැ","දෙසැ"],longhand:["ජනවාරි","පෙබරවාරි","මාර්තු","අප්‍රේල්","මැයි","ජුනි","ජූලි","අගෝස්තු","සැප්තැම්බර්","ඔක්තෝබර්","නොවැම්බර්","දෙසැම්බර්"]},time_24hr:!0};n.l10ns.si=o;var i=n.l10ns;e.Sinhala=o,e.default=i,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/sk.js: -------------------------------------------------------------------------------- 1 | !function(e,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports):"function"==typeof define&&define.amd?define(["exports"],o):o((e="undefined"!=typeof globalThis?globalThis:e||self).sk={})}(this,(function(e){"use strict";var o="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},t={weekdays:{shorthand:["Ned","Pon","Ut","Str","Štv","Pia","Sob"],longhand:["Nedeľa","Pondelok","Utorok","Streda","Štvrtok","Piatok","Sobota"]},months:{shorthand:["Jan","Feb","Mar","Apr","Máj","Jún","Júl","Aug","Sep","Okt","Nov","Dec"],longhand:["Január","Február","Marec","Apríl","Máj","Jún","Júl","August","September","Október","November","December"]},firstDayOfWeek:1,rangeSeparator:" do ",time_24hr:!0,ordinal:function(){return"."}};o.l10ns.sk=t;var n=o.l10ns;e.Slovak=t,e.default=n,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/sl.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).sl={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["Ned","Pon","Tor","Sre","Čet","Pet","Sob"],longhand:["Nedelja","Ponedeljek","Torek","Sreda","Četrtek","Petek","Sobota"]},months:{shorthand:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],longhand:["Januar","Februar","Marec","April","Maj","Junij","Julij","Avgust","September","Oktober","November","December"]},firstDayOfWeek:1,rangeSeparator:" do ",time_24hr:!0,ordinal:function(){return"."}};n.l10ns.sl=o;var t=n.l10ns;e.Slovenian=o,e.default=t,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/sr-cyr.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self)["sr-cyr"]={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["Нед","Пон","Уто","Сре","Чет","Пет","Суб"],longhand:["Недеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"]},months:{shorthand:["Јан","Феб","Мар","Апр","Мај","Јун","Јул","Авг","Сеп","Окт","Нов","Дец"],longhand:["Јануар","Фебруар","Март","Април","Мај","Јун","Јул","Август","Септембар","Октобар","Новембар","Децембар"]},firstDayOfWeek:1,weekAbbreviation:"Нед.",rangeSeparator:" до "};n.l10ns.sr=o;var i=n.l10ns;e.SerbianCyrillic=o,e.default=i,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/sr.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).sr={})}(this,(function(e){"use strict";var t="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},a={weekdays:{shorthand:["Ned","Pon","Uto","Sre","Čet","Pet","Sub"],longhand:["Nedelja","Ponedeljak","Utorak","Sreda","Četvrtak","Petak","Subota"]},months:{shorthand:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],longhand:["Januar","Februar","Mart","April","Maj","Jun","Jul","Avgust","Septembar","Oktobar","Novembar","Decembar"]},firstDayOfWeek:1,weekAbbreviation:"Ned.",rangeSeparator:" do ",time_24hr:!0};t.l10ns.sr=a;var n=t.l10ns;e.Serbian=a,e.default=n,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/sv.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).sv={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},a={firstDayOfWeek:1,weekAbbreviation:"v",weekdays:{shorthand:["sön","mån","tis","ons","tor","fre","lör"],longhand:["söndag","måndag","tisdag","onsdag","torsdag","fredag","lördag"]},months:{shorthand:["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"],longhand:["januari","februari","mars","april","maj","juni","juli","augusti","september","oktober","november","december"]},rangeSeparator:" till ",time_24hr:!0,ordinal:function(){return"."}};n.l10ns.sv=a;var o=n.l10ns;e.Swedish=a,e.default=o,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/uk.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).uk={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={firstDayOfWeek:1,weekdays:{shorthand:["Нд","Пн","Вт","Ср","Чт","Пт","Сб"],longhand:["Неділя","Понеділок","Вівторок","Середа","Четвер","П'ятниця","Субота"]},months:{shorthand:["Січ","Лют","Бер","Кві","Тра","Чер","Лип","Сер","Вер","Жов","Лис","Гру"],longhand:["Січень","Лютий","Березень","Квітень","Травень","Червень","Липень","Серпень","Вересень","Жовтень","Листопад","Грудень"]},time_24hr:!0};n.l10ns.uk=o;var t=n.l10ns;e.Ukrainian=o,e.default=t,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/vn.js: -------------------------------------------------------------------------------- 1 | !function(n,h){"object"==typeof exports&&"undefined"!=typeof module?h(exports):"function"==typeof define&&define.amd?define(["exports"],h):h((n="undefined"!=typeof globalThis?globalThis:n||self).vn={})}(this,(function(n){"use strict";var h="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},e={weekdays:{shorthand:["CN","T2","T3","T4","T5","T6","T7"],longhand:["Chủ nhật","Thứ hai","Thứ ba","Thứ tư","Thứ năm","Thứ sáu","Thứ bảy"]},months:{shorthand:["Th1","Th2","Th3","Th4","Th5","Th6","Th7","Th8","Th9","Th10","Th11","Th12"],longhand:["Tháng một","Tháng hai","Tháng ba","Tháng tư","Tháng năm","Tháng sáu","Tháng bảy","Tháng tám","Tháng chín","Tháng mười","Tháng mười một","Tháng mười hai"]},firstDayOfWeek:1,rangeSeparator:" đến "};h.l10ns.vn=e;var T=h.l10ns;n.Vietnamese=e,n.default=T,Object.defineProperty(n,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/zh-tw.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self)["zh-tw"]={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["週日","週一","週二","週三","週四","週五","週六"],longhand:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]},months:{shorthand:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],longhand:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"]},rangeSeparator:" 至 ",weekAbbreviation:"週",scrollTitle:"滾動切換",toggleTitle:"點擊切換 12/24 小時時制"};n.l10ns.zh_tw=o;var t=n.l10ns;e.MandarinTraditional=o,e.default=t,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/bundle/trestle/locale/flatpickr/zh.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).zh={})}(this,(function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},o={weekdays:{shorthand:["周日","周一","周二","周三","周四","周五","周六"],longhand:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]},months:{shorthand:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],longhand:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"]},rangeSeparator:" 至 ",weekAbbreviation:"周",scrollTitle:"滚动切换",toggleTitle:"点击切换 12/24 小时时制"};n.l10ns.zh=o;var t=n.l10ns;e.Mandarin=o,e.default=t,Object.defineProperty(e,"__esModule",{value:!0})})); -------------------------------------------------------------------------------- /app/assets/javascripts/trestle/custom.js: -------------------------------------------------------------------------------- 1 | // This file is left as an extension point for user customization. 2 | // 3 | // It will be overridden by the similarly named file within 4 | // the app/assets folder of the Rails application. 5 | -------------------------------------------------------------------------------- /app/assets/sprockets/trestle/_custom.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is left as an extension point for user customization. 3 | * 4 | * It will be overridden by the similarly named file within 5 | * the app/assets folder of the Rails application. 6 | */ 7 | -------------------------------------------------------------------------------- /app/assets/sprockets/trestle/custom.css: -------------------------------------------------------------------------------- 1 | //= require trestle/_custom -------------------------------------------------------------------------------- /app/assets/sprockets/trestle/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../../bundle 2 | //= link trestle/custom.js 3 | //= link trestle/custom.css 4 | //= link trestle/icons/font-awesome.css -------------------------------------------------------------------------------- /app/assets/stylesheets/trestle/custom.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is left as an extension point for user customization. 3 | * 4 | * It will be overridden by the similarly named file within 5 | * the app/assets folder of the Rails application. 6 | */ 7 | -------------------------------------------------------------------------------- /app/controllers/concerns/trestle/controller/breadcrumbs.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Controller 3 | module Breadcrumbs 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | helper_method :breadcrumbs 8 | helper_method :breadcrumb 9 | end 10 | 11 | protected 12 | def breadcrumbs 13 | @_breadcrumbs ||= Breadcrumb::Trail.new(Trestle.config.root_breadcrumbs) 14 | end 15 | 16 | def breadcrumb(label, path=nil) 17 | breadcrumbs.append(label, path) 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/concerns/trestle/controller/callbacks.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Controller 3 | module Callbacks 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | Trestle.config.before_actions.each do |action| 8 | before_action(action.options, &action.block) 9 | end 10 | 11 | Trestle.config.after_actions.each do |action| 12 | after_action(action.options, &action.block) 13 | end 14 | 15 | Trestle.config.around_actions.each do |action| 16 | around_action(action.options, &action.block) 17 | end 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/concerns/trestle/controller/helpers.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Controller 3 | module Helpers 4 | extend ActiveSupport::Concern 5 | 6 | include Trestle::FlashHelper 7 | 8 | included do 9 | # Allow inclusion of helpers from Rails application 10 | self.helpers_path += Rails.application.helpers_paths 11 | 12 | # Add helpers declared from configuration as blocks 13 | helper Trestle.config.helper_module 14 | 15 | # Add helpers declared from configuration as module references 16 | helper *Trestle.config.helpers 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/controllers/concerns/trestle/controller/layout.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Controller 3 | module Layout 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | layout :choose_layout 8 | end 9 | 10 | protected 11 | def choose_layout 12 | request.xhr? ? false : "trestle/admin" 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/controllers/concerns/trestle/controller/location.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Controller 3 | module Location 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | after_action :set_trestle_location_header 8 | end 9 | 10 | # The X-Trestle-Location header is set to indicate that the remote form has triggered 11 | # a new page URL (e.g. new -> show) without demanding a full page refresh. 12 | def set_trestle_location_header 13 | unless modal_request? || response.location 14 | headers["X-Trestle-Location"] = request.path 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/controllers/concerns/trestle/controller/modal.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Controller 3 | module Modal 4 | extend ActiveSupport::Concern 5 | 6 | include Trestle::ModalHelper 7 | 8 | included do 9 | helper_method :modal_request?, :dialog_request? 10 | end 11 | 12 | protected 13 | def modal_request? 14 | turbo_frame_request_id == "modal" || 15 | request.headers["X-Trestle-Modal"] || 16 | request.headers["X-Trestle-Dialog"] 17 | end 18 | 19 | def dialog_request? 20 | Trestle.deprecator.warn("The #dialog_request? helper has been renamed to #modal_request?") 21 | modal_request? 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/controllers/concerns/trestle/controller/title.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Controller 3 | module Title 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | helper_method :default_title 8 | end 9 | 10 | protected 11 | def title(title=nil) 12 | @_title = title if title 13 | end 14 | 15 | def default_title 16 | @_title || action_name.titleize 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/controllers/concerns/trestle/controller/toolbars.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Controller 3 | module Toolbars 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | helper_method :toolbars 8 | helper_method :toolbar 9 | end 10 | 11 | protected 12 | def toolbars 13 | @_toolbars ||= {} 14 | end 15 | 16 | def toolbar(name, options={}, &block) 17 | builder = options[:builder] || default_toolbar_builder 18 | 19 | toolbar = (toolbars[name.to_s] ||= Toolbar.new(builder)) 20 | toolbar.clear! if options[:clear] 21 | toolbar.append(&block) if block_given? 22 | toolbar 23 | end 24 | 25 | def default_toolbar_builder 26 | Toolbar::Builder 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/controllers/concerns/trestle/controller/turbo_stream.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Controller 3 | module TurboStream 4 | extend ActiveSupport::Concern 5 | 6 | private 7 | def turbo_stream 8 | Trestle::Turbo::TagBuilder.new(view_context) 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/concerns/trestle/resource/controller/redirection.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Resource 3 | module Controller 4 | module Redirection 5 | protected 6 | def redirect_to_return_location(action, instance, status: :found, default: nil, &block) 7 | fallback_location = block_given? ? block : default 8 | 9 | if admin.return_locations[action] && !modal_request? 10 | location = instance_exec(instance, &admin.return_locations[action]) 11 | 12 | case location 13 | when :back 14 | redirect_back fallback_location: fallback_location, status: status 15 | else 16 | redirect_to location, status: status 17 | end 18 | else 19 | redirect_to fallback_location, status: status 20 | end 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/controllers/concerns/trestle/resource/controller/toolbar.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Resource 3 | module Controller 4 | module Toolbar 5 | def default_toolbar_builder 6 | Resource::Toolbar::Builder 7 | end 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/trestle/admin_controller.rb: -------------------------------------------------------------------------------- 1 | class Trestle::AdminController < Trestle::ApplicationController 2 | if respond_to?(:raise_on_missing_callback_actions=) 3 | self.raise_on_missing_callback_actions = false 4 | end 5 | 6 | def index 7 | end 8 | 9 | class << self 10 | attr_reader :admin 11 | 12 | private 13 | def local_prefixes 14 | return admin.view_path_prefixes if admin 15 | [controller_path.sub(/\/$/, "")] 16 | end 17 | end 18 | 19 | def admin 20 | @_admin ||= self.class.admin.new(self) 21 | end 22 | helper_method :admin 23 | 24 | protected 25 | def breadcrumbs 26 | @_breadcrumbs ||= admin.breadcrumbs.dup 27 | end 28 | 29 | def flash_message(type, title:, message:) 30 | { 31 | title: admin.t("flash.#{type}.title", default: title), 32 | message: admin.t("flash.#{type}.message", default: message) 33 | } 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /app/controllers/trestle/application_controller.rb: -------------------------------------------------------------------------------- 1 | class Trestle::ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | 4 | include Trestle::Controller::Breadcrumbs 5 | include Trestle::Controller::Callbacks 6 | include Trestle::Controller::Helpers 7 | include Trestle::Controller::Layout 8 | include Trestle::Controller::Location 9 | include Trestle::Controller::Modal 10 | include Trestle::Controller::Title 11 | include Trestle::Controller::Toolbars 12 | include Trestle::Controller::TurboStream 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/trestle/dashboard_controller.rb: -------------------------------------------------------------------------------- 1 | class Trestle::DashboardController < Trestle::ApplicationController 2 | def index 3 | admin = primary_admin 4 | redirect_to admin.path if admin 5 | end 6 | 7 | private 8 | def primary_admin 9 | if navigation = Trestle.navigation(self).first 10 | navigation.admin 11 | elsif Trestle.registry.any? 12 | Trestle.registry.first 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/controllers/trestle/resource_controller.rb: -------------------------------------------------------------------------------- 1 | class Trestle::ResourceController < Trestle::AdminController 2 | include Trestle::Resource::Controller::Actions 3 | include Trestle::Resource::Controller::DataMethods 4 | include Trestle::Resource::Controller::Redirection 5 | include Trestle::Resource::Controller::Toolbar 6 | end 7 | -------------------------------------------------------------------------------- /app/helpers/trestle/display_helper.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module DisplayHelper 3 | # Returns a plain-text representation of the given model instance, 4 | # typically used when rendering an associated object within a table. 5 | # 6 | # This helper delegates to Trestle::Display, which works by checking the 7 | # existence of each method from `Trestle.config.display_methods` in turn 8 | # and calling the first one it finds. 9 | # 10 | # By default this list is set to: 11 | # 12 | # [:display_name, :full_name, :name, :title, :username, :login, :email] 13 | # 14 | def display(instance) 15 | Trestle::Display.new(instance).to_s 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/helpers/trestle/flash_helper.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | # [Internal] 3 | module FlashHelper 4 | def normalize_flash_alert(flash) 5 | flash.is_a?(Hash) ? flash.with_indifferent_access : { message: flash } 6 | end 7 | 8 | def debug_form_errors? 9 | Trestle.config.debug_form_errors && instance_has_errors? 10 | end 11 | 12 | def instance_has_errors? 13 | instance.errors.any? rescue false 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/helpers/trestle/headings_helper.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module HeadingsHelper 3 | # These methods are delegated to the ActionView::Helpers::TagHelper proxy object for convenience. 4 | delegate :h1, :h2, :h3, :h4, :h5, :h6, to: :tag 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/helpers/trestle/hook_helper.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module HookHelper 3 | include Trestle::Hook::Helpers 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/trestle/icon_helper.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module IconHelper 3 | # Renders an icon (as an tag). 4 | # 5 | # Trestle includes the FontAwesome icon library but other font 6 | # libraries can be included via custom CSS. 7 | # 8 | # classes - List of font name classes to add to the tag 9 | # attributes - Additional HTML attributes to add to the tag 10 | # 11 | # Examples 12 | # 13 | # <%= icon("fas fa-star") %> 14 | # <%= icon("fas", "fa-star", class: "fa-fw text-muted") 15 | # 16 | # Return the HTML i tag for the icon. 17 | def icon(*classes, **attributes) 18 | tag.i("", **attributes.merge(class: [*classes, attributes[:class]])) 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/helpers/trestle/layout_helper.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | # [Internal] 3 | module LayoutHelper 4 | SIDEBAR_CLASSES = { 5 | "expanded" => "sidebar-expanded", 6 | "collapsed" => "sidebar-collapsed" 7 | } 8 | 9 | def body_attributes 10 | { 11 | class: body_classes 12 | }.reject { |k, v| v.blank? } 13 | end 14 | 15 | def body_classes 16 | [ 17 | SIDEBAR_CLASSES[cookies["trestle:sidebar"]] 18 | ].compact 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/helpers/trestle/title_helper.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module TitleHelper 3 | # Returns the page title (if set using content_for), falling back to 4 | # the titleized action name as a default if not set. 5 | def title 6 | content_for(:title) || default_title 7 | end 8 | 9 | def default_title 10 | action_name.titleize 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/helpers/trestle/toolbars_helper.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | # [Internal] 3 | module ToolbarsHelper 4 | def render_toolbar(toolbar, *args) 5 | result = toolbar.groups(self, *args).map do |items| 6 | if items.many? 7 | tag.div(class: "btn-group", role: "group") do 8 | safe_join(items, "\n") 9 | end 10 | else 11 | items.first 12 | end 13 | end 14 | 15 | safe_join(result, "\n") 16 | end 17 | 18 | def deprecated_toolbar(name) 19 | if content_for?(:"#{name}_toolbar") 20 | Trestle.deprecator.warn("Using content_for(:#{name}_toolbar) is deprecated. Please use toolbar(:#{name}) instead.") 21 | content_for(:"#{name}_toolbar") 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/helpers/trestle/turbo/stream_helper.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Turbo 3 | module StreamHelper 4 | def turbo_stream 5 | Trestle::Turbo::TagBuilder.new(self) 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/helpers/trestle/turbo/tag_builder.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Turbo 3 | class TagBuilder < ::Turbo::Streams::TagBuilder 4 | def modal(template=nil) 5 | turbo_stream_action_tag :modal, template: @view_context.render(template: template || @view_context.action_name, layout: "layouts/trestle/modal", prefixes: @view_context.controller._prefixes, formats: :html) 6 | end 7 | 8 | def close_modal(target) 9 | turbo_stream_action_tag :closeModal, targets: target 10 | end 11 | 12 | def flash 13 | turbo_stream_action_tag :flash, template: @view_context.render(partial: "trestle/flash/flash", formats: :html) 14 | end 15 | 16 | def reload 17 | turbo_stream_action_tag :reload 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/views/kaminari/trestle/_first_page.html.erb: -------------------------------------------------------------------------------- 1 | <%# Link to the "First" page 2 | - available local variables 3 | url: url to the first page 4 | current_page: a page object for the currently displayed page 5 | total_pages: total number of pages 6 | per_page: number of items to fetch per page 7 | -%> 8 |
  • 9 | <% if current_page.first? %> 10 | « 11 | <% else %> 12 | <%= link_to tag.span("«".html_safe), url, class: "page-link" %> 13 | <% end %> 14 |
  • 15 | -------------------------------------------------------------------------------- /app/views/kaminari/trestle/_gap.html.erb: -------------------------------------------------------------------------------- 1 | <%# Non-link tag that stands for skipped pages... 2 | - available local variables 3 | current_page: a page object for the currently displayed page 4 | total_pages: total number of pages 5 | per_page: number of items to fetch per page 6 | -%> 7 |
  • 8 | <%= t('views.pagination.truncate').html_safe %> 9 |
  • 10 | -------------------------------------------------------------------------------- /app/views/kaminari/trestle/_last_page.html.erb: -------------------------------------------------------------------------------- 1 | <%# Link to the "Last" page 2 | - available local variables 3 | url: url to the last page 4 | current_page: a page object for the currently displayed page 5 | total_pages: total number of pages 6 | per_page: number of items to fetch per page 7 | -%> 8 |
  • 9 | <% if current_page.last? %> 10 | » 11 | <% else %> 12 | <%= link_to tag.span("»".html_safe), url, class: "page-link" %> 13 | <% end %> 14 |
  • 15 | -------------------------------------------------------------------------------- /app/views/kaminari/trestle/_page.html.erb: -------------------------------------------------------------------------------- 1 | <%# Link showing page number 2 | - available local variables 3 | page: a page object for "this" page 4 | url: url to this page 5 | current_page: a page object for the currently displayed page 6 | total_pages: total number of pages 7 | per_page: number of items to fetch per page 8 | -%> 9 |
  • 10 | <%= link_to tag.span(page), url, class: "page-link", rel: page.next? ? 'next' : page.prev? ? 'prev' : nil %> 11 |
  • 12 | -------------------------------------------------------------------------------- /app/views/kaminari/trestle/_paginator.html.erb: -------------------------------------------------------------------------------- 1 | <%# The container tag 2 | - available local variables 3 | current_page: a page object for the currently displayed page 4 | total_pages: total number of pages 5 | per_page: number of items to fetch per page 6 | paginator: the paginator that renders the pagination tags inside 7 | -%> 8 | <%= paginator.render do -%> 9 |
      10 | <%= first_page_tag %> 11 | <% each_page do |page| -%> 12 | <% if page.left_outer? || page.right_outer? || page.inside_window? -%> 13 | <%= page_tag page %> 14 | <% elsif !page.was_truncated? -%> 15 | <%= gap_tag %> 16 | <% end -%> 17 | <% end -%> 18 | <%= last_page_tag %> 19 |
    20 | <% end -%> 21 | -------------------------------------------------------------------------------- /app/views/layouts/trestle/modal.html.erb: -------------------------------------------------------------------------------- 1 | <%= tag.div(**modal_wrapper_attributes) do %> 2 | <%= tag.div(**modal_dialog_attributes) do %> 3 | <%= yield %> 4 | <% end %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/views/trestle/_i18n.html.erb: -------------------------------------------------------------------------------- 1 | <% i18n_fallbacks.each do |locale| %> 2 | <%= javascript_include_tag "trestle/locale/#{locale}", 'data-turbo-track': 'reload' rescue nil %> 3 | <%= javascript_include_tag "trestle/locale/flatpickr/#{flatpickr_locale(locale)}", 'data-turbo-track': 'reload' rescue nil %> 4 | <% end %> 5 | 6 | <%= javascript_tag nonce: true do %> 7 | <% if Trestle.config.javascript_i18n_keys.any? %> 8 | Trestle.i18n.store(<%=raw JSON.pretty_generate(I18n.locale => i18n_javascript_translations) %>); 9 | <% end %> 10 | 11 | Trestle.localize(<%= i18n_fallbacks.map { |l| "'#{l}'" }.join(", ").html_safe %>); 12 | <% end %> 13 | -------------------------------------------------------------------------------- /app/views/trestle/_theme.html.erb: -------------------------------------------------------------------------------- 1 | <% if Trestle.config.theme.any? %> 2 | 15 | <% end %> 16 | -------------------------------------------------------------------------------- /app/views/trestle/admin/index.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for(:title, admin.admin_name.titleize) %> 2 | 3 | <%= render layout: "layout" do %> 4 |

    <%= t("trestle.onboarding.no_template", path: "app/views/#{admin.view_path}/index.html.erb", default: "To customize this template, please create %{path}.").html_safe %>

    5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/views/trestle/application/_tabs.html.erb: -------------------------------------------------------------------------------- 1 | <% if tabs.any? || render_sidebar_as_tab? -%> 2 | <%= tag.ul(class: "nav nav-tabs", data: local_assigns.fetch(:data, {})) do %> 3 | <% tabs.each do |name, tab| %> 4 | 7 | <% end %> 8 | 9 | <% if render_sidebar_as_tab? %> 10 | 13 | <% end %> 14 | <% end %> 15 | <% end %> 16 | -------------------------------------------------------------------------------- /app/views/trestle/application/_utilities.html.erb: -------------------------------------------------------------------------------- 1 | <% if content_for?(:utilities) -%> 2 |
    3 | <%= content_for(:utilities) %> 4 |
    5 | <% end -%> 6 | -------------------------------------------------------------------------------- /app/views/trestle/dashboard/index.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for(:title, t("trestle.onboarding.welcome", default: "Welcome to Trestle")) %> 2 | 3 | <%= render layout: "layout" do %> 4 |

    <%= t("trestle.onboarding.no_admins", default: "To begin, please create an admin within app/admin.").html_safe %>

    5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/views/trestle/flash/_alert.html.erb: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /app/views/trestle/flash/_debug.html.erb: -------------------------------------------------------------------------------- 1 | <% errors = local_assigns.fetch(:errors, {}) %> 2 | 3 | <% if errors.any? %> 4 | <%= link_to "Debug errors", "#debug-errors", class: "toggle-debug-errors small", data: { bs_toggle: "collapse" } %> 5 | 6 |
    7 |
      8 | <% errors.each do |key, message| %> 9 |
    • <%= key %>: <%= message %>
    • 10 | <% end %> 11 |
    12 |
    13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/trestle/flash/_flash.html.erb: -------------------------------------------------------------------------------- 1 | <% if flash[:message] -%> 2 | <%= render "trestle/flash/alert", html_class: "alert-success", 3 | icon: icon("alert-icon far fa-check-circle"), 4 | alert: normalize_flash_alert(flash[:message]) %> 5 | <% elsif flash[:error] -%> 6 | <%= render "trestle/flash/alert", html_class: "alert-danger", 7 | icon: icon("alert-icon far fa-times-circle"), 8 | alert: normalize_flash_alert(flash[:error]), 9 | errors: (Trestle::DebugErrors.new(instance.errors) if debug_form_errors?) %> 10 | <% end -%> 11 | -------------------------------------------------------------------------------- /app/views/trestle/resource/_form.html.erb: -------------------------------------------------------------------------------- 1 | <% if admin.form -%> 2 | <% if admin.form.modal? && modal_options = admin.form.options[:modal] %> 3 | <% modal_options!(class: modal_options[:class]) %> 4 | <% modal_options!(controller: modal_options[:controller]) %> 5 | <% end %> 6 | 7 | <%= admin.form.render(self, instance) %> 8 | <% else %> 9 |

    <%= t("trestle.onboarding.no_form", default: "Please define a form block or create a _form.html partial.").html_safe %>

    10 | <% end %> 11 | -------------------------------------------------------------------------------- /app/views/trestle/resource/_scopes.html.erb: -------------------------------------------------------------------------------- 1 | <%= tag.dl(class: admin.scopes.classes) do %> 2 | <% admin.scopes.grouped.each do |group, scopes| %> 3 | <%= tag.dt(group, class: ["scope-group", ("scope-group-empty" if group.blank?)]) if admin.scopes.grouped? %> 4 | 5 |
    6 |
      7 | <% scopes.each do |scope| %> 8 |
    • 9 | <%= link_to persistent_params.merge(scope: scope), class: ["scope", ("active" if scope.active?(params))] do %> 10 | <%= scope.label %> 11 | <% if scope.count? %>(<%= number_with_delimiter(scope.count(admin.collection(params))) %>)<% end %> 12 | <% end %> 13 |
    • 14 | <% end %> 15 |
    16 |
    17 | <% end %> 18 | <% end %> 19 | -------------------------------------------------------------------------------- /app/views/trestle/resource/_table.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "trestle/table/table", table: admin.table, collection: collection %> 2 | <%= render "trestle/table/pagination", collection: collection, entry_name: admin.model_name %> 3 | -------------------------------------------------------------------------------- /app/views/trestle/resource/create.turbo_stream.erb: -------------------------------------------------------------------------------- 1 | <%= turbo_stream.replace admin.build_instance({}, params), template: "trestle/resource/#{instance.persisted? ? "show" : "new"}" %> 2 | <%= turbo_stream.reload if modal_request? && instance.persisted? %> 3 | -------------------------------------------------------------------------------- /app/views/trestle/resource/destroy.turbo_stream.erb: -------------------------------------------------------------------------------- 1 | <%= turbo_stream.close_modal instance %> 2 | <%= turbo_stream.flash %> 3 | <%= turbo_stream.reload %> 4 | -------------------------------------------------------------------------------- /app/views/trestle/resource/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% title = admin.t("titles.edit", default: "Editing %{model_name}") %> 2 | 3 | <% content_for(:title, title) %> 4 | <% breadcrumb(title) %> 5 | 6 | <% toolbar(:primary) do |t| %> 7 | <%= t.save_or_dismiss(:update) %> 8 | <%= hook("edit.toolbar.primary", t) %> 9 | <% end %> 10 | 11 | <% toolbar(:secondary) do |t| %> 12 | <%= t.delete %> 13 | <%= hook("edit.toolbar.secondary", t) %> 14 | <% end %> 15 | 16 | <%= resource_turbo_frame(instance) do %> 17 | <%= trestle_form_for instance, url: admin.actions.include?(:update) ? admin.instance_path(instance, action: :update) : "#", method: :patch do |f| %> 18 | <%= render partial: "form", layout: modal_request? ? "modal" : "layout", locals: { wrapper: admin.form.wrapper? } %> 19 | <% end %> 20 | <% end %> 21 | -------------------------------------------------------------------------------- /app/views/trestle/resource/new.html.erb: -------------------------------------------------------------------------------- 1 | <% title = admin.t("titles.new", default: "New %{model_name}") %> 2 | 3 | <% content_for(:title, title) %> 4 | <% breadcrumb(title) %> 5 | 6 | <% toolbar(:primary) do |t| %> 7 | <%= t.save_or_dismiss(:create) %> 8 | <%= hook("new.toolbar.primary", t) %> 9 | <% end %> 10 | 11 | <% toolbar(:secondary) do |t| %> 12 | <%= hook("new.toolbar.secondary", t) %> 13 | <% end %> 14 | 15 | <%= resource_turbo_frame(instance) do %> 16 | <%= trestle_form_for instance, url: admin.path(:create), method: :post do |f| %> 17 | <%= render partial: "form", layout: modal_request? ? "modal" : "layout", locals: { wrapper: admin.form.wrapper? } %> 18 | <% end %> 19 | <% end %> 20 | -------------------------------------------------------------------------------- /app/views/trestle/resource/show.html.erb: -------------------------------------------------------------------------------- 1 | <% title = admin.t("titles.edit", default: "Editing %{model_name}") %> 2 | 3 | <% content_for(:title, title) %> 4 | <% breadcrumb(title) unless admin.singular? %> 5 | 6 | <% toolbar(:primary) do |t| %> 7 | <%= t.save_or_dismiss(:update) %> 8 | <%= hook("show.toolbar.primary", t) %> 9 | <% end %> 10 | 11 | <% toolbar(:secondary) do |t| %> 12 | <%= t.delete %> 13 | <%= hook("show.toolbar.secondary", t) %> 14 | <% end %> 15 | 16 | <%= resource_turbo_frame(instance) do %> 17 | <%= trestle_form_for instance, url: admin.actions.include?(:update) ? admin.instance_path(instance, action: :update) : "#", method: :patch do |f| %> 18 | <%= render partial: "form", layout: modal_request? ? "modal" : "layout", locals: { wrapper: admin.form.wrapper? } %> 19 | <% end %> 20 | <% end %> 21 | -------------------------------------------------------------------------------- /app/views/trestle/resource/update.turbo_stream.erb: -------------------------------------------------------------------------------- 1 | <%= turbo_stream.replace instance, template: "trestle/resource/show" %> 2 | <%= turbo_stream.reload if modal_request? %> 3 | -------------------------------------------------------------------------------- /app/views/trestle/shared/_footer.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <%= Trestle.config.footer %> 3 |
    <%= t("trestle.version", default: "Version") %>: <%= Trestle::VERSION %>
    4 |
    5 | -------------------------------------------------------------------------------- /app/views/trestle/shared/_header.html.erb: -------------------------------------------------------------------------------- 1 | <% if hook?("view.header") %> 2 |
    3 | <%= hook("view.header") %> 4 |
    5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/views/trestle/shared/_title.html.erb: -------------------------------------------------------------------------------- 1 | <% if Trestle.config.site_logo %> 2 | <% if Trestle.config.site_logo_small %> 3 | <%= image_tag(Trestle.config.site_logo, class: "title-large", alt: Trestle.config.site_title) %> 4 | <%= image_tag(Trestle.config.site_logo_small, class: "title-small", alt: Trestle.config.site_title) %> 5 | <% else %> 6 | <%= image_tag(Trestle.config.site_logo) %> 7 | <% end %> 8 | <% elsif Trestle.config.site_logo_small %> 9 | <%= image_tag(Trestle.config.site_logo_small, alt: "") %> 10 | <%= Trestle.config.site_title %> 11 | <% else %> 12 | <%= Trestle.config.site_title %> 13 | <%= Trestle.config.site_title.split(/ /).map(&:first).first(3).join %> 14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/views/trestle/table/_pagination.html.erb: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /app/views/trestle/table/_table.html.erb: -------------------------------------------------------------------------------- 1 | <% table = table.renderer(self) %> 2 | 3 |
    4 | <%= tag.table(id: table.id, class: table.classes, data: table.data) do %> 5 | <% if table.header? %> 6 | 7 | 8 | <% table.columns.each do |column| %> 9 | <%= tag.th(column.header, class: column.classes, data: column.data) %> 10 | <% end %> 11 | 12 | 13 | <% end %> 14 | 15 | 16 | <% collection.each do |instance| %> 17 | <%= table.row.render(instance) %> 18 | <% end %> 19 | 20 | <% end %> 21 |
    22 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "trestle" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /config/locales/cs.rb: -------------------------------------------------------------------------------- 1 | { 2 | cs: { 3 | date: { 4 | formats: { 5 | trestle_date: proc { |date| "%d/%m/%Y" }, 6 | trestle_calendar: "%d/%m/%Y" 7 | } 8 | }, 9 | 10 | time: { 11 | formats: { 12 | trestle_date: proc { |time| "%d/%m/%Y" }, 13 | trestle_time: "%H:%M", 14 | trestle_time_with_seconds: "%H:%M:%S" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/locales/de.rb: -------------------------------------------------------------------------------- 1 | { 2 | de: { 3 | date: { 4 | formats: { 5 | trestle_date: "%-d. %b. %Y", 6 | trestle_calendar: "%d.%m.%Y" 7 | } 8 | }, 9 | 10 | time: { 11 | formats: { 12 | trestle_date: "%-d. %b. %Y", 13 | trestle_time: "%H:%M", 14 | trestle_time_with_seconds: "%H:%M:%S" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/locales/en.rb: -------------------------------------------------------------------------------- 1 | { 2 | en: { 3 | date: { 4 | formats: { 5 | trestle_date: proc { |date| "#{date.day.ordinalize} %b %Y" }, 6 | trestle_calendar: "%-m/%-d/%Y" 7 | } 8 | }, 9 | 10 | time: { 11 | formats: { 12 | trestle_date: proc { |time| "#{time.day.ordinalize} %b %Y" }, 13 | trestle_time: "%-l:%M %p", 14 | trestle_time_with_seconds: "%l:%M:%S %p" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/locales/fr.rb: -------------------------------------------------------------------------------- 1 | { 2 | fr: { 3 | date: { 4 | formats: { 5 | trestle_date: "%e %b %Y", 6 | trestle_calendar: "%d-%m-%Y" 7 | } 8 | }, 9 | 10 | time: { 11 | formats: { 12 | trestle_date: "%e %b %Y", 13 | trestle_time: "%Hh%M", 14 | trestle_time_with_seconds: '%Hh %M\' %S"' 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/locales/ko.rb: -------------------------------------------------------------------------------- 1 | { 2 | ko: { 3 | date: { 4 | formats: { 5 | trestle_date: "%Y년 %m월 %d일", 6 | trestle_calendar: "%Y년 %m월 %d일", 7 | } 8 | }, 9 | 10 | time: { 11 | formats: { 12 | trestle_date: "%Y년 %m월 %d일", 13 | trestle_time: "%H:%M", 14 | trestle_time_with_seconds: "%H:%M:%S" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/locales/lv.rb: -------------------------------------------------------------------------------- 1 | { 2 | lv: { 3 | date: { 4 | formats: { 5 | trestle_date: "%e %b %Y", 6 | trestle_calendar: "%d-%m-%Y" 7 | } 8 | }, 9 | 10 | time: { 11 | formats: { 12 | trestle_date: "%e %b %Y", 13 | trestle_time: "%H:%M", 14 | trestle_time_with_seconds: "%H:%M:%S" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/locales/nl.rb: -------------------------------------------------------------------------------- 1 | { 2 | nl: { 3 | date: { 4 | formats: { 5 | trestle_date: proc { |date| "#{date.day} %b %Y" }, 6 | trestle_calendar: "%-d-%-m-%Y" 7 | } 8 | }, 9 | 10 | time: { 11 | formats: { 12 | trestle_date: proc { |time| "#{time.day} %b %Y" }, 13 | trestle_time: "%R %p", 14 | trestle_time_with_seconds: "%T %p" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/locales/pl.rb: -------------------------------------------------------------------------------- 1 | { 2 | pl: { 3 | date: { 4 | formats: { 5 | trestle_date: "%e %b %Y", 6 | trestle_calendar: "%d-%m-%Y" 7 | } 8 | }, 9 | 10 | time: { 11 | formats: { 12 | trestle_date: "%e %b %Y", 13 | trestle_time: "%H:%M", 14 | trestle_time_with_seconds: "%H:%M:%S" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/locales/pt-BR.rb: -------------------------------------------------------------------------------- 1 | { 2 | 'pt-BR': { 3 | date: { 4 | formats: { 5 | trestle_date: proc { |date| "#{date.day}º %b %Y" }, 6 | trestle_calendar: "%-m/%-d/%Y" 7 | } 8 | }, 9 | 10 | time: { 11 | formats: { 12 | trestle_date: proc { |time| "#{time.day}º %b %Y" }, 13 | trestle_time: "%-l:%M %p", 14 | trestle_time_with_seconds: "%l:%M:%S %p" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/locales/vi.rb: -------------------------------------------------------------------------------- 1 | { 2 | vi: { 3 | date: { 4 | formats: { 5 | trestle_date: proc { |date| "%d/%m/%Y" }, 6 | trestle_calendar: "%d/%m/%Y" 7 | } 8 | }, 9 | 10 | time: { 11 | formats: { 12 | trestle_date: proc { |time| "%d/%m/%Y" }, 13 | trestle_time: "%H:%M", 14 | trestle_time_with_seconds: "%H:%M:%S" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/locales/zh-CN.rb: -------------------------------------------------------------------------------- 1 | { 2 | 'zh-CN' => { 3 | date: { 4 | formats: { 5 | trestle_date: proc { |date| "%Y年%-m月%-d日" }, 6 | trestle_calendar: "%Y年%-m月%-d日" 7 | } 8 | }, 9 | 10 | time: { 11 | formats: { 12 | trestle_date: proc { |time| "%Y年%-m月%-d日" }, 13 | trestle_time: "%H:%M", 14 | trestle_time_with_seconds: "%H:%M:%S" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Trestle::Engine.routes.draw do 2 | Trestle.registry.each do |admin| 3 | instance_eval(&admin.routes) 4 | end 5 | 6 | root to: "trestle/dashboard#index" 7 | end 8 | -------------------------------------------------------------------------------- /frontend/css/_support.scss: -------------------------------------------------------------------------------- 1 | @import "./support/webpack"; 2 | -------------------------------------------------------------------------------- /frontend/css/components/_background.scss: -------------------------------------------------------------------------------- 1 | %textured-bg { 2 | background-image: var(--theme-texture), var(--theme-gradient); 3 | background-repeat: repeat, no-repeat; 4 | } 5 | 6 | @each $variation, $lightness in $theme-bg-variations { 7 | .theme-bg-#{$variation} { 8 | @extend %textured-bg; 9 | background-color: adjust-theme-hsl(primary, $lightness: $lightness); 10 | } 11 | } 12 | 13 | .theme-bg { 14 | @extend .theme-bg-default; 15 | } 16 | 17 | .error-bg { 18 | @extend %textured-bg; 19 | background-color: $error-bg; 20 | } 21 | -------------------------------------------------------------------------------- /frontend/css/components/_badges.scss: -------------------------------------------------------------------------------- 1 | @mixin badge-variant( 2 | $background, 3 | $color: color-contrast($background), 4 | $hover-background: shift-color($background, $badge-hover-scale) 5 | ) { 6 | --#{$prefix}badge-bg: #{$background}; 7 | --#{$prefix}badge-color: #{$color}; 8 | --#{$prefix}badge-hover-bg: #{$hover-background}; 9 | } 10 | 11 | .badge { 12 | background: var(--#{$prefix}badge-bg); 13 | 14 | @at-root a#{&} { 15 | &:hover, 16 | &:focus { 17 | color: var(--#{$prefix}badge-color); 18 | background-color: var(--#{$prefix}badge-hover-bg); 19 | } 20 | } 21 | } 22 | 23 | .badge-pill { 24 | border-radius: $border-radius-pill; 25 | } 26 | 27 | @each $color, $value in $theme-colors { 28 | .badge-#{$color} { 29 | @include badge-variant($value); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /frontend/css/components/_breadcrumbs.scss: -------------------------------------------------------------------------------- 1 | .breadcrumb { 2 | --#{$prefix}breadcrumb-font-size: 0.75rem; 3 | --#{$prefix}breadcrumb-item-padding-x: 0.35rem; 4 | 5 | @include media-breakpoint-down(md) { 6 | --#{$prefix}breadcrumb-font-size: 0.625rem; 7 | --#{$prefix}breadcrumb-item-padding-x: 0.25rem; 8 | } 9 | } 10 | 11 | .breadcrumb-item { 12 | a { 13 | font-weight: normal; 14 | } 15 | 16 | &.active a { 17 | color: inherit; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /frontend/css/components/_grid.scss: -------------------------------------------------------------------------------- 1 | .grid { 2 | --grid-gap: var(--content-area-margin, 0); 3 | --grid-column-count: 1; 4 | --grid-column-min-width: 0; 5 | 6 | display: grid; 7 | grid-template-columns: repeat(var(--grid-column-count), minmax(var(--grid-column-min-width), 1fr)); 8 | gap: var(--grid-gap); 9 | 10 | > .grid-col-full { 11 | grid-column: 1 / -1; 12 | } 13 | } 14 | 15 | @for $cols from 2 through 12 { 16 | .grid-cols-#{$cols} { 17 | --grid-column-count: #{$cols}; 18 | } 19 | } 20 | 21 | @each $breakpoint in map-keys($grid-breakpoints) { 22 | @include media-breakpoint-up($breakpoint) { 23 | @for $cols from 2 through 12 { 24 | .grid-cols-#{$breakpoint}-#{$cols} { 25 | --grid-column-count: #{$cols}; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /frontend/css/components/_pagination.scss: -------------------------------------------------------------------------------- 1 | .pagination-container { 2 | margin-left: auto; 3 | 4 | display: inline-flex; 5 | align-items: center; 6 | gap: 0.75rem; 7 | 8 | p { 9 | font-size: 0.75rem; 10 | margin: 0; 11 | } 12 | 13 | strong { 14 | color: black; 15 | } 16 | 17 | .pagination { 18 | display: inline-flex; 19 | margin: 0; 20 | 21 | a { 22 | font-weight: normal; 23 | } 24 | } 25 | } 26 | 27 | @include media-breakpoint-down(md) { 28 | .pagination-container { 29 | margin-right: auto; 30 | 31 | text-align: center; 32 | 33 | display: flex; 34 | flex-direction: column; 35 | align-items: center; 36 | gap: 0.375rem; 37 | 38 | .pagination { 39 | margin: 0 auto; 40 | order: -1; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /frontend/css/components/_photoswipe.scss: -------------------------------------------------------------------------------- 1 | .pswp__content { 2 | iframe, 3 | video { 4 | pointer-events: auto; 5 | width: 100%; 6 | height: 100%; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /frontend/css/components/_popover.scss: -------------------------------------------------------------------------------- 1 | .popover-header { 2 | font-weight: 500; 3 | font-size: 0.75rem; 4 | border-bottom-color: $popover-header-border; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/css/components/_sort.scss: -------------------------------------------------------------------------------- 1 | .sort { 2 | &, &:hover, &:focus { 3 | color: $body-color; 4 | text-decoration: none; 5 | } 6 | 7 | &:after { 8 | @include icon-fa($fa-var-bars); 9 | 10 | font-size: 75%; 11 | margin-left: 0.325rem; 12 | opacity: 0.25; 13 | } 14 | 15 | &:hover:after { 16 | opacity: 0.5; 17 | } 18 | 19 | &.active:after { 20 | opacity: 1.0; 21 | } 22 | 23 | &.sort-asc:after { 24 | @include icon-fa($fa-var-chevron-up); 25 | } 26 | 27 | &.sort-desc:after { 28 | @include icon-fa($fa-var-chevron-down); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /frontend/css/components/_timestamp.scss: -------------------------------------------------------------------------------- 1 | .timestamp { 2 | line-height: 1.2; 3 | white-space: nowrap; 4 | 5 | small { 6 | display: block; 7 | opacity: 0.75; 8 | } 9 | 10 | &.timestamp-inline, 11 | &.inline { 12 | small { 13 | display: inline; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /frontend/css/components/_turbo.scss: -------------------------------------------------------------------------------- 1 | .turbo-progress-bar { 2 | background-color: var(--primary); 3 | } 4 | -------------------------------------------------------------------------------- /frontend/css/core/_dependencies.scss: -------------------------------------------------------------------------------- 1 | @import "./bootstrap"; 2 | 3 | @import "~photoswipe/dist/photoswipe.css"; 4 | @import "~flatpickr/dist/flatpickr"; 5 | @import "~select2/src/scss/core"; 6 | @import "~select2-bootstrap-5-theme/src/include-all"; 7 | -------------------------------------------------------------------------------- /frontend/css/core/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin font-smoothing($value: on) { 2 | @if $value == on { 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; 5 | } @else { 6 | -webkit-font-smoothing: subpixel-antialiased; 7 | -moz-osx-font-smoothing: auto; 8 | } 9 | } 10 | 11 | @mixin icon-fa($content) { 12 | font-family: 'Font Awesome 6 Free'; 13 | font-weight: 900; 14 | content: fa-content($content); 15 | } 16 | 17 | @mixin sr-only { 18 | @include visually-hidden; 19 | } 20 | -------------------------------------------------------------------------------- /frontend/css/icons/_fontawesome.scss: -------------------------------------------------------------------------------- 1 | $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; 2 | @import "~@fortawesome/fontawesome-free/scss/fontawesome"; 3 | @import "~@fortawesome/fontawesome-free/scss/regular"; 4 | @import "~@fortawesome/fontawesome-free/scss/solid"; 5 | @import "~@fortawesome/fontawesome-free/scss/brands"; 6 | @import "~@fortawesome/fontawesome-free/scss/v4-shims"; 7 | -------------------------------------------------------------------------------- /frontend/css/layout/_footer.scss: -------------------------------------------------------------------------------- 1 | .app-footer { 2 | flex-shrink: 0; 3 | 4 | background: $footer-bg; 5 | color: $footer-color; 6 | 7 | padding: $footer-padding; 8 | font-size: 0.625rem; 9 | 10 | display: flex; 11 | justify-content: space-between; 12 | 13 | @include media-breakpoint-down(md) { 14 | padding-left: 0.625rem; 15 | padding-right: 0.625rem; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /frontend/css/layout/_header.scss: -------------------------------------------------------------------------------- 1 | @use "sass:math"; 2 | 3 | .app-header { 4 | flex: 0 0 $header-height; 5 | height: $header-height; 6 | 7 | display: flex; 8 | align-items: center; 9 | justify-content: flex-end; 10 | 11 | padding-left: $grid-gutter-width; 12 | padding-right: $grid-gutter-width; 13 | 14 | background: $header-bg; 15 | } 16 | 17 | @include media-breakpoint-down(md) { 18 | .app-header { 19 | margin-top: -$header-height; 20 | 21 | padding-left: math.div($grid-gutter-width, 2); 22 | padding-right: math.div($grid-gutter-width, 2); 23 | 24 | // Hide all elements by default 25 | > * { 26 | display: none; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /frontend/css/support/_sprockets.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | // Mixins & functions 4 | @import "bootstrap/scss/functions"; 5 | @import "bootstrap/scss/mixins"; 6 | @import "trestle/frontend/css/core/functions"; 7 | @import "trestle/frontend/css/core/mixins"; 8 | 9 | // Variables 10 | @import "trestle/frontend/css/variables/bootstrap"; 11 | @import "trestle/frontend/css/variables/trestle"; 12 | @import "bootstrap/scss/variables"; 13 | 14 | // Icon variables 15 | @import "@fortawesome/fontawesome-free/scss/functions"; 16 | @import "@fortawesome/fontawesome-free/scss/variables"; 17 | -------------------------------------------------------------------------------- /frontend/css/support/_webpack.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | // Mixins & functions 4 | @import "~bootstrap/scss/functions"; 5 | @import "~bootstrap/scss/mixins"; 6 | @import "../core/functions"; 7 | @import "../core/mixins"; 8 | 9 | // Variables 10 | @import "../variables/bootstrap"; 11 | @import "../variables/trestle"; 12 | @import "~bootstrap/scss/variables"; 13 | 14 | // Icon variables 15 | @import "~@fortawesome/fontawesome-free/scss/functions"; 16 | @import "~@fortawesome/fontawesome-free/scss/variables"; 17 | -------------------------------------------------------------------------------- /frontend/css/variables/_maps.scss: -------------------------------------------------------------------------------- 1 | // Update color maps 2 | 3 | $custom-colors: ( 4 | // Backwards compatibility 5 | "default": #bbbbbb 6 | ); 7 | 8 | $theme-colors: map-merge($theme-colors, $custom-colors); 9 | 10 | 11 | // Disable box shadows on form controls. 12 | // This is done after importing the default Bootstrap variables as a null value would be overwritten. 13 | 14 | $input-box-shadow: null; 15 | $form-select-box-shadow: null; 16 | -------------------------------------------------------------------------------- /frontend/images/bright-squares.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/frontend/images/bright-squares.png -------------------------------------------------------------------------------- /frontend/index.js: -------------------------------------------------------------------------------- 1 | // CSS entry point 2 | import './css/index.scss' 3 | 4 | // JS entry point 5 | export { default as Trestle } from './js/index.js' 6 | -------------------------------------------------------------------------------- /frontend/js/controllers/confirm_delete_controller.js: -------------------------------------------------------------------------------- 1 | import ConfirmController from './confirm_controller' 2 | 3 | import { i18n } from '../core/i18n' 4 | 5 | export default class extends ConfirmController { 6 | static defaultConfirmClass = 'btn-danger' 7 | 8 | get confirmLabel () { 9 | return this.confirmLabelValue || i18n.t('trestle.confirmation.delete', { defaultValue: 'Delete' }) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/js/controllers/datepicker_controller.js: -------------------------------------------------------------------------------- 1 | import FlatpickrController from './flatpickr_controller' 2 | 3 | import { i18n } from '../core/i18n' 4 | 5 | export default class extends FlatpickrController { 6 | get options () { 7 | return { 8 | ...super.options, 9 | altFormat: i18n.t('admin.datepicker.formats.date', { defaultValue: 'm/d/Y' }) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /frontend/js/controllers/datetimepicker_controller.js: -------------------------------------------------------------------------------- 1 | import FlatpickrController from './flatpickr_controller' 2 | 3 | import { i18n } from '../core/i18n' 4 | 5 | export default class extends FlatpickrController { 6 | get options () { 7 | return { 8 | ...super.options, 9 | enableTime: true, 10 | altFormat: i18n.t('admin.datepicker.formats.datetime', { defaultValue: 'm/d/Y h:i K' }) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/js/controllers/deprecated/init_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from '@hotwired/stimulus' 2 | 3 | import { triggerInit } from '../../deprecated/events' 4 | 5 | export default class extends Controller { 6 | connect () { 7 | triggerInit(this.element) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /frontend/js/controllers/form_error_controller.js: -------------------------------------------------------------------------------- 1 | import ApplicationController from './application_controller' 2 | 3 | import ErrorModal from '../core/error_modal' 4 | 5 | export default class extends ApplicationController { 6 | connect () { 7 | this.appendAction('turbo:before-fetch-response', 'handleFormResponse') 8 | } 9 | 10 | handleFormResponse (e) { 11 | const response = e.detail.fetchResponse.response 12 | 13 | if (response.status >= 500) { 14 | e.preventDefault() 15 | 16 | const title = `${response.status} (${response.statusText})` 17 | response.text().then(content => ErrorModal.show({ title, content })) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend/js/controllers/gallery_controller.js: -------------------------------------------------------------------------------- 1 | import LightboxController from './lightbox_controller' 2 | 3 | export default class extends LightboxController { 4 | static targets = ["image"] 5 | 6 | get options () { 7 | return { 8 | ...super.options, 9 | children: this.imageTargets 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /frontend/js/controllers/index.js: -------------------------------------------------------------------------------- 1 | import { Application } from '@hotwired/stimulus' 2 | import { definitionsFromContext } from '@hotwired/stimulus-webpack-helpers' 3 | 4 | import ApplicationController from './application_controller' 5 | 6 | window.Stimulus = Application.start() 7 | 8 | const context = require.context('.', true, /\.js$/) 9 | const controllerDefinitions = definitionsFromContext(context) 10 | Stimulus.load(controllerDefinitions) 11 | 12 | const Controllers = controllerDefinitions.reduce((result, definition) => { 13 | return { ...result, [definition.identifier]: definition.controllerConstructor } 14 | }, {}) 15 | 16 | export { 17 | ApplicationController, 18 | Controllers 19 | } 20 | -------------------------------------------------------------------------------- /frontend/js/controllers/keyboard_submit_controller.js: -------------------------------------------------------------------------------- 1 | import ApplicationController from './application_controller' 2 | 3 | export default class extends ApplicationController { 4 | connect () { 5 | this.appendAction('keydown', 'handleKeyDown') 6 | } 7 | 8 | handleKeyDown (e) { 9 | if (e.key === 'Enter' && this.preventEnterKey(e.target)) { 10 | e.preventDefault() 11 | } 12 | } 13 | 14 | preventEnterKey (el) { 15 | return el.matches('input:not([type="submit"])') 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /frontend/js/controllers/mobile_sidebar_controller.js: -------------------------------------------------------------------------------- 1 | import ApplicationController from './application_controller' 2 | 3 | export default class extends ApplicationController { 4 | show () { 5 | this.wrapperController.animate() 6 | document.body.classList.add('mobile-nav-expanded') 7 | } 8 | 9 | hide () { 10 | this.wrapperController.animate() 11 | document.body.classList.remove('mobile-nav-expanded') 12 | } 13 | 14 | toggle () { 15 | this.wrapperController.animate() 16 | document.body.classList.toggle('mobile-nav-expanded') 17 | } 18 | 19 | get isExpanded () { 20 | return document.body.classList.contains('mobile-nav-expanded') 21 | } 22 | 23 | get wrapperElement () { 24 | return document.getElementById('app-wrapper') 25 | } 26 | 27 | get wrapperController () { 28 | return this.application.getControllerForElementAndIdentifier(this.wrapperElement, 'wrapper') 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /frontend/js/controllers/modal_controller.js: -------------------------------------------------------------------------------- 1 | import ApplicationController from './application_controller' 2 | 3 | import Modal from '../core/modal' 4 | 5 | export default class extends ApplicationController { 6 | connect () { 7 | this.modal = new Modal(this.element) 8 | this.modal.show() 9 | 10 | this.appendAction('hidden.bs.modal', 'remove') 11 | } 12 | 13 | disconnect () { 14 | this.modal.dispose() 15 | } 16 | 17 | remove () { 18 | this.element.remove() 19 | } 20 | 21 | hide () { 22 | this.modal.hide() 23 | } 24 | 25 | submit (detail) { 26 | let event = this.dispatch('submit', { detail: detail }) 27 | if (event.defaultPrevented) return 28 | 29 | if (this.modalTrigger) { 30 | event = this.modalTrigger.dispatch('submit', { detail: detail }) 31 | if (event.defaultPrevented) return 32 | } 33 | 34 | this.hide() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /frontend/js/controllers/modal_frame_controller.js: -------------------------------------------------------------------------------- 1 | import ApplicationController from './application_controller' 2 | 3 | export default class extends ApplicationController { 4 | connect () { 5 | this.appendAction('turbo:before-fetch-request', 'addTrestleModalHeader') 6 | } 7 | 8 | addTrestleModalHeader (event) { 9 | event.detail.fetchOptions.headers['X-Trestle-Modal'] = true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/js/controllers/navigation_tooltip_controller.js: -------------------------------------------------------------------------------- 1 | import ApplicationController from './application_controller' 2 | 3 | import { Tooltip } from 'bootstrap' 4 | 5 | const TEMPLATE = ` 6 | 10 | ` 11 | 12 | export default class extends ApplicationController { 13 | static targets = ["label"] 14 | 15 | connect () { 16 | this.tooltip = new Tooltip(this.element, { 17 | trigger: 'hover', 18 | placement: 'right', 19 | boundary: 'window', 20 | template: TEMPLATE, 21 | title: () => this.labelText 22 | }) 23 | } 24 | 25 | disconnect () { 26 | this.tooltip.dispose() 27 | } 28 | 29 | get labelText () { 30 | return this.labelTarget.innerText 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /frontend/js/controllers/reloadable_controller.js: -------------------------------------------------------------------------------- 1 | import ApplicationController from './application_controller' 2 | 3 | export default class extends ApplicationController { 4 | static values = { 5 | url: String 6 | } 7 | 8 | connect () { 9 | this.appendAction('turbo:frame-load', 'scrollToTop') 10 | } 11 | 12 | scrollToTop (e) { 13 | const scrollTarget = this.element.closest('[data-scroll-target]') 14 | const boundingRect = scrollTarget.getBoundingClientRect() 15 | 16 | if (scrollTarget && boundingRect.top < 0) { 17 | scrollTarget.scrollIntoView() 18 | } 19 | } 20 | 21 | reload () { 22 | if (this.element.src) { 23 | this.element.reload() 24 | } else if (this.hasUrlValue) { 25 | this.element.src = this.urlValue 26 | } else { 27 | this.element.src = document.location.href 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /frontend/js/controllers/timepicker_controller.js: -------------------------------------------------------------------------------- 1 | import FlatpickrController from './flatpickr_controller' 2 | 3 | import { i18n } from '../core/i18n' 4 | 5 | export default class extends FlatpickrController { 6 | get options () { 7 | return { 8 | ...super.options, 9 | enableTime: true, 10 | noCalendar: true, 11 | altFormat: i18n.t('admin.datepicker.formats.time', { defaultValue: 'h:i K' }) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /frontend/js/controllers/toggle_attr_controller.js: -------------------------------------------------------------------------------- 1 | import ApplicationController from './application_controller' 2 | 3 | export default class extends ApplicationController { 4 | static targets = ["item"] 5 | 6 | static values = { 7 | attribute: String 8 | } 9 | 10 | toggle () { 11 | this.itemTargets.forEach((item) => { 12 | item[this.attributeValue] = !item[this.attributeValue] 13 | }) 14 | } 15 | 16 | set () { 17 | this.itemTargets.forEach((item) => { 18 | item[this.attributeValue] = true 19 | }) 20 | } 21 | 22 | unset () { 23 | this.itemTargets.forEach((item) => { 24 | item[this.attributeValue] = false 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /frontend/js/controllers/toggle_class_controller.js: -------------------------------------------------------------------------------- 1 | import ApplicationController from './application_controller' 2 | 3 | export default class extends ApplicationController { 4 | static targets = ["item"] 5 | 6 | static values = { 7 | class: String 8 | } 9 | 10 | toggle () { 11 | this.itemTargets.forEach((item) => { 12 | item.classList.toggle(this.classValue) 13 | }) 14 | } 15 | 16 | add () { 17 | this.itemTargets.forEach((item) => { 18 | item.classList.add(this.classValue) 19 | }) 20 | } 21 | 22 | remove () { 23 | this.itemTargets.forEach((item) => { 24 | item.classList.remove(this.classValue) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /frontend/js/controllers/tooltip_controller.js: -------------------------------------------------------------------------------- 1 | import ApplicationController from './application_controller' 2 | 3 | import { Tooltip } from 'bootstrap' 4 | 5 | export default class extends ApplicationController { 6 | connect () { 7 | this.tooltip = new Tooltip(this.element) 8 | } 9 | 10 | disconnect () { 11 | this.tooltip.dispose() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/js/core/cookie.js: -------------------------------------------------------------------------------- 1 | export function getCookie (name) { 2 | name += '=' 3 | 4 | var cookies = document.cookie.split(/;\s*/) 5 | 6 | for (let i = cookies.length - 1; i >= 0; i--) { 7 | if (!cookies[i].indexOf(name)) { 8 | var value = cookies[i].replace(name, '') 9 | return decodeURIComponent(value) 10 | } 11 | } 12 | 13 | return '' 14 | } 15 | 16 | export function setCookie (name, value) { 17 | document.cookie = `${name}=${encodeURIComponent(value)}; path=/` 18 | } 19 | 20 | export function deleteCookie (name) { 21 | document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT` 22 | } 23 | 24 | export default { 25 | get: getCookie, 26 | set: setCookie, 27 | delete: deleteCookie 28 | } 29 | -------------------------------------------------------------------------------- /frontend/js/core/turbo_errors.js: -------------------------------------------------------------------------------- 1 | import ErrorModal from './error_modal' 2 | 3 | document.addEventListener('turbo:frame-missing', async (e) => { 4 | e.preventDefault() 5 | 6 | const response = e.detail.response 7 | const title = `${response.status} (${response.statusText})` 8 | response.text().then(content => ErrorModal.show({ title, content })) 9 | }) 10 | -------------------------------------------------------------------------------- /frontend/js/deprecated/tooltip.js: -------------------------------------------------------------------------------- 1 | import { Tooltip } from 'bootstrap' 2 | 3 | document.addEventListener('turbo:load', function () { 4 | // eslint-disable-next-line no-new 5 | new Tooltip(document.body, { 6 | selector: '[data-toggle="tooltip"]' 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /frontend/js/mixins/index.js: -------------------------------------------------------------------------------- 1 | import usePhotoSwipe from './photoswipe' 2 | 3 | export default { 4 | usePhotoSwipe 5 | } 6 | -------------------------------------------------------------------------------- /frontend/js/mixins/photoswipe.js: -------------------------------------------------------------------------------- 1 | const usePhotoSwipe = (controller) => { 2 | Object.assign(controller, { 3 | loadPhotoSwipe () { 4 | return import(/* webpackChunkName: "photoswipe" */ 'photoswipe') 5 | .then(module => module.default) 6 | } 7 | }) 8 | } 9 | 10 | export default usePhotoSwipe 11 | -------------------------------------------------------------------------------- /gemfiles/rails-6.0.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :test do 4 | gem 'coveralls_reborn', require: false 5 | gem 'simplecov', require: false 6 | 7 | gem 'capybara' 8 | gem 'selenium-webdriver', '>= 4.1.0' 9 | gem 'puma', '~> 5.6.8' 10 | 11 | gem 'haml', '~> 6.0' 12 | end 13 | 14 | gem 'rails', '~> 6.0.0' 15 | gem 'sprockets-rails' 16 | gem 'sqlite3', '~> 1.4' 17 | gem 'turbo-rails', '2.0.7' 18 | 19 | gemspec path: "../" 20 | -------------------------------------------------------------------------------- /gemfiles/rails-6.1.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :test do 4 | gem 'coveralls_reborn', require: false 5 | gem 'simplecov', require: false 6 | 7 | gem 'capybara' 8 | gem 'selenium-webdriver', '>= 4.1.0' 9 | gem 'puma', '~> 5.6.8' 10 | 11 | gem 'haml', '~> 6.0' 12 | end 13 | 14 | gem 'rails', '~> 6.1.0' 15 | gem 'sprockets-rails' 16 | gem 'sqlite3', '~> 1.4' 17 | gem 'turbo-rails', '2.0.7' 18 | 19 | gemspec path: "../" 20 | -------------------------------------------------------------------------------- /gemfiles/rails-7.0-propshaft.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :test do 4 | gem 'coveralls_reborn', require: false 5 | gem 'simplecov', require: false 6 | 7 | gem 'capybara' 8 | gem 'selenium-webdriver' 9 | gem 'puma' 10 | 11 | gem 'haml', '~> 6.0' 12 | end 13 | 14 | gem 'rails', '~> 7.0.0' 15 | gem 'propshaft' 16 | gem 'sqlite3', '~> 1.4' 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /gemfiles/rails-7.0.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :test do 4 | gem 'coveralls_reborn', require: false 5 | gem 'simplecov', require: false 6 | 7 | gem 'capybara' 8 | gem 'selenium-webdriver', '>= 4.9.0' 9 | gem 'puma' 10 | 11 | gem 'haml', '~> 6.0' 12 | end 13 | 14 | gem 'rails', '~> 7.0.0' 15 | gem 'sprockets-rails' 16 | gem 'sqlite3', '~> 1.4' 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /gemfiles/rails-7.1-propshaft.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :test do 4 | gem 'coveralls_reborn', require: false 5 | gem 'simplecov', require: false 6 | 7 | gem 'capybara' 8 | gem 'selenium-webdriver' 9 | gem 'puma' 10 | 11 | gem 'haml', '~> 6.0' 12 | end 13 | 14 | gem 'rails', '~> 7.1.0' 15 | gem 'propshaft' 16 | gem 'sqlite3', '~> 1.4' 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /gemfiles/rails-7.1-sassc-rails.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :test do 4 | gem 'coveralls_reborn', require: false 5 | gem 'simplecov', require: false 6 | 7 | gem 'capybara' 8 | gem 'selenium-webdriver' 9 | gem 'puma' 10 | 11 | gem 'haml', '~> 6.0' 12 | end 13 | 14 | gem 'rails', '~> 7.1.0' 15 | gem 'sprockets-rails' 16 | gem 'sassc-rails' 17 | gem 'sqlite3', '~> 1.4' 18 | 19 | gemspec path: "../" 20 | -------------------------------------------------------------------------------- /gemfiles/rails-7.1.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :test do 4 | gem 'coveralls_reborn', require: false 5 | gem 'simplecov', require: false 6 | 7 | gem 'capybara' 8 | gem 'selenium-webdriver' 9 | gem 'puma' 10 | 11 | gem 'haml', '~> 6.0' 12 | end 13 | 14 | gem 'rails', '~> 7.1.0' 15 | gem 'sprockets-rails' 16 | gem 'sqlite3', '~> 1.4' 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /gemfiles/rails-7.2-propshaft.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :test do 4 | gem 'coveralls_reborn', require: false 5 | gem 'simplecov', require: false 6 | 7 | gem 'capybara' 8 | gem 'selenium-webdriver' 9 | gem 'puma' 10 | 11 | gem 'haml', '~> 6.0' 12 | end 13 | 14 | gem 'rails', '~> 7.2.0' 15 | gem 'propshaft' 16 | gem 'sqlite3', '~> 1.4' 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /gemfiles/rails-7.2.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :test do 4 | gem 'coveralls_reborn', require: false 5 | gem 'simplecov', require: false 6 | 7 | gem 'capybara' 8 | gem 'selenium-webdriver' 9 | gem 'puma' 10 | 11 | gem 'haml', '~> 6.0' 12 | end 13 | 14 | gem 'rails', '~> 7.2.0' 15 | gem 'sprockets-rails' 16 | gem 'sqlite3', '~> 1.4' 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /gemfiles/rails-8.0-propshaft.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :test do 4 | gem 'coveralls_reborn', require: false 5 | gem 'simplecov', require: false 6 | 7 | gem 'capybara' 8 | gem 'selenium-webdriver' 9 | gem 'puma' 10 | 11 | gem 'haml', '~> 6.0' 12 | end 13 | 14 | gem 'rails', '~> 8.0.0' 15 | gem 'propshaft' 16 | gem 'sqlite3' 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /gemfiles/rails-8.0.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :test do 4 | gem 'coveralls_reborn', require: false 5 | gem 'simplecov', require: false 6 | 7 | gem 'capybara' 8 | gem 'selenium-webdriver' 9 | gem 'puma' 10 | 11 | gem 'haml', '~> 6.0' 12 | end 13 | 14 | gem 'rails', '~> 8.0.0' 15 | gem 'sprockets-rails' 16 | gem 'sqlite3' 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /i18n/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | translations: 3 | - file: i18n/locale/:locale.json 4 | patterns: 5 | - "*" 6 | - "!*.activerecord" 7 | - "!*.errors" 8 | - "!*.number.nth" 9 | 10 | export_files: 11 | enabled: true 12 | files: 13 | - template: i18n/template.erb 14 | output: app/assets/bundle/trestle/locale/%{base_name}.js -------------------------------------------------------------------------------- /i18n/environment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler/setup" 4 | require "rails" 5 | require "active_support/railtie" 6 | require "action_view/railtie" 7 | 8 | I18n.load_path += Dir["./config/locales/**/*.yml"] -------------------------------------------------------------------------------- /i18n/export: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | bundle exec i18n export -c i18n/config.yml -r i18n/environment.rb -------------------------------------------------------------------------------- /i18n/template.erb: -------------------------------------------------------------------------------- 1 | Trestle.i18n.store(<%= JSON.pretty_generate(translations) %>); -------------------------------------------------------------------------------- /lib/generators/trestle/admin/admin_generator.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Generators 3 | class AdminGenerator < ::Rails::Generators::NamedBase 4 | desc "Creates a non-resourceful Trestle admin" 5 | 6 | source_root File.expand_path("../templates", __FILE__) 7 | 8 | def create_admin 9 | template "admin.rb.erb", File.join("app/admin", class_path, "#{singular_name}_admin.rb") 10 | end 11 | 12 | def create_template 13 | template "index.html.erb", File.join("app/views/admin", class_path, singular_name, "index.html.erb") 14 | end 15 | 16 | protected 17 | def module_name 18 | class_name.deconstantize 19 | end 20 | 21 | def module? 22 | module_name.present? 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/generators/trestle/admin/templates/admin.rb.erb: -------------------------------------------------------------------------------- 1 | Trestle.admin(:<%= singular_name %><% if module? %>, scope: <%= module_name %><% end %>) do 2 | menu do 3 | item :<%= singular_name %>, icon: "fa fa-star" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/trestle/admin/templates/index.html.erb: -------------------------------------------------------------------------------- 1 | <%% content_for(:title, "<%= singular_name.titleize %>") %> 2 | 3 | <%%= render layout: "layout", locals: { wrapper: true, sidebar: false, hide_breadcrumbs: false } do %> 4 | To customize this template, please edit <%= File.join("app/views/admin", class_path, singular_name, "index.html.erb") %>. 5 | <%% end %> 6 | -------------------------------------------------------------------------------- /lib/generators/trestle/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Generators 3 | class InstallGenerator < ::Rails::Generators::Base 4 | desc "Installs Trestle and creates files for configuration and asset customization" 5 | 6 | source_root File.expand_path("../templates", __FILE__) 7 | 8 | def create_initializer 9 | template "trestle.rb.erb", "config/initializers/trestle.rb" 10 | end 11 | 12 | def create_assets 13 | css = (defined?(Sass) || defined?(SassC)) ? "scss" : "css" 14 | template "_custom.#{css}", "app/assets/stylesheets/trestle/_custom.#{css}" 15 | template "custom.js", "app/assets/javascripts/trestle/custom.js" 16 | end 17 | 18 | def create_directory 19 | empty_directory "app/admin" 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/generators/trestle/install/templates/_custom.css: -------------------------------------------------------------------------------- 1 | /* This file may be used for providing additional customizations to the Trestle 2 | * admin. It will be automatically included within all admin pages. 3 | * 4 | * For organizational purposes, you may wish to define your customizations 5 | * within individual partials in this folder and they'll be required below. 6 | * 7 | *= require_tree . 8 | */ 9 | -------------------------------------------------------------------------------- /lib/generators/trestle/install/templates/_custom.scss: -------------------------------------------------------------------------------- 1 | // This file may be used for providing additional customizations to the Trestle 2 | // admin. It will be automatically included within all admin pages. 3 | // 4 | // For organizational purposes, you may wish to define your customizations 5 | // within individual partials and `import` them here, e.g. 6 | // 7 | // @import "trestle/custom/my_custom_css"; 8 | // 9 | -------------------------------------------------------------------------------- /lib/generators/trestle/install/templates/custom.js: -------------------------------------------------------------------------------- 1 | // This file may be used for providing additional customizations to the Trestle 2 | // admin. It will be automatically included within all admin pages. 3 | // 4 | // For organizational purposes, you may wish to define your customizations 5 | // within individual partials and `require` them here. 6 | // 7 | // e.g. //= require "trestle/custom/my_custom_js" 8 | -------------------------------------------------------------------------------- /lib/trestle/adapters.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Adapters 3 | extend ActiveSupport::Autoload 4 | 5 | require_relative "adapters/adapter" 6 | 7 | autoload :ActiveRecordAdapter 8 | autoload :DraperAdapter 9 | autoload :SequelAdapter 10 | 11 | # Creates a new Adapter class with the given modules mixed in 12 | def self.compose(*modules) 13 | Class.new(Adapter) do 14 | modules.each { |mod| include(mod) } 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/trestle/adapters/draper_adapter.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | module Adapters 3 | module DraperAdapter 4 | def decorate_collection(collection) 5 | if decorator = admin.decorator 6 | decorator.decorate_collection(collection) 7 | else 8 | super 9 | end 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/trestle/attribute.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Attribute 3 | attr_reader :name, :type, :options 4 | 5 | def initialize(name, type, options={}) 6 | @name, @type, @options = name.to_sym, type, options 7 | end 8 | 9 | def array? 10 | options[:array] == true 11 | end 12 | 13 | class Association < Attribute 14 | def initialize(name, options={}) 15 | super(name, :association, options) 16 | end 17 | 18 | def association_name 19 | options[:name] || name.to_s.sub(/_id$/, "") 20 | end 21 | 22 | def association_class 23 | options[:class].respond_to?(:call) ? options[:class].call : options[:class] 24 | end 25 | 26 | def polymorphic? 27 | options[:polymorphic] == true 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/trestle/builder.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Builder 3 | def self.target(name) 4 | attr_reader name 5 | alias_method :target, name 6 | end 7 | 8 | def self.build(*args, &block) 9 | new(*args).build(&block) 10 | end 11 | 12 | def build(&block) 13 | instance_eval(&block) if block_given? 14 | target 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/trestle/debug_errors.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class DebugErrors 3 | def initialize(errors) 4 | @errors = errors 5 | end 6 | 7 | def any? 8 | @errors.any? 9 | end 10 | 11 | def each 12 | if defined?(ActiveModel::Error) 13 | # Rails 6.1 introduces a unified Error class 14 | @errors.each do |error| 15 | yield error.attribute, error.message 16 | end 17 | else 18 | @errors.each do |error, message| 19 | yield error, message 20 | end 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/trestle/display.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Display 3 | def initialize(instance) 4 | @instance = instance 5 | end 6 | 7 | def to_s 8 | if display_method != :to_s || @instance.method(display_method).source_location 9 | @instance.public_send(display_method) 10 | else 11 | "#{@instance.class} (##{@instance.id})" 12 | end 13 | end 14 | 15 | private 16 | def display_method 17 | @display_method ||= Trestle.config.display_methods.find { |m| @instance.respond_to?(m) } || :to_s 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/trestle/form/fields.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Form 3 | module Fields 4 | require_relative "fields/form_control" 5 | require_relative "fields/form_group" 6 | 7 | require_relative "fields/date_picker" 8 | 9 | require_relative "fields/check_box_helpers" 10 | require_relative "fields/radio_button_helpers" 11 | 12 | Dir.glob("#{__dir__}/fields/*.rb") { |f| require_relative f } 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/check_box_helpers.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Form 3 | module Fields 4 | module CheckBoxHelpers 5 | def switch? 6 | options[:switch] 7 | end 8 | 9 | def inline? 10 | options[:inline] 11 | end 12 | 13 | def default_wrapper_class 14 | [ 15 | "form-check", 16 | ("form-switch" if switch?), 17 | ("form-check-inline" if inline?) 18 | ].compact 19 | end 20 | 21 | def input_class 22 | ["form-check-input"] 23 | end 24 | 25 | def label_class 26 | ["form-check-label"] 27 | end 28 | 29 | def defaults 30 | Trestle::Options.new(disabled: readonly?) 31 | end 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/color_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::ColorField < Trestle::Form::Fields::FormControl 2 | def field 3 | builder.raw_color_field(name, options) 4 | end 5 | 6 | def defaults 7 | super.merge(class: ["form-control-color"]) 8 | end 9 | end 10 | 11 | Trestle::Form::Builder.register(:color_field, Trestle::Form::Fields::ColorField) 12 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/date_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::DateField < Trestle::Form::Fields::FormControl 2 | include Trestle::Form::Fields::DatePicker 3 | 4 | def field 5 | builder.raw_date_field(name, options) 6 | end 7 | 8 | def controller 9 | "datepicker" 10 | end 11 | end 12 | 13 | Trestle::Form::Builder.register(:date_field, Trestle::Form::Fields::DateField) 14 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/date_picker.rb: -------------------------------------------------------------------------------- 1 | module Trestle::Form::Fields::DatePicker 2 | def normalize_options! 3 | unless options[:prepend] == false 4 | options[:prepend] ||= options.delete(:icon) { default_icon } 5 | end 6 | 7 | super 8 | end 9 | 10 | def defaults 11 | defaults = super 12 | defaults.merge!(data: { controller: controller, allow_clear: true }) if enable_date_picker? 13 | defaults 14 | end 15 | 16 | def default_icon 17 | icon("fa fa-calendar") 18 | end 19 | 20 | def enable_date_picker? 21 | !disabled? && !readonly? && options[:picker] != false 22 | end 23 | 24 | def controller 25 | "datepicker" 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/date_select.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Form 3 | module Fields 4 | class DateSelect < Field 5 | attr_reader :html_options 6 | 7 | def initialize(builder, template, name, options={}, html_options={}, &block) 8 | super(builder, template, name, options, &block) 9 | 10 | @html_options = default_html_options.merge(html_options) 11 | end 12 | 13 | def field 14 | tag.div(class: "date-select") do 15 | builder.raw_date_select(name, options, html_options, &block) 16 | end 17 | end 18 | 19 | def default_html_options 20 | Trestle::Options.new(class: ["form-select"], disabled: readonly?, data: { controller: "select" }) 21 | end 22 | end 23 | end 24 | end 25 | end 26 | 27 | Trestle::Form::Builder.register(:date_select, Trestle::Form::Fields::DateSelect) 28 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/datetime_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::DatetimeField < Trestle::Form::Fields::FormControl 2 | include Trestle::Form::Fields::DatePicker 3 | 4 | def field 5 | builder.raw_datetime_field(name, options) 6 | end 7 | 8 | def controller 9 | "datetimepicker" 10 | end 11 | end 12 | 13 | Trestle::Form::Builder.register(:datetime_field, Trestle::Form::Fields::DatetimeField) 14 | Trestle::Form::Builder.register(:datetime_local_field, Trestle::Form::Fields::DatetimeField) 15 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/datetime_select.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Form 3 | module Fields 4 | class DatetimeSelect < Field 5 | attr_reader :html_options 6 | 7 | def initialize(builder, template, name, options={}, html_options={}, &block) 8 | super(builder, template, name, options, &block) 9 | 10 | @html_options = default_html_options.merge(html_options) 11 | end 12 | 13 | def field 14 | tag.div(class: "datetime-select") do 15 | builder.raw_datetime_select(name, options, html_options, &block) 16 | end 17 | end 18 | 19 | def default_html_options 20 | Trestle::Options.new(class: ["form-select"], disabled: readonly?, data: { controller: "select" }) 21 | end 22 | end 23 | end 24 | end 25 | end 26 | 27 | Trestle::Form::Builder.register(:datetime_select, Trestle::Form::Fields::DatetimeSelect) 28 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/email_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::EmailField < Trestle::Form::Fields::FormControl 2 | def field 3 | builder.raw_email_field(name, options) 4 | end 5 | end 6 | 7 | Trestle::Form::Builder.register(:email_field, Trestle::Form::Fields::EmailField) 8 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/file_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::FileField < Trestle::Form::Fields::FormControl 2 | def field 3 | builder.raw_file_field(name, options) 4 | end 5 | end 6 | 7 | Trestle::Form::Builder.register(:file_field, Trestle::Form::Fields::FileField) 8 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/month_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::MonthField < Trestle::Form::Fields::FormControl 2 | def field 3 | builder.raw_month_field(name, options) 4 | end 5 | end 6 | 7 | Trestle::Form::Builder.register(:month_field, Trestle::Form::Fields::MonthField) 8 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/number_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::NumberField < Trestle::Form::Fields::FormControl 2 | def field 3 | builder.raw_number_field(name, options) 4 | end 5 | end 6 | 7 | Trestle::Form::Builder.register(:number_field, Trestle::Form::Fields::NumberField) 8 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/password_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::PasswordField < Trestle::Form::Fields::FormControl 2 | def field 3 | builder.raw_password_field(name, options) 4 | end 5 | 6 | def defaults 7 | super.merge(autocomplete: "new-password") 8 | end 9 | end 10 | 11 | Trestle::Form::Builder.register(:password_field, Trestle::Form::Fields::PasswordField) 12 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/radio_button_helpers.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Form 3 | module Fields 4 | module RadioButtonHelpers 5 | def inline? 6 | options[:inline] 7 | end 8 | 9 | def default_wrapper_class 10 | [ 11 | "form-check", 12 | ("form-check-inline" if inline?) 13 | ].compact 14 | end 15 | 16 | def input_class 17 | ["form-check-input"] 18 | end 19 | 20 | def label_class 21 | ["form-check-label"] 22 | end 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/range_field.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Form 3 | module Fields 4 | class RangeField < Field 5 | def field 6 | builder.raw_range_field(name, options) 7 | end 8 | 9 | def defaults 10 | super.merge(class: ["form-range"]) 11 | end 12 | end 13 | end 14 | end 15 | end 16 | 17 | Trestle::Form::Builder.register(:range_field, Trestle::Form::Fields::RangeField) 18 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/search_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::SearchField < Trestle::Form::Fields::FormControl 2 | def field 3 | builder.raw_search_field(name, options) 4 | end 5 | end 6 | 7 | Trestle::Form::Builder.register(:search_field, Trestle::Form::Fields::SearchField) 8 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/tag_select.rb: -------------------------------------------------------------------------------- 1 | require_relative "select" 2 | 3 | module Trestle 4 | class Form 5 | module Fields 6 | class TagSelect < Select 7 | def initialize(builder, template, name, options={}, html_options={}) 8 | super(builder, template, name, nil, options, html_options) 9 | end 10 | 11 | def default_html_options 12 | super.merge(multiple: true, class: ["tag-select"], data: { tags: true }) 13 | end 14 | end 15 | end 16 | end 17 | end 18 | 19 | Trestle::Form::Builder.register(:tag_select, Trestle::Form::Fields::TagSelect) 20 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/telephone_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::TelephoneField < Trestle::Form::Fields::FormControl 2 | def field 3 | builder.raw_telephone_field(name, options) 4 | end 5 | end 6 | 7 | Trestle::Form::Builder.register(:telephone_field, Trestle::Form::Fields::TelephoneField) 8 | Trestle::Form::Builder.register(:phone_field, Trestle::Form::Fields::TelephoneField) 9 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/text_area.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::TextArea < Trestle::Form::Fields::FormControl 2 | def defaults 3 | super.merge(rows: 5) 4 | end 5 | 6 | def field 7 | builder.raw_text_area(name, options) 8 | end 9 | end 10 | 11 | Trestle::Form::Builder.register(:text_area, Trestle::Form::Fields::TextArea) 12 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/text_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::TextField < Trestle::Form::Fields::FormControl 2 | def field 3 | builder.raw_text_field(name, options) 4 | end 5 | end 6 | 7 | Trestle::Form::Builder.register(:text_field, Trestle::Form::Fields::TextField) 8 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/time_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::TimeField < Trestle::Form::Fields::FormControl 2 | include Trestle::Form::Fields::DatePicker 3 | 4 | def field 5 | builder.raw_time_field(name, options) 6 | end 7 | 8 | def default_icon 9 | icon("fa fa-clock-o") 10 | end 11 | 12 | def controller 13 | "timepicker" 14 | end 15 | end 16 | 17 | Trestle::Form::Builder.register(:time_field, Trestle::Form::Fields::TimeField) 18 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/time_select.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Form 3 | module Fields 4 | class TimeSelect < Field 5 | attr_reader :html_options 6 | 7 | def initialize(builder, template, name, options={}, html_options={}, &block) 8 | super(builder, template, name, options, &block) 9 | 10 | @html_options = default_html_options.merge(html_options) 11 | end 12 | 13 | def field 14 | tag.div(class: "time-select") do 15 | builder.raw_time_select(name, options, html_options, &block) 16 | end 17 | end 18 | 19 | def default_html_options 20 | Trestle::Options.new(class: ["form-select"], disabled: readonly?, data: { controller: "select" }) 21 | end 22 | end 23 | end 24 | end 25 | end 26 | 27 | Trestle::Form::Builder.register(:time_select, Trestle::Form::Fields::TimeSelect) 28 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/url_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::UrlField < Trestle::Form::Fields::FormControl 2 | def field 3 | builder.raw_url_field(name, options) 4 | end 5 | end 6 | 7 | Trestle::Form::Builder.register(:url_field, Trestle::Form::Fields::UrlField) 8 | -------------------------------------------------------------------------------- /lib/trestle/form/fields/week_field.rb: -------------------------------------------------------------------------------- 1 | class Trestle::Form::Fields::WeekField < Trestle::Form::Fields::FormControl 2 | def field 3 | builder.raw_week_field(name, options) 4 | end 5 | end 6 | 7 | Trestle::Form::Builder.register(:week_field, Trestle::Form::Fields::WeekField) 8 | -------------------------------------------------------------------------------- /lib/trestle/hook.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Hook 3 | require_relative "hook/helpers" 4 | require_relative "hook/set" 5 | 6 | attr_reader :name, :options, :block 7 | 8 | def initialize(name, options={}, &block) 9 | @name, @options, @block = name, options, block 10 | end 11 | 12 | def ==(other) 13 | other.is_a?(self.class) && name == other.name && options == other.options && block == other.block 14 | end 15 | 16 | def visible?(context) 17 | if options[:if] 18 | context.instance_exec(&options[:if]) 19 | elsif options[:unless] 20 | !context.instance_exec(&options[:unless]) 21 | else 22 | true 23 | end 24 | end 25 | 26 | def evaluate(context, *args) 27 | context.instance_exec(*args, &block) 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/trestle/hook/set.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Hook 3 | class Set 4 | attr_reader :hooks 5 | 6 | def initialize 7 | @hooks = {} 8 | end 9 | 10 | def append(name, options={}, &block) 11 | hooks[name.to_s] ||= [] 12 | hooks[name.to_s] << Hook.new(name.to_s, options, &block) 13 | end 14 | 15 | def any?(name) 16 | hooks.key?(name.to_s) && hooks[name.to_s].any? 17 | end 18 | 19 | def for(name) 20 | hooks.fetch(name.to_s) { [] } 21 | end 22 | 23 | def empty? 24 | hooks.empty? 25 | end 26 | 27 | def ==(other) 28 | other.is_a?(self.class) && hooks == other.hooks 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/trestle/options.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Options < Hash 3 | def self.new(hash={}) 4 | self[hash] 5 | end 6 | 7 | def merge(other, &block) 8 | dup.merge!(other, &block) 9 | end 10 | 11 | def merge!(other, &block) 12 | super(other || {}) do |key, v1, v2| 13 | if v1.is_a?(Hash) && v2.is_a?(Hash) 14 | v1.merge(v2, &block) 15 | elsif v1.is_a?(Array) 16 | v1 + Array(v2) 17 | else 18 | v2 19 | end 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/trestle/scopes/definition.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Scopes 3 | class Definition 4 | attr_reader :blocks, :options 5 | 6 | def initialize 7 | @blocks = [] 8 | @options = {} 9 | end 10 | 11 | def append(**defaults, &block) 12 | @blocks << Block.new(**defaults, &block) 13 | end 14 | 15 | def apply_options!(options) 16 | @options.merge!(options) 17 | end 18 | 19 | # Evaluates each of the scope blocks within the given admin context 20 | # and returns a hash of Scope objects keyed by the scope name. 21 | def evaluate(context) 22 | @blocks.map { |block| block.scopes(context) }.flatten.index_by(&:name) 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/trestle/sprockets_compressor.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class SprocketsCompressor 3 | def initialize(original_compressor, excluded_files: ["trestle/admin"]) 4 | @original_compressor = original_compressor 5 | @excluded_files = excluded_files 6 | end 7 | 8 | def call(input) 9 | if @excluded_files.include?(input[:name]) 10 | input[:data] 11 | else 12 | @original_compressor.call(input) 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/trestle/tab.rb: -------------------------------------------------------------------------------- 1 | require "action_view/helpers" 2 | 3 | module Trestle 4 | class Tab 5 | include ActionView::Helpers::TagHelper 6 | 7 | attr_reader :name, :options 8 | 9 | def initialize(name, **options) 10 | @name, @options = name, options 11 | end 12 | 13 | def id(tag=nil) 14 | ["tab", tag, name].compact.join("-") 15 | end 16 | 17 | def label 18 | safe_join([options[:label] || I18n.t("admin.tabs.#{name}", default: name.to_s.titleize), badge].compact, " ") 19 | end 20 | 21 | def badge 22 | tag.span(options[:badge], class: "badge") if options[:badge] 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/trestle/table/builder.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | class Table 3 | class Builder < Trestle::Builder 4 | target :table 5 | 6 | def initialize(options={}) 7 | @table = Table.new(options) 8 | @output_buffer = ActionView::OutputBuffer.new 9 | end 10 | 11 | def row(options={}, &block) 12 | table.row = Row.new(options, &block) 13 | end 14 | 15 | def selectable_column(options={}) 16 | table.columns << SelectColumn.new(options) 17 | end 18 | 19 | def column(field, proc=nil, options={}, &block) 20 | if proc.is_a?(Hash) 21 | options = proc 22 | proc = nil 23 | end 24 | 25 | table.columns << Column.new(field, options, &(proc || block)) 26 | end 27 | 28 | def actions(options={}, &block) 29 | table.columns << ActionsColumn.new(options, &block) 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/trestle/version.rb: -------------------------------------------------------------------------------- 1 | module Trestle 2 | VERSION = "0.10.1" 3 | end 4 | -------------------------------------------------------------------------------- /sandbox/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /sandbox/app/admin/categories_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.resource(:categories) do 2 | menu do 3 | item :categories, icon: "fa fa-tags" 4 | end 5 | 6 | collection do 7 | model.alphabetical 8 | end 9 | 10 | table do 11 | selectable_column 12 | column :name, link: true, sort: { default: true } 13 | column :color do |category| 14 | status_tag category.color, :none, style: "background: #{category.color}" 15 | end 16 | actions 17 | end 18 | 19 | form modal: true do 20 | text_field :name 21 | color_field :color 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /sandbox/app/admin/components/alerts_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.admin(:alerts, scope: Components) do 2 | menu do 3 | group :components do 4 | item :alerts, icon: "fas fa-exclamation-circle", priority: 1 5 | end 6 | end 7 | 8 | helper do 9 | def alert_message(html_class) 10 | <<-HTML.html_safe 11 | This custom alert has the #{html_class} class. This is the Link style. 12 | HTML 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /sandbox/app/admin/components/buttons_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.admin(:buttons, scope: Components) do 2 | menu do 3 | group :components do 4 | item :buttons, icon: "fas fa-caret-square-right", priority: 2 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sandbox/app/admin/components/forms_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.admin(:forms, scope: Components) do 2 | menu do 3 | group :components do 4 | item :forms, icon: "fas fa-check-square", priority: 3 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sandbox/app/admin/components/grid_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.admin(:grid, scope: Components) do 2 | menu do 3 | group :components do 4 | item :grid, icon: "fas fa-grip-horizontal", priority: 4 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sandbox/app/admin/components/icons_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.admin(:icons, scope: Components) do 2 | menu do 3 | group :components do 4 | item :icons, icon: "fas fa-icons", priority: 6 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sandbox/app/admin/components/media_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.admin(:media, scope: Components) do 2 | menu do 3 | group :components do 4 | item :media, icon: "fas fa-images", priority: 5 5 | end 6 | end 7 | 8 | # helper ImageHelper 9 | end 10 | -------------------------------------------------------------------------------- /sandbox/app/admin/components/miscellaneous_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.admin(:miscellaneous, scope: Components) do 2 | menu do 3 | group :components do 4 | item :miscellaneous, icon: "fas fa-box-open", priority: 7 5 | end 6 | end 7 | 8 | controller do 9 | def modal 10 | render turbo_stream: turbo_stream.modal 11 | end 12 | 13 | def modal_post 14 | render turbo_stream: turbo_stream.modal 15 | end 16 | end 17 | 18 | routes do 19 | get "modal" 20 | post "modal_post" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /sandbox/app/admin/components/theme_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.admin(:theme, scope: Components) do 2 | menu do 3 | group :components do 4 | item :theme, icon: "fas fa-palette", priority: 8 5 | end 6 | end 7 | 8 | helper do 9 | def alert_message(html_class) 10 | <<-HTML.html_safe 11 | This custom alert has the #{html_class} class. This is the Link style. 12 | HTML 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /sandbox/app/admin/components/typography_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.admin(:typography, scope: Components) do 2 | menu do 3 | group :components do 4 | item :typography, icon: "fas fa-font", priority: 0 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /sandbox/app/admin/offices_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.resource(:offices) do 2 | menu do 3 | item :offices, icon: "fas fa-building" 4 | end 5 | 6 | table do 7 | column :city, link: true 8 | column :country 9 | column :phone 10 | column :url, sort: :url do |office| 11 | link_to office.url, office.url, target: "_blank" 12 | end 13 | actions 14 | end 15 | 16 | form modal: true do 17 | row do 18 | col { text_field :city } 19 | col { text_field :country } 20 | end 21 | 22 | text_field :address_1, label: "Address" 23 | text_field :address_2, label: false 24 | 25 | divider 26 | 27 | url_field :url, prepend: icon("fas fa-link") 28 | phone_field :phone, prepend: icon("fas fa-phone-alt") 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /sandbox/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /sandbox/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/sandbox/app/assets/images/.keep -------------------------------------------------------------------------------- /sandbox/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require_tree . 14 | -------------------------------------------------------------------------------- /sandbox/app/assets/javascripts/trestle/custom/controllers/modal_demo/modal_controller.js: -------------------------------------------------------------------------------- 1 | var ModalDemo = ModalDemo || {} 2 | ModalDemo.ModalController = class extends Trestle.ApplicationController { 3 | static targets = ['message'] 4 | 5 | submit () { 6 | const message = this.messageTarget.value 7 | 8 | if (message.length) { 9 | this.modalController.submit({ message: message }) 10 | } else { 11 | alert('Message required.') 12 | } 13 | } 14 | 15 | setMessage (e) { 16 | this.messageTarget.value = e.detail.message 17 | } 18 | 19 | get modalController () { 20 | return this.application.getControllerForElementAndIdentifier(this.element, 'modal') 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sandbox/app/assets/javascripts/trestle/custom/controllers/modal_demo/trigger_controller.js: -------------------------------------------------------------------------------- 1 | var ModalDemo = ModalDemo || {} 2 | ModalDemo.TriggerController = class extends Trestle.ApplicationController { 3 | static targets = ['status'] 4 | 5 | connect () { 6 | this.setStatus('ModalDemo.Trigger controller initialized.') 7 | } 8 | 9 | setStatus (message) { 10 | this.statusTarget.innerText = message 11 | } 12 | 13 | setLoading () { 14 | this.setStatus('Loading modal...') 15 | } 16 | 17 | setLoaded () { 18 | this.setStatus('Modal successfully loaded.') 19 | } 20 | 21 | updateStatus (e) { 22 | this.setStatus(`Modal submitted with: ${e.detail.message}`) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sandbox/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 file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's 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/SCSS 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 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /sandbox/app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /sandbox/app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /sandbox/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | end 4 | -------------------------------------------------------------------------------- /sandbox/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/sandbox/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /sandbox/app/fields/custom_field.rb: -------------------------------------------------------------------------------- 1 | class CustomField < Trestle::Form::Field 2 | def initialize(builder, template, name, options={}, &block) 3 | super(builder, template, name, options, &block) 4 | end 5 | 6 | def field 7 | tag.div("Custom Field", class: "p-2 text-white bg-info font-weight-bold") 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /sandbox/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /sandbox/app/helpers/image_helper.rb: -------------------------------------------------------------------------------- 1 | module ImageHelper 2 | def random_image(width, height, seed=nil) 3 | image_tag(random_image_url(width, height, seed)) 4 | end 5 | 6 | def random_image_url(width, height, seed=nil) 7 | "https://picsum.photos/seed/#{seed}/#{width}/#{height}?random" 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /sandbox/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /sandbox/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /sandbox/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /sandbox/app/models/article.rb: -------------------------------------------------------------------------------- 1 | class Article < ApplicationRecord 2 | belongs_to :author, class_name: "User" 3 | 4 | has_and_belongs_to_many :categories 5 | 6 | scope :active, -> { where(active: true) } 7 | 8 | serialize :tags, type: Array, coder: YAML 9 | 10 | validates :title, presence: true 11 | 12 | def tags=(tags) 13 | super(tags.reject(&:blank?)) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /sandbox/app/models/category.rb: -------------------------------------------------------------------------------- 1 | class Category < ApplicationRecord 2 | validates :name, presence: true 3 | 4 | scope :alphabetical, -> { order(:name) } 5 | 6 | def to_s 7 | name 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /sandbox/app/models/color.rb: -------------------------------------------------------------------------------- 1 | ColorCategory = Struct.new(:name, :colors) 2 | 3 | Color = Struct.new(:name, :code) do 4 | def name_with_code 5 | "#{name} (#{code})" 6 | end 7 | 8 | def self.all 9 | [ 10 | new("Red", "#f00"), 11 | new("Green", "#0f0"), 12 | new("Blue", "#00f") 13 | ] 14 | end 15 | 16 | def self.categorized 17 | [ 18 | ColorCategory.new("Primary", [ 19 | new("Red", "#f00"), 20 | new("Green", "#0f0"), 21 | new("Blue", "#00f") 22 | ]), 23 | 24 | ColorCategory.new("Secondary", [ 25 | new("Yellow", "#ff0"), 26 | new("Magenta", "#f0f"), 27 | new("Cyan", "#0ff") 28 | ]) 29 | ] 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /sandbox/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/sandbox/app/models/concerns/.keep -------------------------------------------------------------------------------- /sandbox/app/models/office.rb: -------------------------------------------------------------------------------- 1 | class Office < ApplicationRecord 2 | has_many :users 3 | 4 | scope :alphabetical, ->(order=:asc) { reorder(city: order, country: order) } 5 | 6 | def display_name 7 | [city, country].join(", ") 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /sandbox/app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | belongs_to :office 3 | 4 | validates :first_name, :last_name, presence: true 5 | 6 | has_secure_password 7 | 8 | scope :alphabetical, ->(order=:asc) { reorder(last_name: order, first_name: order) } 9 | 10 | enum level: [:executive, :manager, :staff, :intern, :contractor] 11 | enum avatar_type: { "Mystery Person" => "mp", "Identicon" => "identicon", "MonsterID" => "monsterid", "Wavatar" => "wavatar", "Retro" => "retro", "RoboHash" => "robohash", "Initials" => "blank" } 12 | 13 | def full_name 14 | [first_name, last_name].join(" ") 15 | end 16 | 17 | def initials 18 | [first_name, last_name].compact.map(&:first).join.upcase 19 | end 20 | 21 | def avatar_type_value 22 | self.class.avatar_types[avatar_type] 23 | end 24 | 25 | def avatar_color 26 | "##{Digest::MD5.hexdigest(email)[0..5]}" if email 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /sandbox/app/views/admin/components/buttons/index.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for(:title, "Components: Buttons") %> 2 | 3 | <%= render layout: "layout", locals: { wrapper: false } do %> 4 |
    5 |
    6 | <%= render "basic" %> 7 | <%= render "outline" %> 8 | <%= render "icon" %> 9 |
    10 | 11 |
    12 | <%= render "sizes" %> 13 | <%= render "groups" %> 14 | <%= render "dropdowns" %> 15 |
    16 |
    17 | <% end %> 18 | -------------------------------------------------------------------------------- /sandbox/app/views/admin/components/grid/index.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for(:title, "Components: Grid") %> 2 | 3 | <%= render layout: "layout", locals: { wrapper: false } do %> 4 | <%= container do |c| %> 5 | <% c.sidebar class: "order-first" do %> 6 |

    Grid system

    7 |

    Bootstrap’s grid system uses a series of containers, rows, and columns to layout and align content. It’s built with flexbox and is fully responsive.

    8 | <% end %> 9 | 10 | <% [12, 6, 4, 3, 2, 1].each do |columns| %> 11 | <%= row do %> 12 | <% columns.times do %> 13 | <%= col(lg: (12 / columns)) do %> 14 |
    15 | <%= pluralize(columns, "column") %> 16 | <%= ".col-lg-#{(12 / columns)}" %> 17 |
    18 | <% end %> 19 | <% end %> 20 | <% end %> 21 | <% end %> 22 | <% end %> 23 | <% end %> 24 | -------------------------------------------------------------------------------- /sandbox/app/views/admin/components/miscellaneous/_modals.html.erb: -------------------------------------------------------------------------------- 1 | <%= container do |c| %> 2 | <% c.sidebar class: "order-first" do %> 3 |

    Modals

    4 |

    WIP

    5 | <% end %> 6 | 7 |
    8 | <%= link_to "Launch Modal (GET)", { action: :modal }, class: "btn btn-primary", data: { controller: "modal-trigger", action: "modal-trigger:loading->modal-demo--trigger#setLoading modal-trigger:loaded->modal-demo--trigger#setLoaded modal-trigger:submit->modal-demo--trigger#updateStatus" } %> 9 | <%= button_to "Launch Modal (POST)", { action: :modal_post }, class: "btn btn-warning", data: { controller: "modal-trigger" }, form: { style: "display: inline" } %> 10 | 11 |

    12 |
    13 | <% end %> 14 | -------------------------------------------------------------------------------- /sandbox/app/views/admin/components/miscellaneous/index.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for(:title, "Components: Miscellaneous") %> 2 | 3 | <%= render layout: "layout", locals: { wrapper: false } do %> 4 |
    5 |
    6 | <%= render "badges" %> 7 | <%= render "tags" %> 8 | <%= render "progress" %> 9 | <%= render "modals" %> 10 |
    11 | 12 |
    13 | <%= render "timestamps" %> 14 | <%= render "tooltips" %> 15 | <%= render "avatars" %> 16 |
    17 | 18 |
    19 | <%= render "cards" %> 20 |
    21 | 22 |
    23 | <%= render "tabs_example" %> 24 |
    25 |
    26 | <% end %> 27 | -------------------------------------------------------------------------------- /sandbox/app/views/admin/components/miscellaneous/modal.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for(:title, "Modal Demo") %> 2 | 3 | <% modal_options!(class: "modal-sm", controller: "modal-demo--modal") %> 4 | 5 | <% toolbar(:primary) do |t| %> 6 | <%= t.button "Update", style: :success, data: { action: "modal-demo--modal#submit" } %> 7 | <% end %> 8 | 9 | <% toolbar(:secondary) do |t| %> 10 | <%= t.link "Launch Nested Modal", action: :modal, style: :primary, data: { controller: "modal-trigger", action: "modal-trigger:submit->modal-demo--modal#setMessage" } %> 11 | <% end %> 12 | 13 | <%= render layout: "modal" do %> 14 |
    15 | <%= label_tag :message, "Message", class: "form-label" %> 16 | <%= text_field_tag :message, "", class: "form-control", data: { modal_demo__modal_target: "message" } %> 17 |
    18 | <% end %> 19 | -------------------------------------------------------------------------------- /sandbox/app/views/admin/components/miscellaneous/modal_post.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for(:title, "Modal POST") %> 2 | 3 | <% toolbar(:primary) do |t| %> 4 | <%= t.button "Dismiss", style: :success, data: { action: "modal#hide" } %> 5 | <% end %> 6 | 7 | <%= render layout: "modal" do %> 8 |

    Modal POST response.

    9 | <% end %> 10 | -------------------------------------------------------------------------------- /sandbox/app/views/admin/components/theme/_modal.html.erb: -------------------------------------------------------------------------------- 1 | <%= container do %> 2 |
    3 |

    Modal

    4 |
    5 | 6 | <%= admin_link_to "Launch Modal", admin: :"components/miscellaneous", action: :modal, class: "btn btn-primary", data: { controller: "modal-trigger" } %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /sandbox/app/views/admin/components/theme/_table.html.erb: -------------------------------------------------------------------------------- 1 | <%= container do %> 2 |
    3 |

    Table Rows

    4 |
    5 | 6 |
    7 | 8 | 9 | 10 | 11 | 12 | 13 | <% 2.times do %> 14 | 15 | <% end %> 16 | 17 | <% 2.times do %> 18 | 19 | <% end %> 20 | 21 | <% 3.times do %> 22 | 23 | <% end %> 24 | 25 |
    Row Style
    Primary Row
    Secondary Row
    Regular Row
    26 |
    27 | <% end %> 28 | -------------------------------------------------------------------------------- /sandbox/app/views/admin/components/theme/_typography.html.erb: -------------------------------------------------------------------------------- 1 | <%= container do %> 2 |
    3 |

    Typography

    4 |
    5 | 6 |

    This line of text contains the text-primary class.

    7 |

    This line of text contains the text-primary-emphasis class.

    8 | 9 |

    This line of text contains the text-secondary class.

    10 |

    This line of text contains the text-secondary-emphasis class.

    11 | 12 |

    This line of text is formatted as a link.

    13 | 14 | <% end %> 15 | -------------------------------------------------------------------------------- /sandbox/app/views/admin/components/typography/_headings.html.erb: -------------------------------------------------------------------------------- 1 | <%= container do |c| %> 2 | <% c.sidebar class: "order-first" do %> 3 |

    Headings

    4 |

    All HTML headings, <h1> through <h6>, are available.

    5 |

    .h1 through .h6 classes are also available, for when you want to match the font styling of a heading but cannot use the associated HTML element.

    6 | <% end %> 7 | 8 |

    h1. Bootstrap heading

    9 |

    h2. Bootstrap heading

    10 |

    h3. Bootstrap heading

    11 |

    h4. Bootstrap heading

    12 |
    h5. Bootstrap heading
    13 |
    h6. Bootstrap heading
    14 | <% end %> 15 | -------------------------------------------------------------------------------- /sandbox/app/views/admin/components/typography/_inline.html.erb: -------------------------------------------------------------------------------- 1 | <%= container do |c| %> 2 | <% c.sidebar class: "order-first" do %> 3 |

    Inline text elements

    4 |

    Styling for common inline HTML5 elements.

    5 | <% end %> 6 | 7 |

    You can use the mark tag to highlight text.

    8 |

    This line of text is meant to be treated as deleted text.

    9 |

    This line of text is meant to be treated as no longer accurate.

    10 |

    This line of text is meant to be treated as an addition to the document.

    11 |

    This line of text will render as underlined

    12 |

    This line of text is meant to be treated as fine print.

    13 |

    This line rendered as bold text.

    14 |

    This line rendered as italicized text.

    15 | <% end %> 16 | -------------------------------------------------------------------------------- /sandbox/app/views/admin/components/typography/index.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for(:title, "Components: Typography") %> 2 | 3 | <%= render layout: "layout", locals: { wrapper: false } do %> 4 |
    5 | <%= render "headings" %> 6 | <%= render "lists" %> 7 | <%= render "inline" %> 8 | <%= render "colors" %> 9 | <%= render "blockquotes" %> 10 | <%= render "code" %> 11 |
    12 | <% end %> 13 | -------------------------------------------------------------------------------- /sandbox/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | <%= csrf_meta_tags %> 6 | 7 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbo-track': 'reload' %> 8 | <%= javascript_include_tag 'application', 'data-turbo-track': 'reload' %> 9 | 10 | 11 | 12 | <%= yield %> 13 | 14 | 15 | -------------------------------------------------------------------------------- /sandbox/app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /sandbox/app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /sandbox/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /sandbox/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 | -------------------------------------------------------------------------------- /sandbox/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /sandbox/bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /sandbox/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 | -------------------------------------------------------------------------------- /sandbox/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | Bundler.require(*Rails.groups) 6 | require "trestle" 7 | 8 | module Sandbox 9 | class Application < Rails::Application 10 | # Initialize configuration defaults for current Rails version. 11 | config.load_defaults Rails::VERSION::STRING.to_f 12 | 13 | # Settings in config/environments/* take precedence over those specified here. 14 | # Application configuration should go into files in config/initializers 15 | # -- all .rb files in that directory are automatically loaded. 16 | 17 | config.action_view.form_with_generates_ids = true 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /sandbox/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __dir__) 3 | 4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 5 | $LOAD_PATH.unshift File.expand_path('../../lib', __dir__) 6 | -------------------------------------------------------------------------------- /sandbox/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | -------------------------------------------------------------------------------- /sandbox/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 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: 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 | -------------------------------------------------------------------------------- /sandbox/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /sandbox/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /sandbox/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 app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /sandbox/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /sandbox/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /sandbox/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /sandbox/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 | -------------------------------------------------------------------------------- /sandbox/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /sandbox/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_trestle_sandbox_session' 4 | -------------------------------------------------------------------------------- /sandbox/config/initializers/trestle.rb: -------------------------------------------------------------------------------- 1 | Trestle.configure do |config| 2 | config.site_title = "Trestle Sandbox" 3 | 4 | config.site_logo = "logo.svg" 5 | config.site_logo_small = "logo-small.svg" 6 | 7 | config.theme = { primary: "#337ab7", secondary: "#719dc3" } 8 | 9 | config.menu do 10 | group "Badges & Labels", priority: :last do 11 | item "Item with counter", "#", badge: { text: 99, class: "badge-primary badge-pill" }, priority: :first 12 | item "Item with badge", "#", badge: { text: "NEW!", class: "badge-success" }, icon: "fa fa-car" 13 | item "Item with long label that spans multiple lines", "#", badge: "!" 14 | end 15 | end 16 | 17 | # config.helper -> { ImageHelper } 18 | config.helper "ImageHelper" 19 | 20 | # config.form_field :custom_field, -> { CustomField } 21 | config.form_field :custom_field, "CustomField" 22 | end 23 | -------------------------------------------------------------------------------- /sandbox/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /sandbox/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 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | activerecord: 24 | attributes: 25 | office: 26 | url: URL 27 | -------------------------------------------------------------------------------- /sandbox/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root to: redirect('/admin') 3 | end 4 | -------------------------------------------------------------------------------- /sandbox/config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /sandbox/db/migrate/20210712064032_create_offices.rb: -------------------------------------------------------------------------------- 1 | class CreateOffices < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :offices do |t| 4 | t.string :city 5 | t.string :country 6 | 7 | t.string :address_1 8 | t.string :address_2 9 | 10 | t.string :url 11 | t.string :phone 12 | 13 | t.timestamps 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /sandbox/db/migrate/20210712071021_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :users do |t| 4 | t.string :email 5 | t.string :password_digest 6 | 7 | t.string :first_name 8 | t.string :last_name 9 | 10 | t.date :date_of_birth 11 | 12 | t.string :avatar_type 13 | t.string :time_zone 14 | 15 | t.references :office 16 | t.integer :level 17 | 18 | t.timestamps 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /sandbox/db/migrate/20210712104832_create_categories.rb: -------------------------------------------------------------------------------- 1 | class CreateCategories < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :categories do |t| 4 | t.string :name 5 | t.string :color 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /sandbox/db/migrate/20210713021857_create_articles.rb: -------------------------------------------------------------------------------- 1 | class CreateArticles < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :articles do |t| 4 | t.string :title 5 | t.text :content 6 | 7 | t.datetime :published_at 8 | t.boolean :active, default: false 9 | t.text :tags 10 | 11 | t.references :author 12 | 13 | t.timestamps 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /sandbox/db/migrate/20210714014917_create_articles_categories.rb: -------------------------------------------------------------------------------- 1 | class CreateArticlesCategories < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :articles_categories, id: false do |t| 4 | t.references :article 5 | t.references :category 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /sandbox/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/sandbox/lib/assets/.keep -------------------------------------------------------------------------------- /sandbox/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/sandbox/log/.keep -------------------------------------------------------------------------------- /sandbox/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/sandbox/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /sandbox/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/sandbox/public/apple-touch-icon.png -------------------------------------------------------------------------------- /sandbox/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/sandbox/public/favicon.ico -------------------------------------------------------------------------------- /spec/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /spec/dummy/app/admin/modal_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.resource(:modal, model: Post) do 2 | menu do 3 | item :modal, icon: "fa fa-star" 4 | end 5 | 6 | table do 7 | column :title, link: true 8 | actions 9 | end 10 | 11 | form modal: { class: "modal-lg" } do |post| 12 | text_field :title 13 | text_area :body 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/dummy/app/admin/posts_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.resource(:posts) do 2 | menu do 3 | item :posts, icon: "fa fa-star" 4 | end 5 | 6 | table do 7 | column :title, link: true 8 | actions 9 | end 10 | 11 | form do |post| 12 | text_field :title 13 | text_area :body 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/dummy/app/admin/singular_post_admin.rb: -------------------------------------------------------------------------------- 1 | Trestle.resource(:singular_post, model: Post, singular: true) do 2 | menu do 3 | item :singular, icon: "fas fa-star" 4 | end 5 | 6 | remove_action :new, :create, :edit, :destroy 7 | 8 | instance do 9 | model.first 10 | end 11 | 12 | form do |post| 13 | text_field :title 14 | text_area :body 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/spec/dummy/app/assets/images/.keep -------------------------------------------------------------------------------- /spec/dummy/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require_tree . 14 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/javascripts/trestle/custom.js: -------------------------------------------------------------------------------- 1 | // This file may be used for providing additional customizations to the Trestle 2 | // admin. It will be automatically included within all admin pages. 3 | // 4 | // For organizational purposes, you may wish to define your customizations 5 | // within individual partials and `require` them here. 6 | // 7 | // e.g. //= require "trestle/custom/my_custom_js" 8 | -------------------------------------------------------------------------------- /spec/dummy/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 file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's 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/SCSS 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 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/stylesheets/trestle/_custom.css: -------------------------------------------------------------------------------- 1 | /* This file may be used for providing additional customizations to the Trestle 2 | * admin. It will be automatically included within all admin pages. 3 | * 4 | * For organizational purposes, you may wish to define your customizations 5 | * within individual partials in this folder and they'll be required below. 6 | * 7 | *= require_tree . 8 | */ 9 | -------------------------------------------------------------------------------- /spec/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | end 4 | -------------------------------------------------------------------------------- /spec/dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/spec/dummy/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /spec/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /spec/dummy/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /spec/dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/spec/dummy/app/models/concerns/.keep -------------------------------------------------------------------------------- /spec/dummy/app/models/post.rb: -------------------------------------------------------------------------------- 1 | class Post < ApplicationRecord 2 | scope :published, -> { where(published: true) } 3 | end 4 | -------------------------------------------------------------------------------- /spec/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | <%= csrf_meta_tags %> 6 | 7 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbo-track': 'reload' %> 8 | <%= javascript_include_tag 'application', 'data-turbo-track': 'reload' %> 9 | 10 | 11 | 12 | <%= yield %> 13 | 14 | 15 | -------------------------------------------------------------------------------- /spec/dummy/app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /spec/dummy/app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /spec/dummy/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /spec/dummy/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 | -------------------------------------------------------------------------------- /spec/dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /spec/dummy/bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /spec/dummy/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 | -------------------------------------------------------------------------------- /spec/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | # Pick the frameworks you want: 4 | require "active_record/railtie" 5 | require "action_controller/railtie" 6 | require "action_view/railtie" 7 | require "active_job/railtie" 8 | # require "action_mailer/railtie" 9 | # require "action_cable/engine" 10 | # require "rails/test_unit/railtie" 11 | 12 | Bundler.require(*Rails.groups) 13 | 14 | module Dummy 15 | class Application < Rails::Application 16 | # Initialize configuration defaults for current Rails version. 17 | config.load_defaults Rails::VERSION::STRING.to_f 18 | 19 | # Settings in config/environments/* take precedence over those specified here. 20 | # Application configuration should go into files in config/initializers 21 | # -- all .rb files in that directory are automatically loaded. 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__) 3 | 4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 5 | $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__) 6 | -------------------------------------------------------------------------------- /spec/dummy/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | -------------------------------------------------------------------------------- /spec/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 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 | -------------------------------------------------------------------------------- /spec/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /spec/dummy/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 app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /spec/dummy/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 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_dummy_session' 4 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /spec/dummy/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 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /spec/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | end 3 | -------------------------------------------------------------------------------- /spec/dummy/config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /spec/dummy/db/migrate/20170915062615_create_posts.rb: -------------------------------------------------------------------------------- 1 | class CreatePosts < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :posts do |t| 4 | t.string :title 5 | t.text :body 6 | t.boolean :published 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/dummy/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/spec/dummy/lib/assets/.keep -------------------------------------------------------------------------------- /spec/dummy/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/spec/dummy/log/.keep -------------------------------------------------------------------------------- /spec/dummy/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/spec/dummy/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /spec/dummy/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/spec/dummy/public/apple-touch-icon.png -------------------------------------------------------------------------------- /spec/dummy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/spec/dummy/public/favicon.ico -------------------------------------------------------------------------------- /spec/dummy/tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrestleAdmin/trestle/020161aec0c6923f218cbf7552cc677f77204a43/spec/dummy/tmp/.keep -------------------------------------------------------------------------------- /spec/feature/shakedown_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | feature 'Shakedown', type: :system do 4 | scenario 'Admin index' do 5 | visit '/admin' 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/feature/singular_resource_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | feature 'Singular resources', type: :system do 4 | scenario 'show' do 5 | create_test_post 6 | 7 | visit '/admin/singular_post' 8 | 9 | expect(page).to have_form("/admin/singular_post", "post") { 10 | with_text_field "singular_post[title]", "Single Post" 11 | with_text_area "singular_post[body]" 12 | } 13 | end 14 | 15 | scenario 'update record' do 16 | create_test_post 17 | 18 | visit '/admin/singular_post' 19 | 20 | fill_in "Title", with: "Updated Title" 21 | click_button "Save Post" 22 | 23 | expect(page).to have_content("The post was successfully updated.") 24 | expect(page).to have_current_path(/\/admin\/singular_post/) 25 | end 26 | 27 | def create_test_post 28 | Post.create!(id: 1, title: "Single Post", body: "This is a singular post", published: true) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/generators/admin_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'generators/trestle/admin/admin_generator' 4 | 5 | describe Trestle::Generators::AdminGenerator, type: :generator do 6 | destination File.expand_path("../../../tmp", __FILE__) 7 | 8 | before do 9 | prepare_destination 10 | end 11 | 12 | describe "the generated files" do 13 | before do 14 | run_generator %w(foobar) 15 | end 16 | 17 | describe "the admin resource" do 18 | subject { file("app/admin/foobar_admin.rb") } 19 | 20 | it { is_expected.to exist } 21 | it { is_expected.to contain "Trestle.admin(:foobar) do" } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/integration/resource_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Trestle::ResourceController, type: :request do 4 | include FeatureHelper 5 | 6 | let(:resource) { PostsAdmin } 7 | 8 | describe "delete record without referer" do 9 | let(:post) { create_test_post } 10 | 11 | it "does not raise an exception" do 12 | delete resource.instance_path(post) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/support/capybara.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.before(:each, type: :system) do 3 | driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/support/contexts/countries.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_context "countries" do 2 | before(:all) do 3 | Country = Struct.new(:code, :name) do 4 | alias id code 5 | end 6 | 7 | Region = Struct.new(:name, :countries) 8 | end 9 | 10 | after(:all) do 11 | Object.send(:remove_const, :Country) 12 | Object.send(:remove_const, :Region) 13 | end 14 | 15 | let(:countries) do 16 | [ 17 | Country.new("AUS", "Australia"), 18 | Country.new("USA", "United States"), 19 | Country.new("NZ", "New Zealand") 20 | ] 21 | end 22 | 23 | let(:regions) do 24 | [ 25 | Region.new("America", [Country.new("USA", "United States")]), 26 | Region.new("Oceania", [Country.new("AUS", "Australia"), Country.new("NZ", "New Zealand")]), 27 | ] 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/support/contexts/form.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_context "form" do |field, value| 2 | include_context "template" do 3 | let(:admin) { double(readonly?: false) } 4 | end 5 | 6 | let(:builder) { Trestle::Form::Builder.new(object_name, object, template, options) } 7 | 8 | let(:object_name) { :article } 9 | let(:object) { double(errors: ActiveModel::Errors.new([])) } 10 | 11 | let(:options) { {} } 12 | 13 | before(:each) do 14 | allow(object).to receive_messages(field => value) 15 | end if field 16 | end 17 | -------------------------------------------------------------------------------- /spec/support/contexts/template.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_context "template" do 2 | let(:admin) { nil } 3 | let(:template) { ActionView::Base.respond_to?(:empty) ? ActionView::Base.empty : ActionView::Base.new } 4 | 5 | before(:each) do 6 | template.extend(Trestle::IconHelper) 7 | template.extend(Trestle::UrlHelper) 8 | 9 | allow(template).to receive(:admin).and_return(admin) 10 | allow(template).to receive(:modal_request?).and_return(false) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/support/feature_helper.rb: -------------------------------------------------------------------------------- 1 | module FeatureHelper 2 | def create_test_post 3 | Post.create!(id: 1, title: "First Post", body: "This is a test post", published: true) 4 | end 5 | 6 | def within_modal(&block) 7 | within('.modal', &block) 8 | end 9 | 10 | def within_popover(&block) 11 | within('.popover', &block) 12 | end 13 | 14 | def confirm_delete 15 | within_popover { click_button "Delete" } 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/support/i18n_helper.rb: -------------------------------------------------------------------------------- 1 | module I18nHelper 2 | def with_translations(locale, translations) 3 | I18n.backend.store_translations(locale, translations) 4 | yield 5 | ensure 6 | I18n.reload! 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/support/matchers/have_accessor.rb: -------------------------------------------------------------------------------- 1 | RSpec::Matchers.define :have_accessor do |expected| 2 | match do |actual| 3 | return false unless actual.respond_to?(expected) && actual.respond_to?("#{expected}=") 4 | return false if @expected_default && actual.send(expected) != @expected_default 5 | 6 | true 7 | end 8 | 9 | chain :with_default do |value| 10 | @expected_default = value 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/color_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "form_control_examples" 4 | 5 | describe Trestle::Form::Fields::ColorField, type: :helper do 6 | it_behaves_like "a form control", :color, "#ff0000" do 7 | subject { builder.color_field(:color, options) } 8 | 9 | it "renders as a color field" do 10 | expect(subject).to have_tag("input.form-control", with: { type: "color", value: "#ff0000" }) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/date_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "date_picker_examples" 4 | 5 | describe Trestle::Form::Fields::DateField, type: :helper do 6 | it_behaves_like "a date picker control", :date, Date.new(2020, 9, 1) do 7 | subject { builder.date_field(:date, options) } 8 | 9 | let(:js_controller) { "datepicker" } 10 | 11 | it "renders as a date field" do 12 | expect(subject).to have_tag("input.form-control", with: { type: "date", value: "2020-09-01" }) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/datetime_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "date_picker_examples" 4 | 5 | describe Trestle::Form::Fields::DatetimeField, type: :helper do 6 | it_behaves_like "a date picker control", :date, Time.new(2020, 9, 1) do 7 | subject { builder.datetime_field(:date, options) } 8 | 9 | let(:js_controller) { "datetimepicker" } 10 | 11 | it "renders as a datetime-local field" do 12 | expect(subject).to have_tag("input.form-control[value^='2020-09-01T00:00:00'][type^='datetime']") 13 | end 14 | 15 | it "is aliased as #datetime_local_field" do 16 | expect(builder.datetime_local_field(:date, options)).to eq(subject) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/email_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "form_control_examples" 4 | 5 | describe Trestle::Form::Fields::EmailField, type: :helper do 6 | it_behaves_like "a form control", :email, "foo@example.com" do 7 | subject { builder.email_field(:email, options) } 8 | 9 | it "renders as an email field" do 10 | expect(subject).to have_tag("input.form-control", with: { type: "email", value: "foo@example.com" }) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/file_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "form_control_examples" 4 | 5 | describe Trestle::Form::Fields::FileField, type: :helper do 6 | it_behaves_like "a form control", :attachment do 7 | subject { builder.file_field(:attachment, options) } 8 | 9 | it "renders as a file field" do 10 | expect(subject).to have_tag("input.form-control", with: { type: "file" }) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/month_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "form_control_examples" 4 | 5 | describe Trestle::Form::Fields::MonthField, type: :helper do 6 | it_behaves_like "a form control", :date, Date.new(2020, 9, 1) do 7 | subject { builder.month_field(:date, options) } 8 | 9 | it "renders as a month field" do 10 | expect(subject).to have_tag("input.form-control", with: { type: "month", value: "2020-09" }) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/number_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "form_control_examples" 4 | 5 | describe Trestle::Form::Fields::NumberField, type: :helper do 6 | it_behaves_like "a form control", :count, 123 do 7 | subject { builder.number_field(:count, options) } 8 | 9 | it "renders as a number field" do 10 | expect(subject).to have_tag("input.form-control", with: { type: "number", value: "123" }) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/password_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "form_control_examples" 4 | 5 | describe Trestle::Form::Fields::PasswordField, type: :helper do 6 | it_behaves_like "a form control", :password, "secret" do 7 | subject { builder.password_field(:password, options) } 8 | 9 | it "renders as a password field" do 10 | expect(subject).to have_tag("input.form-control", with: { type: "password", autocomplete: "new-password" }, without: { value: "secret" }) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/range_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "form_field_examples" 4 | 5 | describe Trestle::Form::Fields::RangeField, type: :helper do 6 | include_context "form", :level, 10 7 | 8 | it_behaves_like "a form field", :level 9 | 10 | subject { builder.range_field(:level, options) } 11 | 12 | it "renders a custom file field by default" do 13 | expect(subject).to have_tag("input.form-range", with: { type: "range", id: "article_level" }) 14 | end 15 | 16 | context "when options[:custom] is set to false" do 17 | let(:options) { { custom: false } } 18 | 19 | it "renders a regular file field" do 20 | expect(subject).to have_tag("input", with: { type: "range" }, without: { class: "custom-range" }) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/search_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "form_control_examples" 4 | 5 | describe Trestle::Form::Fields::SearchField, type: :helper do 6 | it_behaves_like "a form control", :query, "ruby" do 7 | subject { builder.search_field(:query, options) } 8 | 9 | it "renders as a search field" do 10 | expect(subject).to have_tag("input.form-control", with: { type: "search", value: "ruby" }) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/telephone_field.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "form_control_examples" 4 | 5 | describe Trestle::Form::Fields::TelephoneField, type: :helper do 6 | it_behaves_like "a form control", :phone, "555-1234" do 7 | subject { builder.telephone_field(:phone, options) } 8 | 9 | it "renders as a tel field" do 10 | expect(subject).to have_tag("input.form-control", with: { type: "tel", value: "555-1234" }) 11 | end 12 | 13 | it "is aliased as #phone_field" do 14 | expect(builder.phone_field(:phone, options)).to eq(subject) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/text_area_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "form_control_examples" 4 | 5 | describe Trestle::Form::Fields::TextArea, type: :helper do 6 | it_behaves_like "a form control", :body, "Content" do 7 | subject { builder.text_area(:body, options) } 8 | 9 | it "renders as a textarea" do 10 | expect(subject).to have_tag("textarea.form-control", with: { rows: 5 }, text: /Content/) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/text_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "form_control_examples" 4 | 5 | describe Trestle::Form::Fields::TextField, type: :helper do 6 | it_behaves_like "a form control", :title, "Title" do 7 | subject { builder.text_field(:title, options) } 8 | 9 | it "renders as a text field" do 10 | expect(subject).to have_tag("input.form-control", with: { type: "text", value: "Title" }) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/time_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "date_picker_examples" 4 | 5 | describe Trestle::Form::Fields::TimeField, type: :helper do 6 | it_behaves_like "a date picker control", :date, Time.new(2020, 9, 1) do 7 | subject { builder.time_field(:date, options) } 8 | 9 | let(:icon) { "fa fa-clock-o" } 10 | let(:js_controller) { "timepicker" } 11 | 12 | it "renders as a time field" do 13 | expect(subject).to have_tag("input.form-control", with: { type: "time", value: "00:00:00.000" }) 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/url_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "form_control_examples" 4 | 5 | describe Trestle::Form::Fields::UrlField, type: :helper do 6 | it_behaves_like "a form control", :website, "https://www.trestle.io" do 7 | subject { builder.url_field(:website, options) } 8 | 9 | it "renders as a url field" do 10 | expect(subject).to have_tag("input.form-control", with: { type: "url", value: "https://www.trestle.io" }) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/trestle/form/fields/week_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require_relative "form_control_examples" 4 | 5 | describe Trestle::Form::Fields::WeekField, type: :helper do 6 | it_behaves_like "a form control", :date, Date.new(2020, 9, 1) do 7 | subject { builder.week_field(:date, options) } 8 | 9 | it "renders as a week field" do 10 | expect(subject).to have_tag("input.form-control", with: { type: "week", value: "2020-W36" }) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/trestle/helpers/avatar_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Trestle::AvatarHelper, type: :helper do 4 | describe "#avatar" do 5 | let(:image) { tag.img(src: "avatar.png") } 6 | 7 | it "renders an avatar" do 8 | result = avatar { image } 9 | 10 | expect(result).to have_tag(".avatar") do 11 | with_tag "img", src: "avatar.png" 12 | end 13 | end 14 | 15 | it "renders a fallback if provided" do 16 | result = avatar(fallback: "SP") { image } 17 | 18 | expect(result).to have_tag(".avatar") do 19 | with_tag "span.avatar-fallback", text: "SP" 20 | with_tag "img", src: "avatar.png" 21 | end 22 | end 23 | 24 | it "passes custom option to the div" do 25 | result = avatar(style: "border: 1px solid red") { image } 26 | 27 | expect(result).to have_tag(".avatar", with: { style: "border: 1px solid red" }) 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/trestle/helpers/icon_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Trestle::IconHelper, type: :helper do 4 | describe "#icon" do 5 | it "returns an tag with the given classes" do 6 | result = icon("fas", "fa-star") 7 | expect(result).to have_tag("i.fas.fa-star") 8 | end 9 | 10 | it "applies any additional attributes to the tag (merging classes)" do 11 | result = icon("fas", "fa-star", class: "fa-fw", id: "icon") 12 | expect(result).to have_tag("i#icon.fas.fa-star.fa-fw") 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/trestle/helpers/title_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Trestle::TitleHelper, type: :helper do 4 | let(:action_name) { "default" } 5 | 6 | describe "#title" do 7 | it "returns the title set by content_for" do 8 | content_for(:title, "Custom Title") 9 | expect(title).to eq("Custom Title") 10 | end 11 | 12 | it "returns a default title if not explicitly defined" do 13 | expect(title).to eq("Default") 14 | end 15 | end 16 | end 17 | --------------------------------------------------------------------------------