Coder Social home page Coder Social logo

navigasmic's Introduction

Navigasmic

Gem Version Build Status Maintainability Test Coverage License

Semantic navigation; a semantic way to build beautifully simple navigation structures in Rails views or configuration.

The Story

Most of the navigation styles I've done over the years pretty much boil down to this idea: Use simple markup, and do the rest with CSS (and Javascript if you need it). Because of that the default markup is beautifully simple.

Ok, so navigation is easy right? Until you start managing active, disabled, and hidden states, and more if you may need them. This can quickly become a mess, and all too often it just stays in the views with whatever logic tossed on top of it as people go. I've seen it too many times, and I wanted to do something about it.

These where the core goals:

  • be simple
  • be easily customizable
  • handle active/highlighted states
  • handle disabled states
  • be pleasant to use
  • uses less code to create than it generates

And working with gvarela at the time, we wrote a DSL that met those requirements:

semantic_navigation :primary do |n|
  n.group "Blog" do
    n.item "Articles", "/blog/posts", highlights_on: "/my_awesome_blog"
    n.item "Links", "/blog/links", disabled_if: proc { !logged_in? }
    n.item "My Portfolio" # auto links to the my_portfolio_path if it exists
  end
end

Since we wanted something that allowed for customization we ended up using the Builder Pattern -- the way Rails uses form builders basically. There are some builders that are provided, and these builders can be configured, extended or replaced if you need more custom markup -- there's more on how to do that stuff below.

Installation

Include the gem in your Gemfile and bundle to install the gem.

gem "navigasmic"

You can also get the initializer by running the install generator.

rails generate navigasmic:install

Usage

Navigasmic allows you to define navigation in two ways. The first is directly in your views (in a partial, or layout for instance), and the second is via a global configuration similar to how simple-navigation works.

Defining Navigation in Initializer

config.semantic_navigation :primary do |n|
  n.group "Blog", class: "blog" do
    n.item "Articles", controller: "/blog/posts"
    n.item "Links", controller: "/blog/links"
  end
  n.group "Info" do
    n.item "Me", "/about", title: "The Awesomeness That Is"
    n.item "My Portfolio"
  end
end

Rendering Navigation (based on navigation defined in initializer)

The semantic_navigation method in Navigasmic provides a single name for defining and rendering navigation. You can use this method in your layouts, views, and partials to render navigation structures that you've defined in the initializer.

<%= semantic_navigation :primary, class: 'my-navigation' %>

Definition Navigation / Rendering in Views

While it's nice to be able to define navigation in the initializer, it's also nice to be able to prototype out some navigation in the views directly. You can do this by simply passing a block to the semantic_navigation helper. By passing it a block you're able to define the navigation structure easily and quickly.

ERB

<%= semantic_navigation :primary, builder: Navigasmic::Builder::ListBuilder, class: "my-navigation" do |n| %>
  <% n.group "Blog", class: "blog" do %>
    <li>Custom Node</li>
    <% n.item "Articles", controller: "/blog/posts" %>
    <% n.item "Links", controller: "/blog/links" %>
  <% end %>
  <% n.group "Info" do
       n.item "Me", "/about", title: "The Awesomeness That Is"
       n.item "My Portfolio"
     end %>
<% end %>

HAML

= semantic_navigation :primary, config: :bootstrap, class: "my-navigation" do |n|
  - n.group "Blog", class: "blog" do
    %li Custom Node
    - n.item "Articles", controller: "/blog/posts"
    - n.item "Links", controller: "/blog/links"
  - n.group "Info" do
    - n.item "Me", "/about", title: "The Awesomeness That Is"
    - n.item "My Portfolio"

Configuring

If you ran the install generator you should have a navigasmic.rb file in your initializers. This file has several examples and some more documentation. It can be used to define navigation structures as well as create named configurations for each builder.

When you invoke the semantic_navigation method you can provide which builder you want to use, and the named configuration for that builder. By defining these builder specific configurations you'll be able to render navigation differently in different parts of your site using the same builder. This allows for the greatest flexibility.

Bootstrap Support

Twitter Bootstrap is pretty awesome, so it's worth supporting. There's a configuration that's provided in the initializer that allows for nice bootstrap support. It handles nav-pills, nav-tabs, and the navbar structure.

Options

There's several options that you can pass to the item method that dictate the state of a given navigation item. You can tell it what to highlight on, if it's disabled and when, and if it should be hidden entirely.

All of the options allow for passing a proc. In the examples below procs are used on anything that needs to happen within the view scope. This is especially useful when you define the navigation structure in an initializer. If you want to check if a user is logged in, those things need to happen within the view scope, so if you're defining your navigation in a view scope you don't need to use a proc, but if you're using the initializer you will.

Passing Links

You can pass links to the item method in a few ways. You can just provide a controller (and/or action) in the options, you can pass a first argument, or you can explicitly call out what the link options are. Here are some examples:

n.item "Articles", controller: "/blog/posts", class: "featured"
n.item "Articles", controller: "/blog/posts", action: "index", class: "featured"
n.item "Articles", "/blog/posts", class: "featured"
n.item "Articles", { controller: "/blog/posts" }, class: "featured"
n.item "Articles", class: "featured", link: { controller: "/blog/posts" }

You can take this much further by matching specific url options. Here's some examples that would match to specific blog posts (they will also only highlight on the given record):

n.item "Article", controller: "/blog/posts", action: "show", id: "42"
n.item "Article", class: "featured", link: { controller: "/blog/posts", action: "show", id: "42" }

Note that we're passing a string for the posts id. That's because when the param comes in and is compared against the link options you provided, the types need to match.

If you don't provide a link, Navigasmic attempts to find a path helper from the label. In the following example we only provide the label, but if I've defined a route (E.g. match "/portfolio" => "portfolio#index", as: "my_portfolio") it will automatically use the my_porfolio_path path helper.

n.item "My Portfolio" # Yeah auto link!

Setting highlighted / active states

Highlight rules allows for passing an array containing any of/or a Boolean, String, Regexp, Hash or Proc. The following examples will highlight:

n.item "On the /my_thoughts path, and on Mondays", "/blog/posts", highlights_on: ["/my_thoughts", proc { Time.now.wday == 1 }]
n.item "On any action in BlogController", highlights_on: [{ controller: "blog" }]
n.item "On any path beginning with 'my_'", highlights_on: /^\/my_/
n.item "Only on '/my_thoughts'", highlights_on: "/my_thoughts"
n.item "When the highlight param is set", highlights_on: proc { params[:highlight].present? }

Disabling

Disable rules allow for you to pass a Boolean or Proc. The following examples will be disabled:

n.item "On Tuesdays, and when not logged in", disabled_if: proc { Time.now.wday == 2 || !logged_in? }
n.item "Never", disabled_if: false
n.item "Always", disabled_if: true

Hiding

Hide rules allow for you to pass a Boolean or Proc. The following examples will be hidden:

n.group "On Tuesdays, and when not logged in", hidden_unless: proc { Time.now.wday != 2 && logged_in? } do
  n.item "When not logged in", hidden_unless: proc { logged_in? }
  n.item "Never", hidden_unless: false
  n.item "Always", hidden_unless: true
end

Builders

Navigasmic comes with a few builders by default. Here's a breakdown of what's available, and what each one does.

  • Navigasmic::Builder::ListBuilder
    Builds an HTML structure of UL and LI tags. Useful for most navigation structured rendered in markup.
  • Navigasmic::Builder::MapBuilder
    Builds an XML structure that use the Sitemaps XML format
  • Navigasmic::Builder::CrumbBuilder (incomplete)
    Builds an HTML structure of A tags based on the first highlighted item it finds and up. Useful for breadcrumbs.

ListBuilder Options

The ListBuilder is the default builder (unless otherwise specified in the initializer). It builds a UL/LI structure that's pretty easy to style and work with.

  • excluded_keys - Array: Allows specifying keys that are ignored in options (you may want to ignore keys used by other builders.) Default: [:map]
  • wrapper_tag - Symbol (or String): Tag used for the top level element. Default: :ul
  • group_tag - Symbol (or String): Tag used for wrapping groups. Default: :ul
  • item_tag - Symbol (or String): Tag used for wrapping specific items. Default: :li
  • wrapper_class - String: The classname that will be applied to the top level element. Default: "semantic-navigation"
  • item_class - String: The classname that will be applied to any item by default. Default: nil
  • has_nested_class - String: The classname that will be applied to any group (or item with nested items). Default: "with-group"
  • is_nested_class - String: The classname that will be applied to any nested items (within a group or item). Default: "with-group"
  • disabled_class - String: The classname that will be applied to disabled items. Default: "disabled"
  • highlighted_class - String: The classname that will be applied to items that should be highlighted. Default: "active"
  • label_generator - Proc: Called when inserting labels into items or groups. Default: proc { |label, options, has_link, has_nested| "<span>#{label}</span>" }
  • link_generator - Proc: Called when generating links. Default: proc { |label, link, link_options, is_nested| link_to(label, link, options.delete(:link_html)) }

MapBuilder Options

The MapBuilder is used for generate XML sitemaps that follow the protocol laid out by the Sitemaps XML format.

  • excluded_keys - Array: Allows specifying keys that are ignored in options (you may want to ignore keys used by other builders.) Default: []
  • option_namespace - Symbol (or String): Option key that holds the map specific options (eg. :changefreq, :priority, :lastmod etc.) Default: :map
  • wrapper_tag - Symbol (or String): Tag used for the top level element. Default: :urlset
  • item_tag - Symbol (or String): Tag used for wrapping specific items (groups are not used in this builder.) Default: :url

A simple example of using the MapBuilder -- create a [action].xml.builder view, and add the following:

xml.instruct!
xml << semantic_navigation(:primary, builder: Navigasmic::Builder::MapBuilder)

License

Licensed under the MIT License

Copyright 2019 jejacks0n

Make Code Not War

navigasmic's People

Contributors

andyundso avatar gdott9 avatar gucki avatar hansondr avatar jejacks0n avatar jondoveston avatar jsmestad avatar kevinold avatar kevmoo avatar mikepack avatar pengwynn avatar sborsje avatar shekibobo avatar subosito avatar vincecima 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

navigasmic's Issues

Namespaces and Links

Im using the following code to generate a link to Admin::BlogsController#new
<%= n.item t('navigation.admin.blog.new'), :link_to => '/admin/blogs/new', :hidden_unless => proc { current_user.try(:admin?) } %>
I have also tried the following:
<%= n.item t('navigation.admin.blog.new'), :link_to => { :controller => '/admin/blogs', :action => 'new' }, :hidden_unless => proc { current_user.try(:admin?) } %>

