Compare commits

...

7 commits

Author SHA1 Message Date
Devin Howard 17adf5a6c5 Merge branch 'develop' into feature/better.select 2017-01-10 18:01:56 -05:00
Connor Turland bf9b25da9f merge develop 2017-01-04 10:50:59 -05:00
Connor Turland 5ba7ba9355 don't commit swp 2016-09-20 12:28:41 -04:00
Connor Turland ce7c88c78c add more space between nodes using auto-layout 2016-09-20 12:27:46 -04:00
Connor Turland 74630c2631 messed up comment 2016-09-20 12:04:42 -04:00
Connor Turland 966dd79187 Merge branch 'develop' into feature/better.select 2016-09-20 11:55:25 -04:00
Connor Turland 5d04d16590 create MetacodeSelect component 2016-09-18 15:22:51 +00:00
11 changed files with 462 additions and 62 deletions

View file

@ -164,16 +164,17 @@ jQuery.browser = browser;
// START METAMAPS CODE
// Add code that makes tab and shift+tab scroll through metacodes
$('.new_topic').bind('keydown',this,function(event){
if (event.keyCode == 9) {
if (event.shiftKey) {
event.data.rotate(-1)
} else {
event.data.rotate(1)
}
event.preventDefault();
event.stopPropagation();
Metamaps.Create.newTopic.metacode = $(items[event.data.frontIndex].image).attr('data-id');
}
if (event.keyCode == 9 && event.shiftKey) {
$(container).show()
event.data.rotate(-1);
event.preventDefault();
event.stopPropagation();
} else if (event.keyCode == 9) {
$(container).show()
event.data.rotate(1);
event.preventDefault();
event.stopPropagation();
}
});
// END METAMAPS CODE
@ -181,14 +182,14 @@ jQuery.browser = browser;
if (options.mouseWheel)
{
// START METAMAPS CODE
$('body').bind('mousewheel',this,function(event, delta) {
if (Metamaps.Create.newTopic.beingCreated &&
!Metamaps.Create.isSwitchingSet &&
!Metamaps.Create.newTopic.pinned) {
event.data.rotate(delta);
return false;
}
});
/*$('body').bind('mousewheel',this,function(event, delta) {
if (Metamaps.Create.newTopic.beingCreated &&
!Metamaps.Create.isSwitchingSet &&
!Metamaps.Create.newTopic.pinned) {
event.data.rotate(delta);
return false;
}
});*/
// END METAMAPS CODE
// ORIGINAL CODE
// $(container).bind('mousewheel',this,function(event, delta) {
@ -255,8 +256,11 @@ jQuery.browser = browser;
this.showFrontText = function()
{
if ( items[this.frontIndex] === undefined ) { return; } // Images might not have loaded yet.
$(options.titleBox).html( $(items[this.frontIndex].image).attr('title'));
$(options.altBox).html( $(items[this.frontIndex].image).attr('alt'));
// METAMAPS CODE
Metamaps.Create.newTopic.setMetacode($(items[this.frontIndex].image).attr('data-id'))
// NOT METAMAPS CODE
//$(options.titleBox).html( $(items[this.frontIndex].image).attr('title'));
//$(options.altBox).html( $(items[this.frontIndex].image).attr('alt'));
};
this.go = function()
@ -269,7 +273,10 @@ jQuery.browser = browser;
this.stop = function()
{
clearTimeout(this.controlTimer);
this.controlTimer = 0;
this.controlTimer = 0;
// METAMAPS CODE
$(container).hide()
// END METAMAPS CODE
};
@ -390,7 +397,7 @@ jQuery.browser = browser;
}
// If all images have valid widths and heights, we can stop checking.
clearInterval(this.tt);
this.showFrontText();
// METAMAPS COMMENT this.showFrontText();
this.autoRotate();
this.updateAll();

View file

