This gem is for creating UIs with Fiat's Synapse product.
Add this to your application's Gemfile
:
gem 'synapse_ui'
Add this to your application's package.json
file:
"dependencies": {
"synapse_ui": "https://github.com/fiatinsight/synapse_ui"
}
Add this to your application.scss
file:
@import '~bootstrap/scss/bootstrap';
@import '~synapse_ui/vendor/assets/stylesheets/synapse_ui';
Note: Stylesheets depend on Bootstrap and should be loaded after
Simpler decisions
This library is designed to extend the power of Bootstrap, not to infringe on it. Even if you're great a leveraging Bootstrap directly, relying on some additional, component-based conventions can help to dramatically speed up routine decision making throughout your design.
Functionality first
Sheets are divided up (for the most part) into components. All the styles in a component-based sheet pertain especially to that component — although maybe not exclusively. Other sheets are purely functional, for example, animations.scss
. If a style doesn't fit into one of those categories, it's not included.
Consistency in customization
Most apps utilize many of the same UI components. This library allows you to adopt a global convention where it makes sense (e.g., off-the-shelf table styling), but also to easily override conventions when required. Additionally, it offers quite a few Bootstrap-inspired, granular selectors to enable significant, on-the-fly customizations directly within your HTML.
You can load all stylesheets in your app by calling @import "synapse_ui"
. Or you can load individual sheets by calling, for example, @import "synapse_ui/buttons"
.
The following sheets are available for inclusion:
- animations.scss
- buttons.scss
- content.scss
- forms.scss
- layouts.scss
- material.scss
- modals.scss
- nav.scss
- tables.scss
- tweaks.scss
- typography.scss
- variables.scss
You can override style variables by including your own project_name-variables.scss
and project_name-styles.scss
files. Variables should be loaded before the Fiat UI package. (Included variables are set using the !default
flag, which means they'll only take effect if there's not a previous variable with the same name.) Styles should be added after the Fiat UI package. For example:
@import url('https://fonts.googleapis.com/css?family=Montserrat:400,400i,700,700i&display=swap');
@import "bootstrap";
@import "project_name-variables";
@import "synapse_ui";
@import "project_name-styles";
Import included JavaScript into your main application's application.js
file:
import 'synapse_ui/app/javascript/packs/application'
Development note: To test new JavaScript / Stimulus controllers, you'll need to run
yarn upgrade synapse_ui
in your application development environment to make them available.
To include Font Awesome, get the pro version package by following these instructions. Then make sure the package is added to your app's package.json
file:
"dependencies": {
"@fortawesome/fontawesome-pro": "^5.12.1"
}
Then import the JavaScript file into your application.js
file:
import '@fortawesome/fontawesome-pro/js/all.js'
For deploying to Heroku, make sure you have the following in your .npmrc
file (application root):
@fortawesome:registry=https://npm.fontawesome.com/
//npm.fontawesome.com/:_authToken=${FONT_AWESOME_AUTH_TOKEN}
Then set a Heroku environment variable for FONT_AWESOME_AUTH_TOKEN
.
Add a Stimulus controller context for the gem in the app's application.js
file:
const synapse_ui_context = require.context('synapse_ui/app/javascript/packs/controllers', true, /\.js$/)
application.load(definitionsFromContext(synapse_ui_context))
Included controllers will be available using the normal data-controller
convention:
Some integrated components are also included to make it easier to build common utilities and workflows.
An alerts module to handle flash
notifications is available by loading:
= render 'layouts/synapse_ui/components/alerts'
This picks up the value of both flash[:alert]
and flash[:notice]
and renders them in the view.
Alerts are styled using the #alert
and .flash
selectors. By default, they're designed to appear at the top of a page and to persist. However, you can optionally fade them by including something like the following in your application.js
file:
$(document).on('turbolinks:load', function() {
setTimeout(function(){
$("#alert").addClass("fade");
}, 4000);
});
A Google Analytics tracking script is supplied. You can include it in the head
section of your main template file by calling:
= render partial: 'layouts/synapse_ui/components/analytics', locals: { tracking_id: "UA-12345678-9", include_dev: true }
Make sure to replace the tracking_id
variable with yours from Google. Setting the value to nil
will bypass the script loading. You can also choose to include the script in development by setting include_dev
to true
(optional).
An errors module is available to handle information passed to flash[:errors]
. To use it, include the following in your layout template:
= render 'layouts/synapse_ui/components/errors'
To pass information to the partial — e.g., during a form submission fault — do something like this in your controller:
respond_to do |format|
if @item.save
format.html # Success response
else
format.html { redirect_to item_path(field_one: @item.field_one, field_two: @item.field_two), alert: "Missing fields required", flash: { errors: @item.errors.full_messages } }
end
end
In the above example, flash[:errors]
will contain a concatenated string of applicable errors. You can query that string to display custom, embedded error messages in your view. For example:
if flash[:error] && flash[:error].include?("Email can't be blank")
# Email must be included
end
By default, the .flash.errors
class is set to display: hidden
, so you can access it in your view template without showing the raw results.
Also in the above example, flash[:alert]
is reserved for a normal alert message. And form parameters — which are usually when using redirect_to
— are passed along to the path. This enables you to re-populate your nicely marked up error form with any originally included values. For example:
= f.input :field_one, input_html: { value: params[:field_one] }
Meta tags are enabled via the meta-tags
gem dependency. Run rails generate meta_tags:install
to create a config initializer. Then include something like this in your main template file:
= display_meta_tags site: site_name, description: description, image_src: image, twitter: { card: "summary_large_image", site: "@username", creator: "@username" }, og: { title: site_name, type: 'website', image: image, description: description }
You can read the full documentation here.
You can include modals. Invoke them by using:
= render partial: 'layouts/synapse_ui/components/modals/small-modal'
Responsive navigation is available. It includes a navbar partial and a fly-out / modal-driven menu.
To install the navbar, add the following to your template:
navbar_locals = {
classes: 'fixed-top bg-transparent pt-3',
mobile_button_classes: 'px-3 py-2 bg-pink layer-2',
menu_icon: 'fal fa-arrow-left mr-2',
menu_title: 'Go',
display_menu_title_mobile: false,
sections: [
{
title: 'Company',
path: 'root_path',
icon: 'far fa-home mr-2'
},
{
title: 'Services',
path: 'root_path',
items: [
{
title: 'Design',
path: 'root_path'
},
{
title: 'Support',
path: 'root_path'
}
]
}
] # end all sections
}
= render partial: 'layouts/synapse_ui/components/nav/navbar', locals: navbar_locals
You can pass the following variables to locals
(N.B. they all default to false
when not included):
classes
: Classes for your navbar wrapper, e.g.,fixed-top
and/orlayer-0
.image
: An image you want to display in the logo section.title
: A title you want to display in the logo section if an image isn't included.sections
: The content of your menu; this is structured as an item with a title, link path, path variable (optional), icon, and any sub-items, each including a title, link path, variable, icon, and method (optional; defaults toget
).menu_icon
: The Font Awesome icon that'll show on your menu button.menu_title
: A title for your menu button.menu_button_classes
: Classes for your menu button on non-mobile.mobile_menu_icon
: The Font Awesome icon that'll show on your menu button.mobile_menu_title
: A title for your menu button.mobile_menu_button_classes
: Classes for your menu button on mobile.
To include the menu modal, add the following to your template (probably just below your navbar partial):
nav_modal_locals = {
image: 'https://s3.amazonaws.com/bucket-name/image.png',
image_width_percentage: 60,
sections: [
{
title: 'Company',
path: 'root_path',
icon: 'far fa-home mr-2'
},
{
title: 'Services',
path: 'services_path',
items: [
{
title: 'Design',
path: 'design_path'
},
{
title: 'Support',
path: 'support_path'
}
]
}
] # end all sections
}
= render partial: 'layouts/synapse_ui/components/nav/nav-modal', locals: nav_modal_locals
You can pass the following variables to locals
:
image
: An image to display above your menu list.image_width_percentage
: Specify what percent of the modal column the image should occupy (defaults to50
).sections
: The content of your menu; this is structured as a section with a title, link path, path variable (optional), icon, and any sub-items, each including a title, link path, variable, icon, and method (optional; defaults toget
).
Search with ransack can be configured simply using a form partial:
= render partial: 'layouts/synapse_ui/components/search-keyword', locals: { url: 'search_everything_path', placeholder: 'Search', filter_types: [ ['organization', 'Organizations'], ['user', 'Users'] ] }
You can pass the following variables into the locals
:
-
url
: Required to route requests within your application. -
placeholder
: A placeholder for the keyword search field; defaults toSearch
. -
filter_types
: Optionally include an array of values and names to be used via a filters dropdown. This allows passing extra information into your controller for complex searches.
Handling search requests and responses can be done as usual. For example, you could set up the path search_everything_path
by putting the following in your routes.rb
file:
get :search_everything, controller: :search
And creating the controller:
class SearchController < ApplicationController
def search_everything
@record_type = params[:filter_type]
if @record_type == 'organization'
q = Organization.order("name ASC").ransack(name_downcase_cont_any: params[:q].downcase).result
elsif @record_type == 'user'
q = User.order("lastname ASC").ransack(username_or_email_or_lastname_or_firstname_cont_any: params[:q]).result
end
@results = q.to_a.uniq
respond_to do |format|
format.html {}
end
end
end
Sitemaps can be configured via the sitemap_generator
gem dependency. Read the full setup documentation here.
A spinner is available to use for page loads, transitions, etc. To include it in your app, load the following partial in your layout template:
= render partial: 'layouts/synapse_ui/components/spinner', locals: { color: 'light', icon: 'fal fa-spinner-third' }
This accepts variables for color
and icon
. By default, color is set to light
, but can also be dark
. The icon
variable is fal fa-spinner-third
by default, but can be any fully qualified Font Awesome icon.
To display the spinner, adjust the following to work with your application.js
file:
// Show spinner during Turbolinks loads
$(document).on('turbolinks:load', function() {
// hide spinner when a page is loaded
$(".spinner").hide();
});
$(document).on('turbolinks:click', function() {
// show spinner when a Turbolinks link is clicked
$(".spinner").show();
});
// Show spinner during AJAX requests
$(document).on('turbolinks:load', function() {
// hide spinner to start
$(".spinner").hide();
// show spinner on AJAX start
$(document).ajaxStart(function(){
$(".spinner").show();
});
// hide spinner on AJAX stop
$(document).ajaxStop(function(){
$(".spinner").hide();
});
});
If you want to create a custom spinner template, instead, you can do so by wrapping your own HTML with the spinner
class.
A table generator is available for creating advanced, flexible tables. To invoke, include the following in your view:
= render partial: 'layouts/synapse_ui/components/tables/config', locals: { header: false, columns: ['Column 1', 'Column 2'], items: @items, namespace: 'namespace', type: 'item-type', cache_scope: 'specific_cache_scope', cache_status: true, style: 'minimal condensed', variables: { variable_one: computed_value, variable_two: "red" } }
You can pass the following variables into the locals
:
-
title
: Defaults tofalse
unless a title is included. -
header
: Defaults tofalse
unless optionally included and set totrue
. This allows you to display column headers. -
columns
: Set an array of column titles between one and a handful. Defaults tofalse
if not included. -
paginate
: Decide whether the list should paginate. Iftrue
, you'll need to set the pagination parameters on the query, probably in the controller. If not set, it will default tofalse
. (To ensure this works, you'll want to addgem 'will_paginate', require: true
to your appGemfile
.) -
per_page
: Sets the pagination number to 25 unless it's explicitly declared otherwise. -
items
: Include the query you want to iterate over. -
namespace
: Optionally include a namespace to use in the list item partial (e.g., to reuse partials for multiple namespaces). -
type
: Include the partial name for this item type. Create partials in a folder in your app atapp/views/layouts/synapse_ui/components/tables/item_types
. E.g.,_page.html.erb
would be called withpage
. -
cache_status
: Determines whether results will be cached or not. Defaults tofalse
unless included astrue
. -
cache_scope
Sets the cache scope automatically using#{controller_path}/#{action_name}
unless it's explicitly included, e.g.,admin_users
. Including a scope for the cache key helps to differentiate similar queries for separate views. -
cache_expire
: Sets cache expiration using Ruby time helpers, e.g.,1.hour
. Defaults tofalse
unless declared. -
style
: Include classes to pass into thetable
element. Defaults tofalse
unless something is included. -
data_controller
: Include a controller for Stimulus actions, e.g.,drag-table-row
. -
data_action
: Include any actions for a Stimulus controller separated by a single space. For example:dragstart->drag-table-row#dragstart dragover->drag-table-row#dragover
. -
data_url
: Include a data URL attribute for handling things (maybe in JavaScript). (Note: This is included in the table layout twice, both at thetable
element as well as in thetbody
element. This is designed to allow flexibility for actions you might want to take that depend on this value.) -
variables
: Pass anything else you like for use in your template. For example:variables: { color: 'red', account: Current.account }
Then retrieve them by callingvariables[:color]
orvariables[:account]
. Defaults tonil
unless included.
Forthcoming...
Bug reports and pull requests are welcome on GitHub at https://github.com/fiatinsight/synapse_ui.
The gem is available as open source under the terms of the MIT License.