Both result in the link following html output:

  • New Blog
  • Am i doing something wrong?

    Error in Ruby example in the README

    I think there is an error in the README, under Usage / Defining Navigation in Initializer:

    config.semantic_navigation :primary do |n|
      n.group 'Blog', class: 'blog' do
        '<li>Custom Node</li>'.html_safe # <----------- THIS LINE
        n.item 'Articles', controller: '/blog/posts'
        n.item 'Links', controller: '/blog/links'
      end
    end
    

    How is the line marked above supposed to work? It looks to me that it just creates a string that gets discarded right away. It tried the code and indeed, "Custom Node" did not appear in the output. (It did work the the HAML version below it)

    Did I miss something or is it a bug in the README? If it is a bug, how would one achieve the same effect?

    Breadcrumbs

    From README.md:
    Navigasmic::Builder::CrumbBuilder (incomplete)

    Is it still incomplete or is the readme outdated?

    We should do something about it either way: complete the code or fix the readme.

    Items are being duplicated on render

    I am trying to set up a managed navigation menu. I have Menu and Page models that determine what links should be shown in the menu.

        class Menu < ActiveRecord::Base
          attr_accessible :name, :slug
    
          has_many :pages
        end
    
        class Page < ActiveRecord::Base
          belongs_to :menu
          attr_accessible :link, :order, :title, :menu_id
        end
    

    I am looping through the pages and having navigasmic render them.

        <div id="navigation">
            <%= semantic_navigation :primary do |n| %>
                <% Menu.find_by_slug(:primary).pages.each do |page| %>
                    <%= n.item page.title, page.link %>
                <% end %>
            <% end %>
        </div>
    

    I only have 2 pages in the DB at the moment.

        irb(main):002:0> Menu.find_by_slug(:primary).pages
          Menu Load (1.0ms)  SELECT "menus".* FROM "menus" WHERE "menus"."slug" = 'primary' LIMIT 1
          Page Load (0.0ms)  SELECT "pages".* FROM "pages" WHERE "pages"."menu_id" = 1
        => [#<Page id: 2, title: "Blog", link: "/posts", order: 1, menu_id: 1, created_at: "2013-01-26 16:10:51", updated_at: "2013-01-26 16:23:56">, #<Page id: 1, title: "New Book", link: "/books/new", order:0, menu_id: 1, created_at: "2013-01-26 16:06:43", updated_at: "2013-01-26 16:24:07">]
    

    But, when I view the navigation container, it renders the first page 4 times and the second one twice.

        <ul class="semantic-navigation" id="primary">
          <li><a href="/posts"><span>Blog</span></a></li>
          <li><a href="/posts"><span>Blog</span></a></li>
          <li><a href="/books/new"><span>New Book</span></a></li>
          <li><a href="/posts"><span>Blog</span></a></li>
          <li><a href="/posts"><span>Blog</span></a></li>
          <li><a href="/books/new"><span>New Book</span></a></li>
        </ul>
    

    I have no idea how it is behaving so oddly. Am I doing something wrong?

    Version releases to Rubygems aren't working right.

    As mentioned in #35.

    Version bumps should be made in their own commits. I'm not sure what the problem is with pushing official releases, but I've never been able to use the RubyGems version of navigasmic, and always had to use the github source. It seems like new versions are still just pushing up older releases with the new version name.

    Update documentation of :highlights

    To highlight a link, you've got to do somehting like:

    = navigation.item "Section", :highlights => [{:controller => :section}]
    

    However, the documentations says the option is named "highlights_on", which I guess it was correct at some time.

    Thanks.

    Add a title to the link tag of a group?

    - navigation.group flag(current_locale_flag), id: :language_chooser, title: t('.choose_language') do ... end
    

    This adds the title to the li tag, but I'd like to add it to the a tag. Is this possible?

    Groupings

    For the life of me I cannot get the groupings to work. I even pasted your example directly in:

    <% semantic_navigation :utility_nav do |n| %>
    <%= n.group 'Media' do %>
    <%= n.item 'Image Gallery', :link => '/media/images', :highlights_on => '/media/videos' %>
    <%= n.item 'Videos', :link => '/media/videos', :disabled_if => proc { true } %>
    <%= n.item 'Contact Us' # auto links to the contact_us_path if it exists %>
    <% end %>
    <% end %>

    and I get the following error:

    line ##: syntax error, unexpected ')'
    ....concat(( n.group 'Media' do ).to_s); @output_buffer.concat ...

    line ##: syntax error, unexpected kEND, expecting ')'
    ; end ; @output_buffer.concat "\n\t\t\t\t\t\t "

    line ##: syntax error, unexpected kEND, expecting ')'
    ; end ; @output_buffer.concat "\n\t...

    I'm running 2.3.10.

    Let me know what I'm missing

    -- Nick

    Move to GitHub Actions

    Hello!

    We would like to update our app to Ruby 3.2, but run into the issue reported in #57.

    I'm happy to submit a fix, but would need a proper CI setup to check that I break nothing in older Ruby versions. Travis tells that the project no longer exists on their platform, or at least, it is not public.

    image

    Would it be an option to move to GitHub Actions for you? would make it easier for future contributors to run the tests on their fork. also, it's free for public projects.

    Is this project still alive?

    Hello

    I loved Navigasmic, but as it doesn't seem to be alive anymore, I removed it from my projects. I especially miss a feature like this: #47

    All the best.

    :highlights_on for group?

    I notice that nobody seems to care about this gem anymore. What a pity.

    Anyways, another question: I'd like to use :highlights_on on groups: for example when displaying a user, the users group should be active, although there is no explicit show user item in it.

    To compare, the "list users" has a menu entry, so the parent group is highlighted:

    image

    image

    But for "show user", there's no entry, so the parent group isn't highlighted:

    image

    image

    I know that I can manually assign a class: 'active' myself, but this should be possible in an easier way.

    Controller parameter in item brokes nested resources

    Hi,

    for first, Navigasmatic is really great! :)

    For second, when i use controller parameter in navigation item brokes router for nested resources.

    One example for thousands words:

    = semantic_navigation :user, config: :bootstrap, class: 'pull-right' do |n|
      - n.item 'title', controller: 'categories'
      - n.item 'user profile', edit_user_registration_path

    It generates link path correctly but when is clicked on user profile it gives:

    No route matches {:controller=>"devise/categories"}

    Right route is devise/registrations

    When is used categories_path it works.

    link_options doesn't work

    Hi,

    I can't set the link_options.
    I would like to set a logout link:

    n.item 'Sign out', 'destroy_user_session_path', method: 'delete', hidden_unless: proc{ user_signed_in? } 
    

    but the link_optins in the builder.link_generator are empty.

    Dynamic item name, is that supported?

    Did not see an option for this, but how would you go about displaying something like current_user.email for an item name.

    n.group n.proc { current_user.email } do was something we tried, but that doesn't appear to work.

    List items should use classes not ids

    The reason is that ids should be unique. Having <li id="videos"></li> means you can't (validly) have <div id="videos"></div> on the page.

    The following nav structure would also result in duplicates:

    • People
      • Info
    • Products
      • Info

    Support item block definition

    Because the item is not always text-only, it would be nice to add support for the following syntax:

    <% semantic_navigation :app_nav, html: { class: 'nav' } do |n| %>
      <%= n.item link: blah_path do %>
        <i class="icon-th"></i> Blah
      <% end %>
      <%= n.item link: page_path('help') do %>
        <i class="icon-question-sign"></i> Help & Support
      <% end %>
    <% end %>

    I have a monkeypatch that I used with 0.5.6 here https://gist.github.com/clyfe/4714797

    :highlights_on in a group

    Hi. I'm trying to set an 'n.group' to be highlighted, but it doesn't work. Rails instead generates an HTML tag like 'highlighted_on="controller_name"'. How can I make a group receive the "active" class when I'm viewing one of his sons? Thank you for your time!

    Add custom attribute to link

    I want to add a custom accesskey="0" attribute to one of my menu items, but I don't seem to be able to find out how to do this. I played around with link_options without success, though this really looks like it has to be the right place to do this, but it places a link_options="{:accesskey=>0}" on the li element, not on the a.

    Any help is highly appreciated.

    group does not take a :link

    It should be possible to pass a :link argument to group:

    =n.group "My group". :link => root_path do
      =n.item ....
      =n.item ....
    

    I18n support

    It would be even more awesome if the names of groups and items could be translated through Rails' I18n framework.

    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.