@ -531,18 +531,48 @@ button.button.btn-no:hover {
left: -1000px;
display: block;
position: absolute;
width: 340px;
margin: -40px 0 0 -35px;
z-index: 1;
}
.selectedMetacode {
float: left;
background: #FFF;
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
padding: 5px 10px 5px 6px;
vertical-align: top;
border-right: 1px solid #DDD;
cursor: pointer;
}
.selectedMetacode:hover, .selectedMetacode.isBeingSelected {
background: #EDEDED;
}
.selectedMetacode img {
width: 24px;
height: 24px;
display: inline-block;
vertical-align: middle;
}
.selectedMetacode span {
vertical-align: middle;
}
.selectedMetacode .downArrow {
display: inline-block;
vertical-align: middle;
width: 0;
height: 0;
border-style: solid;
border-width: 8px 6px 0 6px;
border-color: #777 transparent transparent transparent;
margin-left: 2px;
margin-top: 4px;
}
#new_topic .twitter-typeahead {
position: absolute !important;
top: 45px;
left: 41px;
z-index: 9999;
width: 256px;
height: 34px;
float: left;
}
.new_topic #topic_name,
.new_topic .tt-hint {
@ -552,7 +582,8 @@ button.button.btn-no:hover {
margin: 0;
padding: 10px 6px;
border: none;
border-radius: 2px;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
outline: none;
font-size: 14px;
line-height: 14px;
@ -563,7 +594,7 @@ button.button.btn-no:hover {
color: #BDBDBD;
}
.openMetacodeSwitcher {
display: block;
display: none;
height: 16px;
width: 16px;
background-image: url(<%= asset_data_uri('metacodesettings_sprite.png') %>);
@ -583,8 +614,8 @@ button.button.btn-no:hover {
background-image: url(<%= asset_data_uri('pincarousel_sprite.png') %>);
position: absolute;
z-index: 2;
top: 20px;
right: 16px;
top: -20px;
right: -16px;
}
.pinCarousel:hover {
background-position: 0 -16px;
@ -597,8 +628,14 @@ button.button.btn-no:hover {
}
#metacodeImg {
height: 120px;
width: 380px;
display: none;
position: absolute !important;
top: -30px;
z-index: -1;
}
#metacodeImgTitle {
display: none;
float: left;
width: 120px;
text-align: center;

View file

@ -0,0 +1,72 @@
#metacodeSelector {
display: none;
}
.metacodeSelect {
border-top: 1px solid #DDD;
padding: 0;
.tabList {
float: left;
background: #FFF;
div {
border-right: 1px solid #DDD;
border-bottom: 1px solid #DDD;
}
div:last-child {
border-bottom: none;
}
div.active {
border-right: none;
background: #EEE;
.metacodeFilterInput {
background: #EEE;
}
}
.metacodeFilterInput {
width: 100px;
outline: none;
border: 0;
padding: 8px;
font-size: 14px;
line-height: 14px;
color: #424242;
font-family: 'din-medium', helvetica, sans-serif;
}
span {
padding: 8px;
display: block;
}
}
.metacodeList {
float:left;
list-style: none;
background: #FFF;
min-height: 107px;
min-width: 100px;
li {
padding: 8px;
cursor: pointer;
&:hover, &.keySelect {
background: #EEE;
}
}
img {
width: 24px;
height: 24px;
display: inline-block;
vertical-align: middle;
padding-right: 6px;
}
}
}

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true
class UsersController < ApplicationController
before_action :require_user, only: [:edit, :update, :updatemetacodes]
before_action :require_user, only: [:edit, :update, :updatemetacodes, :update_metacode_focus]
respond_to :html, :json
@ -91,6 +91,16 @@ class UsersController < ApplicationController
format.json { render json: @user }
end
end
# PUT /user/update_metacode_focus
def update_metacode_focus
@user = current_user
@user.settings.metacode_focus = params[:value]
@user.save
respond_to do |format|
format.json { render json: { success: "success" }}
end
end
private

View file

@ -25,6 +25,10 @@ module ApplicationHelper
end
@metacodes.sort! { |m1, m2| m2.name.downcase <=> m1.name.downcase }.rotate!(-1)
end
def user_metacode
current_user.settings.metacode_focus ? Metacode.find(current_user.settings.metacode_focus.to_i) || user_metacodes()[0] : user_metacodes()[0]
end
def user_most_used_metacodes
@metacodes = current_user.most_used_metacodes.map { |id| Metacode.find(id) }

View file

@ -66,6 +66,28 @@ class User < ApplicationRecord
json['rtype'] = 'mapper'
json
end
def recentMetacodes
array = []
self.topics.sort{|a,b| b.created_at <=> a.created_at }.each do |t|
if array.length < 5 and array.index(t.metacode_id) == nil
array.push(t.metacode_id)
end
end
array
end
def mostUsedMetacodes
self.topics.to_a.reduce({}) { |memo, topic|
if memo[topic.metacode_id] == nil
memo[topic.metacode_id] = 1
else
memo[topic.metacode_id] = memo[topic.metacode_id] + 1
end
memo
}.to_a.sort{ |a, b| b[1] <=> a[1] }.map{|i| i[0]}.slice(0, 5)
end
def all_accessible_maps
maps + shared_maps

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true
class UserPreference
attr_accessor :metacodes
attr_accessor :metacodes, :metacode_focus
def initialize
array = []
@ -15,5 +15,6 @@ class UserPreference
end
end
@metacodes = array
@metacode_focus = array[0]
end
end

View file

@ -11,24 +11,36 @@
</div>
<div id="metacodeImg">
<% @metacodes = [user_metacode()].concat(user_metacodes()).uniq %>
<% set = metacodeset() %>
<% @metacodes.each do |metacode| %>
<img class="cloudcarousel" width="40" height="40" src="<%= asset_path metacode.icon %>" alt="<%= metacode.name %>" title="<%= metacode.name %>" data-id="<%= metacode.id %>" />
<% end %>
</div>
</div>
<div class="selectedMetacode">
<img src="<%= asset_path user_metacode().icon %>" />
<span><%= user_metacode().name %></span>
<div class="downArrow"></div>
</div>
<%= form.text_field :name, :maxlength => 140, :placeholder => "title..." %>
<div id="metacodeImgTitle"></div>
<div class="clearfloat"></div>
<script>
<% @metacodes.each do |metacode| %>
<% if !metacodeset() %>
Metamaps.Create.selectedMetacodes.push("<%= metacode.id %>");
Metamaps.Create.newSelectedMetacodes.push("<%= metacode.id %>");
Metamaps.Create.selectedMetacodeNames.push("<%= metacode.name %>");
Metamaps.Create.newSelectedMetacodeNames.push("<%= metacode.name %>");
<% end %>
<% end %>
</script>
<div id="metacodeSelector"></div>
<div class="clearfloat"></div>
<script>
<% @metacodes.each do |metacode| %>
<% if !set %>
Metamaps.Create.selectedMetacodes.push("<%= metacode.id %>");
Metamaps.Create.newSelectedMetacodes.push("<%= metacode.id %>");
Metamaps.Create.selectedMetacodeNames.push("<%= metacode.name %>");
Metamaps.Create.newSelectedMetacodeNames.push("<%= metacode.name %>");
<% end %>
<% end %>
Metamaps.Create.newTopic.metacode = <%= user_metacode().id %>
<% current_user.recentMetacodes.each do |id| %>
Metamaps.Create.recentMetacodes.push(<%= id %>);
<% end %>
<% current_user.mostUsedMetacodes.each do |id| %>
Metamaps.Create.mostUsedMetacodes.push(<%= id %>);
<% end %>
</script>
<% end %>

View file

@ -104,6 +104,7 @@ Metamaps::Application.routes.draw do
end
end
post 'user/updatemetacodes', to: 'users#updatemetacodes', as: :updatemetacodes
post 'user/update_metacode_focus', to: 'users#update_metacode_focus'
namespace :api, path: '/api', default: { format: :json } do
namespace :v2, path: '/v2' do

View file

@ -1,6 +1,10 @@
/* global $, Hogan, Bloodhound */
/* global Metamaps, $, Hogan, Bloodhound */
import React from 'react'
import ReactDOM from 'react-dom'
import DataModel from './DataModel'
import MetacodeSelect from '../components/MetacodeSelect'
import Mouse from './Mouse'
import Selected from './Selected'
import Synapse from './Synapse'
@ -16,7 +20,9 @@ const Create = {
newSelectedMetacodeNames: [],
selectedMetacodes: [],
newSelectedMetacodes: [],
init: function() {
recentMetacodes: [],
mostUsedMetacodes: [],
init: function () {
var self = Create
self.newTopic.init()
self.newSynapse.init()
@ -75,7 +81,6 @@ const Create = {
}
metacodeModels.sort()
$('#metacodeImg, #metacodeImgTitle').empty()
$('#metacodeImg').removeData('cloudcarousel')
var newMetacodes = ''
metacodeModels.each(function(metacode) {
@ -83,13 +88,11 @@ const Create = {
})
$('#metacodeImg').empty().append(newMetacodes).CloudCarousel({
titleBox: $('#metacodeImgTitle'),
yRadius: 40,
xRadius: 190,
xPos: 170,
yPos: 40,
speed: 0.3,
mouseWheel: true,
bringToFront: true
})
@ -139,16 +142,30 @@ const Create = {
$('#topic_name').focus()
},
newTopic: {
init: function() {
$('#topic_name').keyup(function(e) {
const ESC = 27
init: function () {
const DOWN_ARROW = 40
const ESC = 27
$('#topic_name').keyup(function (e) {
Create.newTopic.name = $(this).val()
if (e.which == DOWN_ARROW && !Create.newTopic.name.length) {
Create.newTopic.openSelector()
}
if (e.keyCode === ESC) {
Create.newTopic.hide()
} // if
Create.newTopic.name = $(this).val()
})
$('.selectedMetacode').click(function() {
if (Create.newTopic.metacodeSelectorOpen) {
Create.newTopic.hideSelector()
$('#topic_name').focus()
} else Create.newTopic.openSelector()
})
Create.newTopic.initSelector()
$('.pinCarousel').click(function() {
if (Create.newTopic.pinned) {
@ -200,16 +217,15 @@ const Create = {
})
}
})
$('#topic_name').click(function() { Create.newTopic.hideSelector() })
// initialize metacode spinner and then hide it
$('#metacodeImg').CloudCarousel({
titleBox: $('#metacodeImgTitle'),
yRadius: 40,
xRadius: 190,
xPos: 170,
yPos: 40,
speed: 0.3,
mouseWheel: true,
bringToFront: true
})
$('.new_topic').hide()
@ -218,13 +234,62 @@ const Create = {
name: null,
newId: 1,
beingCreated: false,
metacodeSelectorOpen: false,
metacode: null,
x: null,
y: null,
addSynapse: false,
pinned: false,
open: function() {
$('#new_topic').fadeIn('fast', function() {
initSelector: function () {
ReactDOM.render(
React.createElement(MetacodeSelect, {
onClick: function (id) {
Create.newTopic.setMetacode(id)
Create.newTopic.hideSelector()
$('#topic_name').focus()
},
close: function () {
Create.newTopic.hideSelector()
$('#topic_name').focus()
},
metacodes: DataModel.Metacodes.models,
recent: Create.recentMetacodes,
mostUsed: Create.mostUsedMetacodes
}),
document.getElementById('metacodeSelector')
)
},
openSelector: function () {
Create.newTopic.initSelector()
$('#metacodeSelector').show()
Create.newTopic.metacodeSelectorOpen = true
$('.metacodeFilterInput').focus()
$('.selectedMetacode').addClass('isBeingSelected')
},
hideSelector: function () {
ReactDOM.unmountComponentAtNode(document.getElementById('metacodeSelector'))
$('#metacodeSelector').hide()
Create.newTopic.metacodeSelectorOpen = false
$('.selectedMetacode').removeClass('isBeingSelected')
},
setMetacode: function (id) {
Create.newTopic.metacode = id
var metacode = DataModel.Metacodes.get(id)
$('.selectedMetacode img').attr('src', metacode.get('icon'))
$('.selectedMetacode span').html(metacode.get('name'))
$.ajax({
type: 'POST',
dataType: 'json',
url: '/user/update_metacode_focus',
data: { value: id },
success: function (data) {},
error: function () {
console.log('failed to save metacode focus')
}
})
},
open: function () {
$('#new_topic').fadeIn('fast', function () {
$('#topic_name').focus()
})
Create.newTopic.beingCreated = true
@ -246,6 +311,7 @@ const Create = {
},
reset: function() {
$('#topic_name').typeahead('val', '')
Create.newTopic.hideSelector()
}
},
newSynapse: {

View file

@ -0,0 +1,168 @@
/* global $ */
import React, { Component, PropTypes } from 'react'
const ENTER_KEY = 13
const LEFT_ARROW = 37
const UP_ARROW = 38
const RIGHT_ARROW = 39
const DOWN_ARROW = 40
const Metacode = (props) => {
const { m, onClick, underCursor } = props
return (
<li onClick={() => onClick(m.id) } className={ underCursor ? 'keySelect' : '' }>
<img src={ m.get('icon') } />
<span>{ m.get('name') }</span>
</li>
)
}
class MetacodeSelect extends Component {
constructor (props) {
super(props)
this.state = {
filterText: '',
activeTab: 0,
selectingSection: true,
underCursor: 0
}
}
componentDidMount() {
const self = this
setTimeout(function() {
$(document.body).on('keyup.metacodeSelect', self.handleKeyUp.bind(self))
}, 10)
}
componentWillUnmount() {
$(document.body).off('.metacodeSelect')
}
changeFilterText (e) {
this.setState({ filterText: e.target.value, underCursor: 0 })
}
changeDisplay (activeTab) {
this.setState({ activeTab, underCursor: 0 })
}
getSelectMetacodes () {
const { metacodes, recent, mostUsed } = this.props
const { filterText, activeTab } = this.state
let selectMetacodes = []
if (activeTab == 0) { // search
selectMetacodes = filterText.length > 1 ? metacodes.filter(m => {
return m.get('name').toLowerCase().search(filterText.toLowerCase()) > -1
}) : []
} else if (activeTab == 1) { // recent
selectMetacodes = recent.map(id => {
return metacodes.find(m => m.id == id)
}).filter(m => m)
} else if (activeTab == 2) { // mostUsed
selectMetacodes = mostUsed.map(id => {
return metacodes.find(m => m.id == id)
}).filter(m => m)
}
return selectMetacodes
}
handleKeyUp (e) {
const { close } = this.props
const { activeTab, underCursor, selectingSection } = this.state
const selectMetacodes = this.getSelectMetacodes()
let nextIndex
switch (e.which) {
case ENTER_KEY:
if (selectMetacodes.length && !selectingSection) this.resetAndClick(selectMetacodes[underCursor].id)
break
case UP_ARROW:
if (selectingSection && activeTab == 0) {
close()
break
}
else if (selectingSection) {
nextIndex = activeTab - 1
this.changeDisplay(nextIndex)
break
}
nextIndex = underCursor == 0 ? selectMetacodes.length - 1 : underCursor - 1
this.setState({ underCursor: nextIndex })
break
case DOWN_ARROW:
if (selectingSection) {
nextIndex = activeTab == 2 ? 0 : activeTab + 1
this.changeDisplay(nextIndex)
break
}
nextIndex = underCursor == selectMetacodes.length - 1 ? 0 : underCursor + 1
this.setState({ underCursor: nextIndex })
break
case RIGHT_ARROW:
if (selectingSection) this.setState({ selectingSection: false })
break
case LEFT_ARROW:
if (!selectingSection) this.setState({ selectingSection: true })
break
}
}
resetAndClick (id) {
const { onClick } = this.props
this.setState({ filterText: '', underCursor: 0 })
this.changeDisplay(0)
onClick(id)
}
render () {
const { onClick, close, recent, mostUsed } = this.props
const { filterText, activeTab, underCursor, selectingSection } = this.state
const selectMetacodes = this.getSelectMetacodes()
return <div className='metacodeSelect'>
<div className='tabList'>
<div className={ activeTab == 0 ? 'active' : '' }
onClick={() => { this.changeDisplay(0) }}>
<input type='text'
className='metacodeFilterInput'
placeholder='Search...'
ref='input'
value={ filterText }
onChange={ this.changeFilterText.bind(this) } />
</div>
<div className={ activeTab == 1 ? 'active' : '' }
onClick={() => { this.changeDisplay(1) }}>
<span>Recent</span>
</div>
<div className={ activeTab == 2 ? 'active' : '' }
onClick={() => { this.changeDisplay(2) }}>
<span>Most Used</span>
</div>
</div>
<ul className='metacodeList'>
{ selectMetacodes.map((m, index) => {
return <Metacode underCursor={!selectingSection && underCursor == index}
key={m.id}
m={m}
onClick={this.resetAndClick.bind(this)} />
})}
</ul>
<div className='clearfloat'></div>
</div>
}
}
MetacodeSelect.propTypes = {
onClick: PropTypes.func.isRequired,
close: PropTypes.func.isRequired,
metacodes: PropTypes.array.isRequired,
recent: PropTypes.array.isRequired,
mostUsed: PropTypes.array.isRequired
}
export default MetacodeSelect