npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

tag-search

v0.0.22

Published

search and list html tags like named anchor links

Downloads

9

Readme

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