Skip to content

snowkeeper/tag-search

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

82 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tag-search

Add a html tag search box to your page. Uses ES6 ReactJS classes to produce a component you can consume into your react app. View Screencaps

Typical use case is to look for named anchors and generate a menu. Works well for mobile display.

There are standalone builds available in the examples directory.

There are also fully functional builds for System.js and browserify in the examples directory with full source examples.

npm i -g live-server and run live-server ./examples to view the output of different setups.

Install

jspm
jspm i npm:tag-search
npm
npm i tag-search

Example

Assuming your html looks like this:

<a name="classreference"></a>
<h3>Class Refence</h3>
... content ...
<a name="endpoints"></a>
<h3>Endpoint Api</h3>

Create your component.
All options are the default unless otherwise noted, with exception of styles and classes which are explained below.

import { render } from 'react-dom'
import SearchTags from 'tag-search'
import {Emitter} from 'events'

let emitter = new Emitter();

// add the options
let tagOpts = {
    wrapperLeftText: 'menu',
	wrapperRightText: 'search',
    mainHeader: 'Anchor',
	apiHeader: 'Method',
	searchBar: 'searchBar',
	placeholder: "quick find",
	searchList: 'searchList',
	topLink: true,
    topLinkAnchor: 'name',
    topLinkText: 'Top',
    tagSelector: 'a[name]',
    nameFromTagAttr: 'innerHTML', //default name
    linkFromTagAttr: 'name',
	contextTextUntilTag: "a[name]",
	nameFromNextTag: true,
    nameFromTag: ["H2", "H3", "H4", "H5"],
	nameFromTagSaveChildren: ':not("em, code")',
	useLocation: true,
    skipHistory: false, 
    forceSearch: 2000,
	noclasses: false,
	nostyles: false,
	styles: {
		'searchBar': {},
		'input': {},
		'inputDiv': {},
		'searchList': {},
		'ul': {},
		'li': {},
		'li:a': {},
		'li:heading': {},
		'context': {}
	},
	classes: {
		'searchBar': ' col-sm-offset-3 col-sm-9 ',
		'input': ' form-control clearable',
		'ul': ' sidebar-nav ',
	}
}

let tag = {
	tag: '.create-anchor-links :header',
    where: 'after', //default is before
    class: 'anchor'
}

render( <SearchTags reset={true} options={tagOpts} events={emitter} tag={tag} {...this.props} />, document.getElementById('anchor-search'));

reset

Resets the config instead of merge after mount.
For events add reset:true to your configuration object.

options

wrapperLeftText - {String} - the "menu" text
wrapperRightText - {String} - the "search" text
mainHeader - {String} - the header text for normal pages
apiHeader - {String} - the header text for api type pages
searchBar - {String} - ID of main div
placeholder - {String} - placeholder text
searchList - {String} - ID of search list div
topLink - {Boolean} - Show a link on top of the list
topLinkText - {String} - text of link
topLinkAnchor - {String} - anchor href to scroll to
tagSelector - {String} - selector of tags to use for search list
nameFromTagAttr - {String} - the attribute to grab the name from
linkFromTagAttr - {String} - the a href links will be generated from this attribute in each tagSelector
contextTextUntilTag - {String} - use the text until this tag is reached for the context string
nameFromNextTag - {Boolean} - get the display name from the next tag
useLocation - {Boolean} - Use window.location in place of $(document).scrollTop. Defaults to true except for mobile.
skipHistory - {Boolean} - if you provided a history object already and need to skip using for any reason
noclasses - {Boolean} - do not include any classes
nostyles - {Boolean} - do not include any styles
classes - {Object} - object of classes
styles - {Object} - object of styles
forceSearch - {Number} - The amount of time to allow for user input before the search is performed. The user input is cached until done typing. The default is to force a render at 2 seconds and start the cache over until typing is finished.

if nameFromNextTag == true || nameFromPrevTag == true

nameFromTag - {Array} - name of the tag the get display name from. first tag found wins
nameFromTagSaveChildren - {String} - if you use innerHTML you may want to include some children. It should be an acceptable JQuery selector for $(nameFromNextTag).clone().children(nameFromTagSaveChildren)

events

You can pass an event emitter as the events prop and event listeners will be attached to re-render the menu at any time. Pass any new options as the data object and they will be merged into the configuration. The new configuration is emitted back.

