GithubHelp home page GithubHelp logo

metalsmith-navigation's Introduction

metalsmith-navigation [BETA]

A Metalsmith plugin to generate navigation.

Installation

$ npm install metalsmith-navigation

CLI Usage

Install via npm and then add the metalsmith-navigation key to your metalsmith.json plugins.

{
  "plugins": {
    "metalsmith-navigation": {
        "navConfigs": {},
        "navSettings": {}
    }
  }
}

Javascript Usage

Pass options to the markdown plugin and pass it to Metalsmith with the use method:

var navigation = require('metalsmith-navigation');

// default values shown
var navConfigs = {

    // nav config name
    header: {

        /*
        * sortby function or property name
        * function example: function(navNode){ return navNode.getValueToSortBy(); }
        */
        sortBy: false,

        /*
        * if true nodes will be sorted by path before sortBy
        * if false the sorting will not be stable unless ALL nodes have a unique sort value
        */
        sortByNameFirst: true,

        /*
        * to be included in this nav config, a file's metadata[filterProperty] must equal filterValue
        * ex:
        *   navConfigs = {
        *       footer: {
        *           filterProperty: 'my_nav_group'
        *       }
        *   }
        *   file is only added to footer nav when files[path].my_nav_group == 'footer' OR files[path].my_nav_group.indexOf('footer') !== -1
        */
        filterProperty: false,

        /*
        * if false, nav name (navConfigs key) is used instead
        * ex:
        *   navConfigs = {
        *       footer: {
        *           filterValue: 'footer' // default value used if !navConfigs.footer
        *       }
        *   }
        * if files[path][filterProperty] is a string that equals or an array that contains filterValue it will be included
        */
        filterValue: false,

        /*
        * the file object property that breadcrumb array is assigned to on each file object
        * breadcrumbs not generated or set if false
        * typically only one navConfig should generate breadcrumbs, often one specifically for them
        */
        breadcrumbProperty: 'breadcrumb_path',

        /**
        * each file's full nav path will be assigned to that file's metadata object using the value of propertyPath as the key.
        * only assigned to file metadata objects of files included this navConfig
        * if false will not be assigned to any objects.
        * ex:
        *   navConfigs: {
        *       footer: {
        *           pathProperty: 'my_nav_path'
        *       }
        *   };
        *
        *   // in the template of services/marketing/email.html
        *   // my_nav_path == 'services/marketing/email.html'
        *
        * note: each navConfig can have a different pathProperty as file paths may be differerent in different nav configs.
        */
        pathProperty: 'nav_path',

        /**
        * the file object property that an array of nav child nodes will be assigned to
        */
        childrenProperty: 'nav_children',

        /*
        * if a file and sibling dir have matching names the file will be used as the parent in the nav tree
        * ex: /foo /foo.html
        */
        mergeMatchingFilesAndDirs: true,

        /*
        * if ALL dirs should be included as nav nodes
        */
        includeDirs: false,
    },

    // ... any number of navConfigs may be created

};

// default values shown
var navSettings = {
    /*
    * metadata key all navs will be assigned to metatdata[navListProperty]
    * not set if false
    */
    navListProperty: 'navs',

    /*
    * if true, paths will be transformed to use metalsmith-permalinks
    * metalsmith-permalinks must be run before metalsmith-navigation
    */
    permalinks: false,
};

var nav = navigation(navConfigs, navSettings);

metalsmith.use(nav);

// OR use CLI syntax with single param

var settings = {
    navConfigs: navConfigs,
    navSettings: navSettings,
};
var nav = navigation(settings);

License

MIT

metalsmith-navigation's People

Contributors

comfycode avatar glen-84 avatar jmjf avatar justblackbird avatar kwizzn avatar martinvehmas avatar sgtpooki avatar sombekke avatar taneltm avatar unstoppablecarl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

metalsmith-navigation's Issues

Metalsmith-navigation is not cross-platform

The replaceDirsSeparator function is unnecessary, does not actually work for windows machines, and is a waste of time for all non-windows machines.

