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.scssfile:
@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_uiin 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
endIn 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
endBy 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_localsYou 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-topand/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_localsYou 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: :searchAnd 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
endSitemaps 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 tofalseunless a title is included. -
header: Defaults tofalseunless 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 tofalseif 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: trueto 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.erbwould be called withpage. -
cache_status: Determines whether results will be cached or not. Defaults tofalseunless included astrue. -
cache_scopeSets 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 tofalseunless declared. -
style: Include classes to pass into thetableelement. Defaults tofalseunless 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 thetableelement as well as in thetbodyelement. 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 tonilunless 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.