emitter.emit('tag-search:update', {
	nameFromPrevTag: true,
	nameFromTag: ["H2", "H3", "H4", "H5"],
	linkFromTagAttr: 'data-link',
	contextTextUntilTag: 'h*',
	useLocation: true,
    reset: true // creates a fresh config object
});
emitter.once('tag-search:options', (options) => {
	debug('new tag-search options', options)
})

Available emit events for you to use

// return emits tag-search:options
events.emit('tag-search:update', configObject);	 

// get the current configuration object
// return emits tag-search:options
events.emit('tag-search:config');

// return emits tag-search:tagged
events.emit('tag-search:tag', configObject);      

Listen for results

events.once('tag-search:options', (configObject) => {})	 
events.once('tag-search:tagged', (results) => {})	  

tag

Send a tag prop to add name tags to your page via jquery. This will add a named anchor either before or after your selected tags.

let tag = {
	tag: '.create-anchor-links :header', //jquery selector
    where: 'after', //default is before
    class: 'anchor' //class for the anchor
}

There is also an event to add tags:

events.emit('tag-search:tag', {
	tag: '.create-anchor-links :header', //jquery selector
    where: 'after', //default is before
    class: 'anchor' //class for the anchor   
});

And a response event:

events.once('tag-search:tagged', (tags) => {
    tags.success // true or false
    tags.tags // jquery array to selected elements
});
NOTE

If you pass a react-router history object as history={this.props.history} or {...this.props} then the history will be pushed as well.

Listeners

Adds document listeners for clicks and to hide the menu. Will look for selector .catchMenuClick a.

// catch menu clicks
$(document).on('click', '.catchMenuClick a', function(e) {
	thisComponent.catchMenuClick(e)
});

// hide the results when clicked outside
$(document).on('mouseup','body', function (e)
{
	thisComponent.hideSearchList(e)
})

// jump to first anchor on page that matches and give a list of matches
$(document).on('click input focus', '#' + thisComponent.state.Anchored.searchBar + ' input', function(e) {
	thisComponent.wordWait(e.target.value);		
});
// open menu on single click
$(document).on('click', '#TSWrapperLeft', function(e) {
		thisComponent.checkMenu()	
});
// show search on click
$(document).on('click', '#TSWrapperRight', function(e) {
	thisComponent.checkTyping()			
});

Default inline styles

The component will render with these inline styles by default. Add your style properties with camelCase. They will be transformed when appropriate.

exports.styles = {
	'searchBar': {
		'height': '50px',
		'position': 'fixed',
		'bottom': 0,
		'right': 0,
		'zIndex': 1002,
		'padding': 0,
		'width': '100%'
	},
	'input': {
		'width': '100%',
		'fontSize': '1.4em',
		'fontWeight': 'bold',
		'color': '#555',
		'backgroundColor': '#f7f7f7',
		'border': 'none',
		'height': '50px',
		'zIndex': 1003,
	},
	'inputDiv': {
	    'paddingTop': 0,
		'paddingRight': 0,
		'paddingBottom': 0,
		'paddingLeft': 8,
		'height': '50px',
	},
	'searchList': {
		'height': '300px',
		'margin': '-350px 15px 0 15px',
		'border': '1px solid #ccc',
		'borderBottom': 'none',
		'overflowY': 'auto',
		'backgroundColor': '#fbfbfb',
		'padding': '10px 20px',
		'display': 'none'
	},
	'ul': {
	    'fontSize': '13px',
		'listStyle': 'none',
		'lineHeight': 1.2,
		'margin': '0',
		'padding': 0,
		'position': 'relative',
		'zIndex': 2,
	},
	'li': {
		'padding': '5px 5px',
		'color': '#348dd9',
	},
	'li:a': {
		'color': '#333',
		'display': 'block',
		'padding': '5px 5px 5px 0',
	},
	'li:heading': {
		'fontSize': '1.25em',
		'textTransform': 'uppercase',
        'padding': '5px 5px',
		'color': '#348dd9',
	},
	'context': {
		'color': '#7a7a7a',
		'fontSize': '.9em',
		'display': 'block',
		'marginTop': 0,
		'height': 'auto',
	}

}

Default classes

No stylesheet is included by default.

exports.classes = {
	'searchBar': '',
	'input': '',
	'inputDiv': '',
	'searchList': '',
	'ul': '',
	'li': '',
	'li:a': '',
	'li:heading': '',
	'context': ''
}

CAUTION If you plan on using your own classes, either through stylesheet or object, you must send nostyles: true or a modified styles object with the styles you want removed (or a blank for each property). If you do not then an inline style will take precedence.

Screencaps

Desktop

Imgur

Mobile

Imgur

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published