Last active
December 14, 2015 04:29
-
-
Save jbgutierrez/5028740 to your computer and use it in GitHub Desktop.
Laboratorio de yeoman y angular (http://yoplay.jbgutierrez.info)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
String::pad = (l, s) -> | |
if (l -= @length) > 0 | |
(s = new Array(Math.ceil(l / s.length) + 1).join(s)).substr(0, s.length) + this + s.substr(0, l - s.length) | |
else | |
@ | |
app = angular.module "yoplayApp", ["ngResource"] | |
app.factory "myHttpInterceptor", ["$q", "$window", "$rootScope", ($q, $window, $rootScope) -> | |
(promise) -> | |
$rootScope.loading = true | |
promise.then ((response) -> | |
$rootScope.loading = false | |
response | |
), (response) -> | |
$rootScope.loading = false | |
$q.reject response | |
] | |
app.config [ '$httpProvider', ($httpProvider) -> | |
$httpProvider.responseInterceptors.push "myHttpInterceptor" | |
] | |
app.service 'analytics', -> | |
@trackEvent = (category, action, opt_label) -> | |
_gaq.push ['_trackEvent', category, action, opt_label.toLowerCase()] | |
app.factory 'LastFM', ["$resource", ($resource) -> | |
$resource 'http://ws.audioscrobbler.com/2.0', | |
{}, | |
get: | |
method: 'GET' | |
params: | |
api_key: '<secret>' | |
format: 'json' | |
] | |
app.factory 'VK', ["$resource", ($resource) -> | |
$resource 'https://api.vkontakte.ru/method/audio.search', | |
{}, | |
get: | |
method: 'JSONP' | |
params: | |
offset: 0 | |
count: 50 | |
auto_complete: 1 | |
access_token: '<secret>' | |
] | |
app.factory "player", ["$rootScope", "$document", "analytics", "VK", ($rootScope, $document, analytics, VK) -> | |
player = | |
audio: null | |
audio = $document[0].createElement("audio") | |
$rootScope.canPlayMP3 = angular.isFunction(audio.canPlayType) && audio.canPlayType("audio/mpeg") | |
if $rootScope.canPlayMP3 | |
audio.addEventListener "ended", (-> | |
$rootScope.$apply -> | |
player.nextTrack() | |
), false | |
player = | |
audio: audio | |
loadTrack: (track, album) -> | |
@album = album if angular.isDefined(album) | |
@track = track if angular.isDefined(track) | |
track = @album.tracks[@track] | |
window.success = (result) => | |
tracks = result.response | |
tracks.shift() | |
if tracks.length | |
matches = tracks.filter (t) -> t.duration is +track.duration | |
source = if matches.length then matches[0] else tracks[0] | |
audio.src = source.url | |
audio.play() | |
analytics.trackEvent 'VK', 'player', 'successed' | |
else | |
track.notFound = true | |
@album.incomplete = true | |
@nextTrack() | |
analytics.trackEvent 'VK', 'player', 'failed' | |
VK.get {q: "#{@album.artist} #{track.name}", callback: 'success'} | |
nextTrack: -> | |
@track++ | |
@track = 0 unless @album.tracks.length > @track | |
@loadTrack() | |
player | |
] | |
app.filter "formatYear", -> | |
(str = '') -> | |
tokens = str.match(/\d{4}/) | |
if tokens then tokens[0] else str | |
app.filter "humanizeDuration", -> | |
(number) -> | |
Math.floor(+number / 60) + ":" + (+number % 60).toFixed().pad(2, "0") | |
@MainCtrl = ["$scope", "analytics", "LastFM", "player", ($scope, analytics, LastFM, player) -> | |
$scope.album = null | |
$scope.player = player | |
showInfo = (album) -> | |
LastFM.get {method: 'album.getinfo', mbid: album.mbid}, (result) -> | |
album = result.album | |
album.imagePath = album.image[2]['#text'] | |
album.tracks = if angular.isArray(album.tracks.track) then album.tracks.track else [ album.tracks.track ] | |
$scope.album = album | |
player.loadTrack 0, album | |
$scope.search = -> | |
return unless $scope.criteria | |
LastFM.get {method: 'artist.gettopalbums', artist: $scope.criteria}, (result) -> | |
albums = result.topalbums && result.topalbums.album || [] | |
albums = [albums] unless angular.isArray(albums) | |
albums = albums.filter (a) -> a.mbid | |
if albums.length | |
$scope.notFoundCriteria = false | |
a.imagePath = a.image[1]['#text'] for a in albums | |
showInfo albums[0] | |
$scope.albums = albums | |
analytics.trackEvent 'LastFM', 'search', 'successed' | |
else | |
$scope.notFoundCriteria = $scope.criteria | |
analytics.trackEvent 'LastFM', 'search', 'failed' | |
analytics.trackEvent 'user', 'search', $scope.criteria | |
$scope.showInfo = (album) -> | |
showInfo(album) | |
analytics.trackEvent 'user', 'showInfo', "#{album.artist} - #{album.name}" | |
$scope.getColumnStyle = (idx, total) -> | |
columnIdx = Math.floor(idx * 2 / total) | |
style = if columnIdx then 'margin-left: 350px' else '' | |
unless idx == 0 | |
nextColumnIdx = Math.floor((idx - 1) * 2 / total) | |
style += "; margin-top: -#{Math.ceil(total / 2) * 2}0px" if nextColumnIdx isnt columnIdx | |
style | |
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
var app; | |
String.prototype.pad = function(l, s) { | |
if ((l -= this.length) > 0) { | |
return (s = new Array(Math.ceil(l / s.length) + 1).join(s)).substr(0, s.length) + this + s.substr(0, l - s.length); | |
} else { | |
return this; | |
} | |
}; | |
app = angular.module("yoplayApp", ["ngResource"]); | |
app.factory("myHttpInterceptor", [ | |
"$q", "$window", "$rootScope", function($q, $window, $rootScope) { | |
return function(promise) { | |
$rootScope.loading = true; | |
return promise.then((function(response) { | |
$rootScope.loading = false; | |
return response; | |
}), function(response) { | |
$rootScope.loading = false; | |
return $q.reject(response); | |
}); | |
}; | |
} | |
]); | |
app.config([ | |
'$httpProvider', function($httpProvider) { | |
return $httpProvider.responseInterceptors.push("myHttpInterceptor"); | |
} | |
]); | |
app.service('analytics', function() { | |
return this.trackEvent = function(category, action, opt_label) { | |
return _gaq.push(['_trackEvent', category, action, opt_label.toLowerCase()]); | |
}; | |
}); | |
app.factory('LastFM', [ | |
"$resource", function($resource) { | |
return $resource('http://ws.audioscrobbler.com/2.0', {}, { | |
get: { | |
method: 'GET', | |
params: { | |
api_key: '<secret>', | |
format: 'json' | |
} | |
} | |
}); | |
} | |
]); | |
app.factory('VK', [ | |
"$resource", function($resource) { | |
return $resource('https://api.vkontakte.ru/method/audio.search', {}, { | |
get: { | |
method: 'JSONP', | |
params: { | |
offset: 0, | |
count: 50, | |
auto_complete: 1, | |
access_token: '<secret>' | |
} | |
} | |
}); | |
} | |
]); | |
app.factory("player", [ | |
"$rootScope", "$document", "analytics", "VK", function($rootScope, $document, analytics, VK) { | |
var audio, player; | |
player = { | |
audio: null | |
}; | |
audio = $document[0].createElement("audio"); | |
$rootScope.canPlayMP3 = angular.isFunction(audio.canPlayType) && audio.canPlayType("audio/mpeg"); | |
if ($rootScope.canPlayMP3) { | |
audio.addEventListener("ended", (function() { | |
return $rootScope.$apply(function() { | |
return player.nextTrack(); | |
}); | |
}), false); | |
player = { | |
audio: audio, | |
loadTrack: function(track, album) { | |
var _this = this; | |
if (angular.isDefined(album)) { | |
this.album = album; | |
} | |
if (angular.isDefined(track)) { | |
this.track = track; | |
} | |
track = this.album.tracks[this.track]; | |
window.success = function(result) { | |
var matches, source, tracks; | |
tracks = result.response; | |
tracks.shift(); | |
if (tracks.length) { | |
matches = tracks.filter(function(t) { | |
return t.duration === +track.duration; | |
}); | |
source = matches.length ? matches[0] : tracks[0]; | |
audio.src = source.url; | |
audio.play(); | |
return analytics.trackEvent('VK', 'player', 'successed'); | |
} else { | |
track.notFound = true; | |
_this.album.incomplete = true; | |
_this.nextTrack(); | |
return analytics.trackEvent('VK', 'player', 'failed'); | |
} | |
}; | |
return VK.get({ | |
q: "" + this.album.artist + " " + track.name, | |
callback: 'success' | |
}); | |
}, | |
nextTrack: function() { | |
this.track++; | |
if (!(this.album.tracks.length > this.track)) { | |
this.track = 0; | |
} | |
return this.loadTrack(); | |
} | |
}; | |
} | |
return player; | |
} | |
]); | |
app.filter("formatYear", function() { | |
return function(str) { | |
var tokens; | |
if (str == null) { | |
str = ''; | |
} | |
tokens = str.match(/\d{4}/); | |
if (tokens) { | |
return tokens[0]; | |
} else { | |
return str; | |
} | |
}; | |
}); | |
app.filter("humanizeDuration", function() { | |
return function(number) { | |
return Math.floor(+number / 60) + ":" + (+number % 60).toFixed().pad(2, "0"); | |
}; | |
}); | |
this.MainCtrl = [ | |
"$scope", "analytics", "LastFM", "player", function($scope, analytics, LastFM, player) { | |
var showInfo; | |
$scope.album = null; | |
$scope.player = player; | |
showInfo = function(album) { | |
return LastFM.get({ | |
method: 'album.getinfo', | |
mbid: album.mbid | |
}, function(result) { | |
album = result.album; | |
album.imagePath = album.image[2]['#text']; | |
album.tracks = angular.isArray(album.tracks.track) ? album.tracks.track : [album.tracks.track]; | |
$scope.album = album; | |
return player.loadTrack(0, album); | |
}); | |
}; | |
$scope.search = function() { | |
if (!$scope.criteria) { | |
return; | |
} | |
LastFM.get({ | |
method: 'artist.gettopalbums', | |
artist: $scope.criteria | |
}, function(result) { | |
var a, albums, _i, _len; | |
albums = result.topalbums && result.topalbums.album || []; | |
if (!angular.isArray(albums)) { | |
albums = [albums]; | |
} | |
albums = albums.filter(function(a) { | |
return a.mbid; | |
}); | |
if (albums.length) { | |
$scope.notFoundCriteria = false; | |
for (_i = 0, _len = albums.length; _i < _len; _i++) { | |
a = albums[_i]; | |
a.imagePath = a.image[1]['#text']; | |
} | |
showInfo(albums[0]); | |
$scope.albums = albums; | |
return analytics.trackEvent('LastFM', 'search', 'successed'); | |
} else { | |
$scope.notFoundCriteria = $scope.criteria; | |
return analytics.trackEvent('LastFM', 'search', 'failed'); | |
} | |
}); | |
return analytics.trackEvent('user', 'search', $scope.criteria); | |
}; | |
$scope.showInfo = function(album) { | |
showInfo(album); | |
return analytics.trackEvent('user', 'showInfo', "" + album.artist + " - " + album.name); | |
}; | |
return $scope.getColumnStyle = function(idx, total) { | |
var columnIdx, nextColumnIdx, style; | |
columnIdx = Math.floor(idx * 2 / total); | |
style = columnIdx ? 'margin-left: 350px' : ''; | |
if (idx !== 0) { | |
nextColumnIdx = Math.floor((idx - 1) * 2 / total); | |
if (nextColumnIdx !== columnIdx) { | |
style += "; margin-top: -" + (Math.ceil(total / 2) * 2) + "0px"; | |
} | |
} | |
return style; | |
}; | |
} | |
]; | |
}).call(this); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]--> | |
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]--> | |
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]--> | |
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]--> | |
<head> | |
<meta charset="utf-8"/> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> | |
<title>YoPlay - Música online</title> | |
<meta name="description" content="Catálogo musical proporcionado por LastFM y VK"> | |
<meta name="viewport" content="width=960"> | |
<!-- Place favicon.ico and apple-touch-icon.png in the root directory --> | |
<link rel="stylesheet" href="styles/styles.css"> | |
</head> | |
<body ng-app="yoplayApp" ng-controller="MainCtrl"> | |
<!--[if lt IE 7]> | |
<p class="chromeframe">You are using an outdated browser. <a href="http://browsehappy.com/">Upgrade your browser today</a> or <a href="http://www.google.com/chromeframe/?redirect=true">install Google Chrome Frame</a> to better experience this site.</p> | |
<![endif]--> | |
<!--[if lt IE 9]> | |
<script src="components/es5-shim/es5-shim.js"></script> | |
<script src="components/json3/lib/json3.min.js"></script> | |
<![endif]--> | |
<p class="browser-not-supported" style='display:none' ng-hide='canPlayMP3'>Tu navegador no soporta la reproducción de ficheros mp3. Usa Chrome, IE > 8 o Safari.</p> | |
<div class="loading" style='display:none' ng-show='loading'> | |
<p>cargando <img src='/images/ajax-loader.gif' /> </p> | |
</div> | |
<div class='container'> | |
<header class="header"> | |
<h1 id='logo'> | |
<span>YoPlay</span> | |
<img src='/images/logo.jpg' alt='YoPlay logo'/> | |
</h1> | |
<p class='lead'>Música por cortesía de <a href='http://yeoman.io' target='_blank'>Yeoman</a>, <a href='http://www.lastfm.es' target='_blank'>LastFM</a> y <a href='http://vk.com/' target='_blank'>VK</a>.</p> | |
<form ng-submit="search()"> | |
<input type="text" class='input-xlarge' ng-model="criteria" placeholder='¿Qué artista te apetece escuchar?' /> | |
<button class="btn btn-large btn-success">Reproducir</button> | |
<p class='error' style='display:none' ng-show="notFoundCriteria">* Lo siento pero no encuentro discos de "{{notFoundCriteria}}" en LastFM</p> | |
</form> | |
</header> | |
<section class="library" style='display:none' ng-show="albums.length"> | |
<img class='image' ng-src="{{album.imagePath}}" title='{{album.name}}' ng-repeat="album in albums" ng-click="showInfo(album)"/> | |
</section> | |
<section class="album-info" style='display:none' ng-show="album"> | |
<img class='image' ng-src="{{album.imagePath}}" /> | |
<div class="details"> | |
<div class="page-header"> | |
<h2> | |
<span> | |
<i class="control icon-play" title='Reproducir' ng-click="player.audio.play()" ng-show="canPlayMP3 && player.audio.paused"></i> | |
<i class="control icon-pause" title='Pausar' ng-click="player.audio.pause()" ng-hide="!canPlayMP3 || player.audio.paused"></i> | |
{{ album.name }} | |
</span> | |
<small>{{ album.releasedate | formatYear }}</small> | |
</h2> | |
</div> | |
<hr> | |
<ol class='tracks' | |
><li class='track' style='{{ getColumnStyle($index, album.tracks.length) }}' ng-repeat="track in album.tracks" ng-class="{current: canPlayMP3 && $index==player.track, 'not-found': track.notFound}" ng-click="player.loadTrack($index)"> | |
<span class='number muted'>{{ $index + 1 }}</span> | |
<span class='name'>{{ track.name }}</span> | |
<span class='duration muted'>{{ track.duration | humanizeDuration }}</span> | |
</li | |
></ol> | |
<p class='error' style='display:none' ng-show="album.incomplete">* Parece que VK no están todas las canciones del disco</p> | |
</div> | |
</section> | |
</div> | |
<!-- build:js scripts/scripts.js --> | |
<script src="components/angular/angular.js"></script> | |
<script src="components/angular-resource/angular-resource.js"></script> | |
<script src="scripts/app.js"></script> | |
<!-- endbuild --> | |
<script> | |
var _gaq=[['_setAccount','UA-XXXXXXXX-1'],['_trackPageview']]; | |
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0]; | |
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js'; | |
s.parentNode.insertBefore(g,s)}(document,'script')); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import "compass"; | |
/* Estilos procedentes de twitter bootstrap */ | |
@import "compass_twitter_bootstrap/mixins"; | |
@import "compass_twitter_bootstrap/reset"; | |
@import "compass_twitter_bootstrap/variables"; | |
.btn { | |
display: inline-block; | |
@include ie7-inline-block(); | |
padding: 4px 14px; | |
margin-bottom: 0; // For input.btn | |
font-size: $baseFontSize; | |
line-height: $baseLineHeight; | |
*line-height: $baseLineHeight; | |
text-align: center; | |
vertical-align: middle; | |
cursor: pointer; | |
@include buttonBackground($btnBackground, $btnBackgroundHighlight, $grayDark, 0 1px 1px rgba(255,255,255,.75)); | |
border: 1px solid $btnBorder; | |
*border: 0; // Remove the border to prevent IE7's black border on input:focus | |
border-bottom-color: darken($btnBorder, 10%); | |
@include border-radius(4px); | |
@include ie7-restore-left-whitespace(); // Give IE7 some love | |
@include box-shadow(#{inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)}); | |
// Hover state | |
&:hover { | |
color: $grayDark; | |
text-decoration: none; | |
background-color: darken($white, 10%); | |
*background-color: darken($white, 15%); /* Buttons in IE7 don't get borders, so darken on hover */ | |
background-position: 0 -15px; | |
// transition is only when going to hover, otherwise the background | |
// behind the gradient (there for IE<=9 fallback) gets mismatched | |
@include transition(background-position .1s linear); | |
} | |
// Focus state for keyboard and accessibility | |
&:focus { | |
@include tab-focus(); | |
} | |
// Active state | |
&.active, | |
&:active { | |
background-color: darken($white, 10%); | |
background-color: darken($white, 15%) \9; | |
background-image: none; | |
outline: 0; | |
@include box-shadow(#{inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)}); | |
} | |
// Disabled state | |
&.disabled, | |
&[disabled] { | |
cursor: default; | |
background-color: darken($white, 10%); | |
background-image: none; | |
@include opacity(65); | |
@include box-shadow(none); | |
} | |
} | |
// Button Sizes | |
// -------------------------------------------------- | |
// Large | |
.btn-large { | |
padding: 9px 14px; | |
font-size: $baseFontSize + 2px; | |
line-height: normal; | |
@include border-radius(5px); | |
} | |
// Success appears as green | |
.btn-success { | |
@include buttonBackground($btnSuccessBackground, $btnSuccessBackgroundHighlight); | |
} | |
body { | |
margin: 0; | |
font-family: $baseFontFamily; | |
font-size: $baseFontSize; | |
line-height: $baseLineHeight; | |
color: $textColor; | |
background-color: $bodyBackground; | |
} | |
h1, h2 { | |
margin: ($baseLineHeight / 2) 0; | |
font-family: $headingsFontFamily; | |
font-weight: $headingsFontWeight; | |
line-height: 1; | |
color: $headingsColor; | |
text-rendering: optimizelegibility; // Fix the character spacing for headings | |
small { | |
font-weight: normal; | |
line-height: 1; | |
color: $grayLight; | |
} | |
} | |
h1 { | |
xdisplay: none; | |
} | |
ul, ol { | |
padding: 0; | |
margin: 0 0 $baseLineHeight / 2 25px; | |
} | |
li { | |
line-height: $baseLineHeight; | |
} | |
hr { | |
margin: $baseLineHeight 0; | |
border: 0; | |
border-top: 1px solid $hrBorder; | |
border-bottom: 1px solid $white; | |
} | |
form { | |
margin: 0 0 $baseLineHeight; | |
} | |
input[type="text"] { | |
display: inline-block; | |
height: $baseLineHeight; | |
padding: 4px 6px; | |
margin-bottom: 9px; | |
font-size: $baseFontSize; | |
line-height: $baseLineHeight; | |
color: $gray; | |
@include border-radius($inputBorderRadius); | |
background-color: $inputBackground; | |
border: 1px solid $inputBorder; | |
@include box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); | |
@include transition(#{border linear .2s, box-shadow linear .2s}); | |
// Focus state | |
&:focus { | |
border-color: rgba(82,168,236,.8); | |
outline: 0; | |
outline: thin dotted \9; /* IE6-9 */ | |
@include box-shadow(#{inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6)}); | |
} | |
@include placeholder(); | |
width: 270px; | |
} | |
.muted { | |
color: #999999; | |
} | |
/* Estilos específicos de la aplicación */ | |
@import "sprites"; | |
.lead { | |
margin-bottom: $baseLineHeight; | |
font-size: 20px; | |
font-weight: 200; | |
line-height: $baseLineHeight * 1.5; | |
} | |
a { | |
@include unstyled-link; | |
} | |
.error { | |
color: $errorText; | |
font-size: 14px; | |
font-style: italic; | |
padding: 10px; | |
text-align: center; | |
} | |
.browser-not-supported { | |
@extend .error; | |
background-color: $errorBackground; | |
border: 1px solid $errorBorder; | |
} | |
.loading { | |
position: fixed; | |
top: 0; | |
width: 100%; | |
p { | |
background-color: $warningBackground; | |
color: $warningText; | |
border: 1px solid $warningBorder; | |
width: 80px; | |
padding: 2px 10px; | |
margin: 0 auto; | |
font-size: 12px; | |
text-align: center; | |
} | |
} | |
#logo { | |
@include hide-text(); | |
img { | |
height: 124px; | |
width: 350px; | |
margin-left: -35px; | |
} | |
} | |
.container { | |
width: 940px; | |
margin: 0 auto; | |
} | |
.header { | |
margin: 60px 0 30px; | |
text-align: center; | |
form input { | |
margin-top: 8px; | |
height: 28px; | |
font-size: 16px; | |
} | |
} | |
.library { | |
margin: 30px 128px; | |
height: 128px; | |
overflow-y: scroll; | |
text-align: center; | |
img { | |
width: 64px; | |
height: 64px; | |
cursor: pointer; | |
} | |
} | |
.album-info { | |
.image { | |
width: 220px; | |
float: left; | |
margin-left: 20px; | |
} | |
.details { | |
margin-left: 260px; | |
} | |
hr { | |
margin: 10px 0px; | |
} | |
h2 { | |
font-size: 16px; | |
margin: 0; | |
} | |
.control { | |
cursor: pointer; | |
vertical-align: top; | |
margin-top: 3px; | |
} | |
small { | |
float: right; | |
font-size: 14px; | |
} | |
.tracks { | |
margin: 0 10px; | |
li { | |
padding: 0 14px; | |
} | |
} | |
img { | |
margin-top: 10px; | |
@include single-box-shadow(rgba(0, 0, 0, 0.5), 1px, 1px, 14px); | |
} | |
li { | |
width: 300px; | |
font-size: 12px; | |
line-height: 20px; | |
height: 20px; | |
cursor: pointer; | |
overflow: hidden; | |
display: block; | |
list-style: none; | |
&.current { | |
background-color: #f5f5f5; | |
} | |
.number { | |
text-align: right; | |
width: 30px; | |
margin-right: 10px; | |
} | |
.duration { | |
float: right; | |
} | |
.name { | |
width: 230px; | |
text-overflow: ellipsis; | |
overflow: hidden; | |
white-space: nowrap; | |
word-wrap: normal; | |
display: inline-block; | |
} | |
&.not-found { | |
.name { | |
text-decoration: line-through; | |
} | |
} | |
} | |
.error { | |
font-size: 12px; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment