1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 12:12:48 +01:00

Added fuzzy search using fuse.js

This commit is contained in:
Hillel Coren 2016-02-28 22:43:43 +02:00
parent d465c0d019
commit 6a9b2130c5
8 changed files with 227 additions and 178 deletions

View File

@ -95,13 +95,14 @@ module.exports = function(grunt) {
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.no.min.js',
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.es.min.js',
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.sv.min.js',
'public/vendor/typeahead.js/dist/typeahead.min.js',
'public/vendor/typeahead.js/dist/typeahead.jquery.min.js',
'public/vendor/accounting/accounting.min.js',
'public/vendor/spectrum/spectrum.js',
'public/vendor/jspdf/dist/jspdf.min.js',
'public/vendor/moment/min/moment.min.js',
'public/vendor/moment-timezone/builds/moment-timezone-with-data.min.js',
'public/vendor/stacktrace-js/dist/stacktrace-with-polyfills.min.js',
'public/vendor/fuse.js/src/fuse.min.js',
//'public/vendor/moment-duration-format/lib/moment-duration-format.js',
//'public/vendor/handsontable/dist/jquery.handsontable.full.min.js',
//'public/vendor/pdfmake/build/pdfmake.min.js',

View File

@ -74,8 +74,7 @@ class AccountRepository
{
$data = $this->getAccountSearchData();
$type = trans('texts.navigation');
$data[$type] = $this->getNavigationSearchData();
$data['navigation'] = $this->getNavigationSearchData();
return $data;
}
@ -83,10 +82,10 @@ class AccountRepository
private function getAccountSearchData()
{
$data = [
trans('texts.clients') => [],
trans('texts.contacts') => [],
trans('texts.invoices') => [],
trans('texts.quotes') => [],
'clients' => [],
'contacts' => [],
'invoices' => [],
'quotes' => [],
];
$clients = Client::scope()
@ -95,26 +94,31 @@ class AccountRepository
foreach ($clients as $client) {
if ($client->name) {
$data[trans('texts.clients')][] = [
$data['clients'][] = [
'value' => $client->name,
'tokens' => explode(' ', $client->name),
'url' => $client->present()->url,
];
}
foreach ($client->contacts as $contact) {
$data[trans('texts.contacts')][] = [
'value' => $contact->getDisplayName(),
'tokens' => explode(' ', $contact->getFullName() . ' ' . $contact->email),
'url' => $client->present()->url,
];
if ($contact->getFullName()) {
$data['contacts'][] = [
'value' => $contact->getDisplayName(),
'url' => $client->present()->url,
];
}
if ($contact->email) {
$data[trans('texts.contacts')][] = [
'value' => $contact->email,
'url' => $client->present()->url,
];
}
}
foreach ($client->invoices as $invoice) {
$entityType = $invoice->getEntityType();
$data[trans("texts.{$entityType}s")][] = [
$data["{$entityType}s"][] = [
'value' => $invoice->getDisplayName() . ': ' . $client->getDisplayName(),
'tokens' => explode(' ', $invoice->invoice_number . ' ' . intval($invoice->invoice_number) . ' ' . $client->getDisplayName()),
'url' => $invoice->present()->url,
];
}

View File

@ -14,7 +14,7 @@
"underscore": "1.7.0",
"jspdf": "1.0.272",
"bootstrap-datepicker": "1.4.0",
"typeahead.js": "0.9.3",
"typeahead.js": "0.11.1",
"accounting": "0.3.2",
"spectrum": "1.3.4",
"d3": "3.4.11",
@ -25,7 +25,8 @@
"moment-timezone": "~0.4.0",
"quill": "~0.20.0",
"datetimepicker": "~2.4.5",
"stacktrace-js": "~1.0.1"
"stacktrace-js": "~1.0.1",
"fuse.js": "~2.0.2"
},
"resolutions": {
"jquery": "~1.11"

File diff suppressed because one or more lines are too long

125
public/css/built.css vendored
View File

@ -2054,89 +2054,76 @@ See http://bgrins.github.io/spectrum/themes/ for instructions.
.combobox-container:not(.combobox-selected) .fa-times {
display: none;
}
.twitter-typeahead .tt-query,
.twitter-typeahead .tt-hint {
margin-bottom: 0;
/**********************************************************
* typeahead.js v0.11.1 - twitter bootstrap v3.3.5 *
**********************************************************/
/*root typeahead class*/
.twitter-typeahead {
display: inherit !important;
width: 100%;
}
.tt-dropdown-menu {
min-width: 160px;
margin-top: 2px;
padding: 5px 0;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0,0,0,.2);
*border-right-width: 2px;
*border-bottom-width: 2px;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
box-shadow: 0 5px 10px rgba(0,0,0,.2);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
.twitter-typeahead .tt-input[disabled] {
background-color : #eeeeee !important;
}
.tt-suggestion {
display: block;
/*Added to input that's initialized into a typeahead*/
.twitter-typeahead .tt-input {
}
/*Added to hint input.*/
.twitter-typeahead .hint {
}
/*Added to menu element*/
.twitter-typeahead .tt-menu {
width: 100%;
max-height: 500px;
overflow-y: none;
border: 1px solid #cccccc;
border-radius:4px;
-moz-box-shadow: 12px 14px 30px -7px #616161;
-webkit-box-shadow: 12px 14px 30px -7px #616161;
box-shadow: 12px 14px 30px -7px #616161;
}
/*Added to dataset elements*/
.twitter-typeahead .tt-dataset {
}
/*dded to suggestion elements*/
.twitter-typeahead .tt-suggestion {
padding: 3px 20px;
white-space: nowrap;
}
.tt-suggestion.tt-is-under-cursor {
color: #fff;
background-color: #0081c2;
background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
background-image: -o-linear-gradient(top, #0088cc, #0077b3);
background-image: linear-gradient(to bottom, #0088cc, #0077b3);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0)
/*Added to menu element when it contains no content*/
.twitter-typeahead .tt-empty {
background-color: white;
}
.tt-suggestion.tt-is-under-cursor a {
color: #fff;
/*Added to menu element when it is opened*/
.twitter-typeahead .tt-open {
background-color: white;
}
.tt-suggestion p {
margin: 0;
/*Added to suggestion element when menu cursor moves to said suggestion*/
.twitter-typeahead .tt-suggestion:hover,
.twitter-typeahead .tt-suggestion:focus,
.twitter-typeahead .tt-cursor {
cursor: hand !important;
background-color: #337ab7;
color: white;
}
/*
.tt-hint {
padding: 6px 12px;
}
*/
/*Added to the element that wraps highlighted text*/
.twitter-typeahead .tt-highlight {
.twitter-typeahead .tt-hint
{
display: block;
height: 34px;
padding: 6px 12px;
font-size: 14px;
line-height: 1.428571429;
border: 1px solid transparent;
border-radius:4px;
}
.twitter-typeahead .hint-small
{
height: 30px;
padding: 5px 10px;
font-size: 12px;
border-radius: 3px;
line-height: 1.5;
}
.twitter-typeahead .hint-large
{
height: 45px;
padding: 10px 16px;
font-size: 18px;
border-radius: 6px;
line-height: 1.33;
}
body { background: #f8f8f8 !important;
font-family: 'Roboto', sans-serif;

View File

@ -1,84 +1,71 @@
.twitter-typeahead .tt-query,
.twitter-typeahead .tt-hint {
margin-bottom: 0;
/**********************************************************
* typeahead.js v0.11.1 - twitter bootstrap v3.3.5 *
**********************************************************/
/*root typeahead class*/
.twitter-typeahead {
display: inherit !important;
width: 100%;
}
.tt-dropdown-menu {
min-width: 160px;
margin-top: 2px;
padding: 5px 0;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0,0,0,.2);
*border-right-width: 2px;
*border-bottom-width: 2px;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
box-shadow: 0 5px 10px rgba(0,0,0,.2);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
.twitter-typeahead .tt-input[disabled] {
background-color : #eeeeee !important;
}
.tt-suggestion {
display: block;
/*Added to input that's initialized into a typeahead*/
.twitter-typeahead .tt-input {
}
/*Added to hint input.*/
.twitter-typeahead .hint {
}
/*Added to menu element*/
.twitter-typeahead .tt-menu {
width: 100%;
max-height: 500px;
overflow-y: none;
border: 1px solid #cccccc;
border-radius:4px;
-moz-box-shadow: 12px 14px 30px -7px #616161;
-webkit-box-shadow: 12px 14px 30px -7px #616161;
box-shadow: 12px 14px 30px -7px #616161;
}
/*Added to dataset elements*/
.twitter-typeahead .tt-dataset {
}
/*dded to suggestion elements*/
.twitter-typeahead .tt-suggestion {
padding: 3px 20px;
white-space: nowrap;
}
.tt-suggestion.tt-is-under-cursor {
color: #fff;
background-color: #0081c2;
background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
background-image: -o-linear-gradient(top, #0088cc, #0077b3);
background-image: linear-gradient(to bottom, #0088cc, #0077b3);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0)
/*Added to menu element when it contains no content*/
.twitter-typeahead .tt-empty {
background-color: white;
}
.tt-suggestion.tt-is-under-cursor a {
color: #fff;
/*Added to menu element when it is opened*/
.twitter-typeahead .tt-open {
background-color: white;
}
.tt-suggestion p {
margin: 0;
/*Added to suggestion element when menu cursor moves to said suggestion*/
.twitter-typeahead .tt-suggestion:hover,
.twitter-typeahead .tt-suggestion:focus,
.twitter-typeahead .tt-cursor {
cursor: hand !important;
background-color: #337ab7;
color: white;
}
/*
.tt-hint {
padding: 6px 12px;
}
*/
/*Added to the element that wraps highlighted text*/
.twitter-typeahead .tt-highlight {
.twitter-typeahead .tt-hint
{
display: block;
height: 34px;
padding: 6px 12px;
font-size: 14px;
line-height: 1.428571429;
border: 1px solid transparent;
border-radius:4px;
}
.twitter-typeahead .hint-small
{
height: 30px;
padding: 5px 10px;
font-size: 12px;
border-radius: 3px;
line-height: 1.5;
}
.twitter-typeahead .hint-large
{
height: 45px;
padding: 10px 16px;
font-size: 18px;
border-radius: 6px;
line-height: 1.33;
}

View File

@ -68,6 +68,8 @@ We're using the [Git-Flow](http://nvie.com/posts/a-successful-git-branching-mode
* [patricktalmadge/bootstrapper](https://github.com/patricktalmadge/bootstrapper) - Laravel Twitter Bootstrap Bundle
* [danielfarrell/bootstrap-combobox](https://github.com/danielfarrell/bootstrap-combobox) - A combobox plugin
* [eternicode/bootstrap-datepicker](https://github.com/eternicode/bootstrap-datepicker) - A datepicker for @twitter bootstrap
* [twitter/typeahead.js](https://github.com/twitter/typeahead.js) - a fast and fully-featured autocomplete library
* [krisk/Fuse](https://github.com/krisk/Fuse) - Lightweight fuzzy-search, in JavaScript
* [knockout/knockout](https://github.com/knockout/knockout) - Knockout makes it easier to create rich, responsive UIs with JavaScript
* [rniemeyer/knockout-sortable](https://github.com/rniemeyer/knockout-sortable) - A Knockout.js binding to connect observableArrays with jQuery UI sortable functionality
* [bpampuch/pdfmake](https://github.com/bpampuch/pdfmake) - Client/server side PDF printing in pure JavaScript

View File

@ -259,37 +259,84 @@
}
function showSearch() {
$('#search').typeahead('setQuery', '');
console.log('showSearch..');
//$('#search').typeahead('setQuery', '');
$('#navbar-options').hide();
$('#search-form').show();
if (window.hasOwnProperty('searchData')) {
if (window.hasOwnProperty('loadedSearchData')) {
console.log('has data');
$('#search').focus();
} else {
trackEvent('/activity', '/search');
$.get('{{ URL::route('getSearchData') }}', function(data) {
console.log(data);
window.searchData = true;
var datasets = [];
for (var type in data)
window.loadedSearchData = true;
$('#search').typeahead({
hint: true,
highlight: true,
},
{
if (!data.hasOwnProperty(type)) continue;
datasets.push({
name: type,
header: '&nbsp;<b>' + type + '</b>',
local: data[type]
});
}
if (datasets.length == 0) {
return;
}
$('#search').typeahead(datasets).on('typeahead:selected', function(element, datum, name) {
name: 'data',
display: 'value',
source: searchData(data['clients']),
templates: {
header: '&nbsp;<b>{{ trans('texts.clients') }}</b>'
}
},
{
name: 'data',
display: 'value',
source: searchData(data['contacts']),
templates: {
header: '&nbsp;<b>{{ trans('texts.contacts') }}</b>'
}
},
{
name: 'data',
display: 'value',
source: searchData(data['invoices']),
templates: {
header: '&nbsp;<b>{{ trans('texts.contacts') }}</b>'
}
},
{
name: 'data',
display: 'value',
source: searchData(data['quotes']),
templates: {
header: '&nbsp;<b>{{ trans('texts.quotes') }}</b>'
}
},
{
name: 'data',
display: 'value',
source: searchData(data['navigation']),
templates: {
header: '&nbsp;<b>{{ trans('texts.navigation') }}</b>'
}
}).on('typeahead:selected', function(element, datum, name) {
window.location = datum.url;
}).focus().typeahead('setQuery', $('#search').val());
}).focus();
//.typeahead('setQuery', $('#search').val());
});
}
}
function searchData(data) {
return function findMatches(q, cb) {
var options = {
keys: ['value'],
}
var fuse = new Fuse(data, options);
var matches = fuse.search(q);
cb(matches);
}
};
function hideSearch() {
$('#search-form').hide();
$('#navbar-options').show();