For future reference, you would need to change var newKey = oldKey.replace(path.sep, "/"); to var newKey = oldKey.replace('\\\\', "/"); for this to properly replace the windows path separators. However, this still leaves an issue where you are not replacing all of the path separators, only the first ones. Usually, you would pass the 'g' flag to the replace function, but that is not supported on node, so you would actually need to do something like this:

//no need to replace "/" with "/", so just check for "\\" and replace all occurrences.
var regex = /\\/g;
var newKey = oldKey.replace(regex, "/");  

And finally, there is no reason for us to even do all this work, because node will handle this all for us:

var path = require('path');
var filePathArray = filePath.split(path.sep);
var newFilePath = filePathArray.join('/'); // join with whatever you like here

This is much cleaner, more maintainable, and more future proof. If windows/mac/nix changes their file separators to @.. all joyent (or io.js) would need to do is update their path.sep implementation for that OS, and your code would still be good to go.

Breadcrumb custom labels

Hi,

Is it possible to have custom labels for breadcrumb text like fetching from title or any other metadata key?

For example:

File Breadcrumb
main/sports-primary/index.html Home > Sports
main/sports-primary/soccer/index.html Home > Sports > Football Video
main/sports-primary/basketball/index.html Home > Sports > Basketball Video

Can't make 'sort_by' option to work

Hi,
Even after chaining the sot_by values in the /examples/generic folder the files keep showing by their file names.

Any thoughts?

Thanks you for this great plugin - Its very helpful.

children missing

Hey,

I have a file structure setup like this:

image

But my navs object is missing the children completely.

image

My settings are pretty basic:

var navConfigs = {
  primary: {
    sortBy: 'nav_sort',
    filterProperty: 'nav_groups',
    includeDirs: true
  }
};

var navSettings = {
  navListProperty: 'navs',
  permalinks: true
};

Did anyone else face any similar issue?

Can the navigation tree be passed to handlebar templates?

I'm having a tough time getting this working with the aformentioned combination.

I have the following config in grunt:

    'metalsmith-markdown': {},
    'metalsmith-navigation': {
        'navConfigs': {},
        'navSettings': {}
    },
    'metalsmith-layouts': {
        engine: 'handlebars',
        directory: 'src/site/layout',
        partials: 'src/site/layout',
        default: 'page.html'
    }

In my layout templates I want to iterate through the navigation generated by metalsmith-navigation and produce a menu in a partial. Is this possible or am I way off track?

Note: the 'navs' object inside the templates is an empty object.

Current page flag

Is there a way to identify the page in a navigation list as the page where the navigation list is being rendered? For example: on the "about" page, I want to change the style of the link to the "about" page in the navigation.

Relative navigation not working with permalinks?

Not sure if I'm missing something but I can't get the relative navigation working with permalinks turned on.

I've downloaded the example and tried to get that working too but it doesn't spit out anything under the current page title in the _nav_relative.html file.

What would I need to do to get this to work in the example?

Detecting active on index.html

I'm using metalsmith-permalinks. Here are pertinent excerpts from build.js

var navConfigs = {
        primary: {
            sortBy: 'navOrder',
            includeDirs: true,
            filterProperty: 'navGroups'
        }
    },
    navSettings = {
        navListProperty: 'navs',
        permalinks: true
    };
...
    .use(permalinks({
        relative: false
    }))
    // process navigation tree
    .use(navigation(navConfigs, navSettings))

Trying to sort through how to determine the active menu item, I built the following template (simplified to focus on what seems to be the important data) and wrapped it in an {{#each navs.primary}} block.

testMenu.hbs

{{#if file}}
    FILE: name: {{name}} | path: {{path}} | @root.nav_path: {{@root.nav_path}} | @root.path: {{@root.path}}
{{else}}
    !FILE: {{name}}
{{/if}}
{{#if children}}
    CHILD:
    {{#each children}}
        {{> testMenu}}
    {{/each}}
    END-CHILD
{{/if}}

After examining the output on a few test pages, I decided that if path == @root.path, the current page is active. Here's a sample from the child page for products/product1.

NAV
        FILE: name:index.html | path: index.html | @root.nav_path: products/product1/index.html | @root.path: products/product1
        FILE: name:about | path: about | @root.nav_path: products/product1/index.html | @root.path: products/product1
        !FILE: products
        CHILD:
                FILE: name:product1 | path: products/product1 | @root.nav_path: products/product1/index.html | @root.path: products/product1
        END-CHILD
END-NAV

Which is all well and good and everything is consistent and easy until I look at the site's index.html. For index.html, I get

FILE: name: index.html | path: index.html | @root.nav_path: index.html | @root.path: 

My guess is transformPermalinks() is not stripping the file name from the root index.html. I'm wondering if there's a (sane) way to make it do that so testing for active is consistent for all pages. Adding the following into my local copy after line 409 seems to fix this without harming anything else, but I'm not sure if you see other possible consequences to important nav metadata. It seems like the only possible hole is the node.parent, which probably shouldn't exist for the root index.html anyway.

            } else if ((node.depth == 1) && (node.path == 'index.html')){
                node.path = "";
            }

If this seems reasonable, let me know and I can set up a PR.

Missing CLI usage example

The Readme.md currently has an empty object for "metalsmith-navigation".

I thought that I would pass two keys: "navConfigs" and "settings", but it seems that I should only be passing navConfigs. How would I also pass settings?

Permalinks with mergeMatchingFilesAndDirs doesn't sort as expected.

In the example, swap the nav_order value on services.md and any other file. build-permalinks. The services item is still last.

In my test project, adding some console.logs to makeArraySortByFunc() as follows:

var makeArraySortByFunc = function(sortBy, usePermalinks){
    usePermalinks = usePermalinks || false;

    if(typeof sortBy !== 'function'){
        var sortByKey = sortBy;

        sortBy = function(node){
            if(node.file){
                node.sort = node.file[sortByKey];
/**/            console.log("sortBy:", node.name, node.file.navOrder);
                return node.file[sortByKey];
            }
        };

    } else {
        var sortByFunc = sortBy;
        sortBy = function(node){
            // first param is the expected metalsmith file object, node objecty is available as second param
            return sortByFunc(node.file, node);
        };
    }

    if(usePermalinks){
        var _sortBy = sortBy;
        sortBy = function(node){

            // if node is dir and has 1 child sort using that child node instead of the dir
            if(!node.file && node.children.length === 1){
/**/            console.log("parent:", node.name); 
                node = node.children[0];
/**/            if (!node.file) {console.log(node.name, "has no file");}
            }
            return _sortBy(node);
        };
    }

    return sortBy;
};

gets the following output

parent: about
sortBy: index.html 2
parent: contact
sortBy: index.html 6
parent: custom
sortBy: index.html 5
sortBy: index.html 1
parent: leadership
sortBy: index.html 3
sortBy: index.html 2                    <-- lodashSortBy happens here, I'm betting
sortBy: index.html 3
sortBy: index.html 5
sortBy: index.html 6
sortBy: index.html 4                    <-- products/index.html, no "parent" --> not permalink in sort
parent: product1
sortBy: index.html 3                    <-- then products/product1/index.html

where products is a directory at the same level as index.html, about, etc. with a products.md sibling, and products1 is a child of products.

Turning off permalinks, the output is

sortBy: about.html 2
sortBy: contact.html 6
sortBy: custom.html 5
sortBy: index.html 1
sortBy: leadership.html 3
sortBy: products.html 4
sortBy: product1.html 3

But here it's sorting the products.html file, which is pushed to build, not the directory node.

I think the issue is that permalinks builds products.md to products/index.html. That happens before navigation runs, so navigation doesn't find a mergeable sibling. makeArraySortByFunc() requires a single child to recognize a permalink folder, which isn't true because products has product1 as a child too. And we end up not recognizing that products is a permalink with additional children.

Somehow, the permalink dir node needs to get its index.html file attached regardless of how many children it has.

The rationale for this is that I need to apply metadata to products (sort key, menu item title, etc.) so I'm loading the metadata onto the sibling. The menu item for products is an href="#" dropdown item (using Bootstrap), so the page will never be accessed, only the children.

It's late. I'll look at this more tomorrow and update here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.