Skip to content

Instantly share code, notes, and snippets.

@sjernigan
Last active September 6, 2016 00:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sjernigan/52d23d6e7270b29be59514688f38ff85 to your computer and use it in GitHub Desktop.
Save sjernigan/52d23d6e7270b29be59514688f38ff85 to your computer and use it in GitHub Desktop.
Star Rating Dashing Widget

Description

This widget, based on the bar chart widget, show stars and fills in the correct percentage. We use it for monitoring the app store feedback.

Example

Usage:

In erb file:

<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
  <div data-id="my_star_rating_widget" data-view="StarRatings" data-title="Item Scores" data-moreinfo="" data-min="0" data-max="5"></div>
</li>

Provide data to the widget using something like

items = []
items << {name: "Item 1", value: rand(5)}
items << {name: "Item 2", value: rand(5)}
items << {name: "Item 3", value: rand(5)}
send_event 'my_star_rating_widget', {  title: "Widget Title", items: items}

Move html, scss and coffee files to widgets/star_ratings

class Dashing.StarRatings extends Dashing.Widget
@accessor 'title'
@accessor 'max'
@accessor 'min'
ready: ->
node = $(@node)
@max = parseInt node.data "max"
@min = parseInt node.data "min"
@drawWidget( @get('items') )
onData: (eventData) ->
@drawWidget(eventData.items)
drawWidget: (star_items) ->
container = $(@node)
rowsContainer = container.find('.rows-container')
if !star_items
rowsContainer.empty()
else
# Float value used to scale the rows to use the entire space of the widget
rowHeight = 100 / star_items.length
counter = 0
@clearIntervals()
# Add or move rows for each project. Checks first if the row already exists.
star_items.forEach (item) =>
normalizedItemName = item.name.replace(/\W+/g, "_")
referenceRow = rowsContainer.children().eq(counter)
existingRow = rowsContainer.find("."+normalizedItemName)
if existingRow.length
if referenceRow.attr("class").indexOf(normalizedItemName) == -1
existingRow.detach().insertBefore(referenceRow)
existingRow.hide().fadeIn(1200)
else
row = createRow(item)
if referenceRow.length
row.insertBefore(referenceRow)
else
rowsContainer.append(row)
row.hide().fadeIn(1200)
elem = rowsContainer.find("."+normalizedItemName+" .full-stars")
if elem.length
@animateBarContent(elem[0], parseFloat(elem[0].style.width),
parseFloat(item.value), 1000)
++counter
# Remove any nodes that were not in the new data, these will be the rows
# at the end of the widget.
currentNode = rowsContainer.children().eq(counter-1)
while currentNode.next().length
currentNode = currentNode.next()
currentNode.fadeOut(100, -> $(this).remove() )
# Set the height after rows were added/removed.
rows = rowsContainer.children()
percentageOfTotalHeight = 90 / star_items.length
applyCorrectedRowHeight(rows, percentageOfTotalHeight)
applyZebraStriping(rows)
#***/
# Create a JQuery row object with the proper structure and base
# settings for the item passed in.
#
# The Row DOM Hierarchy:
# Row
# Row Content (here so we can use vertical alignment)
# Project Name
# Outer Bar Container (The border and background)
# Inner Bar Container (The value and text)
#
# @item - object representing an item and it's value
# /
createRow = (item) ->
row = ( $("<div/>")
.attr("class", "row " + item.name.replace(/\W+/g, "_") ) )
# .attr("class", item.name.replace(/\W+/g, "_") ) )
# .attr("id", item.name.match(/[[\w]*(?:.*)]/ ) ) )
rowContent = ( $("<div/>")
.attr("class", "row-content") )
projectName = ( $("<div/>")
.attr("class", "project-name")
.text(item.name)
.attr("title", item.name) )
ratings = $("<div/>")
.attr("class", "ratings")
emptyStars = $("<div/>")
.attr("class", "empty-stars")
fullStars = $("<div/>")
.attr("class", "full-stars")
fullStars.css("width", "0%")
# Put it all together.
rowContent.append(projectName)
ratings.append(emptyStars)
ratings.append(fullStars)
rowContent.append(ratings)
row.append(rowContent)
return row
#***/
# Does calculations for the animation and sets up the javascript
# interval to perform the animation.
#
# @element - element that is going to be animated.
# @from - the value that the element starts at.
# @to - the value that the element is going to.
# @baseDuration - the minimum time the animation will perform.
# /
animateBarContent: (element, from, to, baseDuration) ->
endpointDifference = (to-from)
if endpointDifference != 0
currentValue = from
# Every x milliseconds, the function should run.
stepInterval = 16.667
# Change the duration based on the distance between points.
duration = baseDuration + (Math.abs(endpointDifference) * 2500)/(@max - @min)
numberOfSteps = duration / stepInterval
valueIncrement = endpointDifference / numberOfSteps
local_max = @max
local_min = @min
interval = setInterval(
->
currentValue += valueIncrement
if Math.abs(currentValue - from) >= Math.abs(endpointDifference)
setBarValue(element, to, local_min, local_max)
clearInterval(interval)
else
setBarValue(element, currentValue, local_min, local_max)
stepInterval)
@addInterval(interval)
#***/
# Sets the text and width of the element in question to the specified value
# after making sure it is bounded between min and max
#
# @element - element to be set
# @value - the numeric value to set the element to. This can be a float.
# /
setBarValue = (element, value, minValue, maxValue) ->
if (value > maxValue)
value = maxValue
else if (value < minValue)
value = minValue
#element.textContent = Math.floor(value)
element.style.width = (((value - minValue) * 100)/(maxValue - minValue)) + "%"
#***/
# Applies a percentage-based row height to the list of rows passed in.
#
# @rows - the elements to apply this height value to
# @percentageOfTotalHeight - The height to be applied to each row.
# /
applyCorrectedRowHeight = (rows, percentageOfTotalHeight) ->
height = percentageOfTotalHeight + "%"
for row in rows
row.style.height = height
#***/
# Adds a class to every other row to change the background color. This
# was done mainly for readability.
#
# @rows - list of elements to run zebra-striping on
# /
applyZebraStriping = (rows) ->
isZebraStripe = false
for row in rows
# In case elements are moved around, we don't want them to retain this.
row.classList.remove("zebra-stripe")
if isZebraStripe
row.classList.add("zebra-stripe")
isZebraStripe = !isZebraStripe
#***/
# Stops all javascript intervals from running and clears the list.
#/
clearIntervals: ->
if @intervalList
for interval in @intervalList
clearInterval(interval)
@intervalList = []
#***/
# Adds a javascript interval to a list so that it can be tracked and cleared
# ahead of time if the need arises.
#
# @interval - the javascript interval to add
#/
addInterval: (interval) ->
if !@intervalList
@intervalList = []
@intervalList.push(interval)
<h1 class="title" data-bind="title"></h1>
<div class="rows-container">
</div>
<p class="more-info" data-bind="moreinfo | raw"></p>
<p class="updated-at" data-bind="updatedAtMessage"></p>
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
// row-size is a magic number used for scaling. It will make things bigger
// or smaller but always in proportion with each other. Feel free to change
// this to reflect your personal needs.
$row-size: 0.7em;
$blue: #2db4d4;
$white: #ffffff;
$base-color: $blue;
$base-color-dark: darken($base-color, 10%);
$base-color-light: lighten($base-color, 10%);
$base-color-lighter: lighten($base-color, 25%);
$base-color-lightest: lighten($base-color, 35%);
$text-color: $base-color-lightest;
// ----------------------------------------------------------------------------
// Widget-project-completion styles
// ----------------------------------------------------------------------------
.widget.widget-star-ratings {
height: 100%;
width: 100%;
padding: 5px;
position:relative;
background-color: $base-color;
vertical-align: baseline;
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing:border-box;
}
.title {
color: $text-color;
margin-bottom: 5px;
}
.rows-container {
height: 75%;
width:100%;
color: $text-color;
font-size: $row-size;
text-align:center;
}
.row {
height:0%;
width:100%;
vertical-align: middle;
display:table;
transition-property: height;
transition-duration: 0.3s;
transition-timing-function: linear;
}
.row-content {
padding-left: 5px;
display:table-cell;
vertical-align: middle;
}
.project-name {
display:inline-block;
width:35%;
padding-right: $row-size;
text-align: left;
vertical-align: middle;
text-overflow: ellipsis;
overflow:hidden;
white-space: nowrap;
}
.outer-bar {
display:inline-block;
width: 65%;
vertical-align: middle;
border: ($row-size / 3) solid $base-color-dark;
border-radius: 2 * $row-size;
background-color: $base-color-lighter;
.inner-bar {
background-color: $base-color-dark;
border-radius: $row-size / 2;
color: $white;
}
}
.ratings {
position: relative;
vertical-align: middle;
display: inline-block;
color: #b1b1b1;
overflow: hidden;
}
.full-stars {
position: absolute;
width: 65%;
left: 0;
top: 0;
white-space: nowrap;
overflow: hidden;
color: #fde16d;
}
.empty-stars:before, .full-stars:before {
content:"\2605\2605\2605\2605\2605";
font-size: 14pt;
}
.empty-stars:before {
-webkit-text-stroke: 1px #848484;
}
.full-stars:before {
-webkit-text-stroke: 1px orange;
}
.zebra-stripe {
background-color: $base-color-light;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment