Jump to content

Recommended Posts

Aligator (wip)

Processwire Module to render a nested tree starting from a single root or an array of pages.

Aligator is similar to MarkupSimpleNavigation but has a different approach of how to define the markup for your menu. It doesn't assume any markup or classes. It's up to you to define them where needed. It's less plug and play and requires some more advanced knowledge of ProcessWire, as some additional setup and coding is needed. But allows for powerful and easier customization without using hooks.

Aligator uses callback functions to achieve this. Additionally a selector can be used to filter the children for your navigation.

Note: This module is a fun project trying to find simple configurable method to render navigations. It's a work in progress and there might be major changes to how the module works.

 

See further infos and examples on the repository:

https://github.com/somatonic/Aligator

  • Like 21

Share this post


Link to post
Share on other sites

Haven't used it now, but already like it! (have had a look to the readme!) ^-^

Below an example of a root parent and a first level child:

alligator-baby.jpg

  • Like 15

Share this post


Link to post
Share on other sites

Apparently the child has a higher z-index :)

  • Like 6

Share this post


Link to post
Share on other sites

Is it called Aligator because it eats hell lot of memory or something?

Jokes aside, why Aligator? Why with the single "L"?

And you know my obsession... Can it render MenuBuilder contents somehow?

Share this post


Link to post
Share on other sites
20 hours ago, Ivan Gretsky said:

Is it called Aligator because it eats hell lot of memory or something?

Jokes aside, why Aligator? Why with the single "L"?

And you know my obsession... Can it render MenuBuilder contents somehow?

No it's powerful and fast and dangerous ...

With one "L"? It's more efficient...

No I don't know your obsession. Well I don't know what MenuBuilder contents are. Why? Does MenuBuilder not have any render functions? Aligator and MSN only work with Pages.

 

  • Like 3

Share this post


Link to post
Share on other sites
On 1.7.2016 at 5:13 PM, Soma said:

With one "L"? It's more efficient...

:lol:

  • Like 1

Share this post


Link to post
Share on other sites

Why Aligator with one "L"?

Maybe it is first name, last name?

First Name: Ali

Last Name: Gator

Share this post


Link to post
Share on other sites
On 01/07/2016 at 5:13 PM, Soma said:

No I don't know your obsession. Well I don't know what MenuBuilder contents are.

This is 'classic Soma'.

Share this post


Link to post
Share on other sites

Hi @Soma,

I finally got around to trying this module - it's really powerful, I love it!

A couple of questions:

1. Is there a way to include the Home page in a navigation generated by this module? Something like the 'show_root' option in MarkupSimpleNavigation?

2. If I want to add 'first' and 'last' classes to my nav items (like the 'firstlast' option in MarkupSimpleNavigation) is the following a good way or can you see a more efficient way?

In my callback function I add to the $class variable like this...

$class .= $item == $item->siblings()->first() ? ' first' : '';
$class .= $item == $item->siblings()->last() ? ' last' : '';

TIA.

Share this post


Link to post
Share on other sites

Had to look more at it again. :)

No there's no such built in option. But there's some ways to do it. 

The first argument not only works with a root page but also with a PageArray.

So you could do build a PageArray, add children of root, then prepend the home page to it and send that to Aligator. All you need to make sure you don't render level2 of home page which would render your menu again if home is your root. Got it? :)

$menuPages = new PageArray();
$home = $pages->get("/");
$menuPages->add($home->children());
$menuPages->prepend($home);

$content .= $modules->Aligator->render($menuPages, array(
    array( // level1
        "selector" => ""
    ),
    array( // level2 : don't render the children of "home"
        "selector" => "parent!=1"
    )
), 3);

Or a more straight forward version is to use the wrapperOpen and add it manually.

$homeUrl = $pages->get(1)->url;

$nav = $modules->Aligator->render($pages->get(1), array(
	array( // level1
		"selector" => "",
		"callback" => function($item, $level) use($homeUrl){
			return array(
				"wrapperOpen" => "<ul><li><a href='$homeUrl'>Home</a></li>",
			);
		}
	)
), 3);

The first last class thingy is not something I really ever use, but if it works for your navigation then it's ok to do it that way. It's just you would be careful if there's lots of pages as siblings. You'd also want to use the same selector you use for the level. Otherwise it would not work as the last sibling might be not rendered as by the selector.

  • Like 1

Share this post


Link to post
Share on other sites
36 minutes ago, Soma said:

No there's no such built in option. But there's some ways to do it. 

Thanks for the suggestions for including "Home" - I had been playing around with adding a "show_root" option to Aligator but your suggestions are simpler and better.

I made a few other changes in a fork of Aligator that I'll pass on for consideration.

1. I think there is a typo here - should be...

if($return["wrapperClose"]) {

2. I changed the early returns so that an empty string is returned, to be consistent with the @return tag in the function DocBlock.

3. I added a new argument that is supplied to the callback function - a $props array of some menu-related properties of the current $item.

The reason being that these properties are commonly needed to work out the menu markup, and they are already determined within the Aligator render() function - so it makes sense to pass 'is_parent', 'is_current' and 'num_children' as an argument rather than have to determine them again separately in the callback function.

I also included 'is_first' and 'is_last' in the $props array because it is much more efficient to determine this within render() from the $key in the foreach (just like is done in MarkupSimpleNavigation) than in the callback as per my post above. But now that I think about it I don't know that these properties are that useful after all - probably anything you would use them for could be just as easily achieved in CSS or JS with :first-child and :last-child.

4. Lastly, because of the $props argument I override 'is_parent' and 'num_children' in the case of the Home page being rendered. That's because despite the way PW treats Home as the root parent of all pages (there are probably good reasons for this), I think most website users think of the Home page as a sibling of the top-level pages rather than a parent of them.

The commit with my changes is here if you are interested.

Share this post


Link to post
Share on other sites

Me again. :)

Is there a way I can set options for all levels of a particular menu without changing the default options?

Suppose I have several menus in my site and I have defined some default options for Aligator that suit most of these menus. Now on one of these menus I want to exclude a particular template from all levels in the menu. I don't know how many levels will be in this menu (users may add pages creating additional levels of nesting).

So I can use the "selector" setting for this, but do I use this in the default options or the options for that specific menu? It seems like there are catches either way.

Default options: works for all levels (good), but affects all the menus in my site (bad).
Menu options: limited to this specific menu (good), but I have to repeat the setting over and over some arbitrary number of times to account for all the levels that might exist in the menu (bad).

Seems like there should be some way to set default options (all levels) but just for this one menu. Or am I missing something?

Share this post


Link to post
Share on other sites

One more thing: I think that here and here Aligator should fall back to the selector defined in the defaults rather than an empty string.

$selector = isset($options[$level-1]['selector']) ? $options[$level-1]['selector'] : $this->defaultOptions['selector'];

 

Share this post


Link to post
Share on other sites

Posting some more stuff while it is fresh in my mind...

I made a new commit here. Changes...

1. Fix for selector not falling back to what is defined in the default options.

2. Allows menu options to be defined for all levels by using "all" as the options array key. Example:

$menuOptions = array(
    // use these options for all levels in this menu
    "all" => array(
        "selector" => "template=basic-page|domain_root",
        "callback" => function($item, $level){
            $class = $item === wire("page") ? " current" : "";
            $class .= wire("page")->parents->has($item) ? " parent" : "";
            $class .= $item->numChildren("template=basic-page") ? " has_children" : "";
            return array(
                "item" => "<a href='$item->url'>$item->title $item->template</a>",
                "listOpen" => "<li class='level$level$class'>",
                "listClose" => "</li>",
                "wrapperOpen" => "<ul class='mainnav'>",
                "wrapperClose" => "</ul>",
            );
        },
    )
);

Setting an explicit array key also made me think of something else that it would be good to mention in the module readme. If you want to set options only for level 3 (for example) it isn't necessary to include anything for the other levels. Instead of...

$menuOptions = array(
    array(), // level 1
    array(), // level 2
    array( // level 3
        "selector" => "template=basic-page",
    ),
);

...you can do...

$menuOptions = array(
    // options for level 3 (array keys are zero-indexed)
    2 => array(
        "selector" => "template=basic-page",
    ),
);

 

And an issue to report: there is a problem when using Aligator to render more than one menu - the $level variable in the render() method can be incorrect for any menus after the first menu. This is because $level is declared as a static variable, so after the first menu is rendered $level can retain its value when it should be reset to zero for each menu that is rendered. Not sure of the best way to fix that (the whole static variable / recursive function thing makes my head spin).
Edit: In this commit I changed from the static variable to passing $level as an argument to render() and it seems like an okay solution.

  • Like 1

Share this post


Link to post
Share on other sites

Hey, thanks for the input. I was putting my head into it quite a few time yesterday, and messed up something. Took a while to figure it all out today. :) It can get complicated. You refactor stuff, then suddenly it all messed up. 

I commited a new version with some of your input and various fixes. https://github.com/somatonic/Aligator/commit/dc4d8aa19ff665e24aa07ccba65fd3aff0d4e002

The static level I also recognized that it gives problem when using more than one menu. Also the default options now carry over all levels. I don't think a "all" array is needed just for that, since it was the intention to have a default that is the fallback. 

I added a $states variable to callback (third argument) that returns a string with classes for the different states. You can enable it and configure it too.

Something like this was my testing scenario:

/**
 * load Alitgaor module
 * --------------------------------------------------------------------------------
 */

$nav = $modules->Aligator;

/**
 * first menu
 * using PageArray for first level and prepend root page
 * --------------------------------------------------------------------------------
 */

$menuPages = new PageArray();
$home = $pages->get("/");
$menuPages->add($home->children("template=basic-page"));
$menuPages->prepend($home);

$nav->enableStates = true; // enable states ("parent current has_children first last")
$nav->levels = 3; // set max levels to render

// the default options for all levels
$nav->setDefaultOptions(array(
        "selector" => "template=basic-page",
        "callback" => function($item, $level, $states){
            // $states is a string of item state classes to insert somewhere
            $classes = $states ? " class='$states'" : "";
            return array(
                "item" => "<a href='$item->url'>$item->title</a>",
                "listOpen" => "<li$classes>",
                "listClose" => "</li>",
                "wrapperOpen" => "<ul class='dropdown'>",
                "wrapperClose" => "</ul>",
            );
        }
    )
);

// render the menu
$content .= $nav->render($menuPages, array(
    array( // level1
        "selector" => "template=basic-page",
    ),
    array( // level2
        "selector" => "template=basic-page|article"
    )
));


/**
 * render different menu with default options and using a root page
 * --------------------------------------------------------------------------------
 */

$nav->levels = 4;
$nav->collapsed = true;
$nav->setDefaultStates(array("is_current" => "active"));
$nav->setDefaultOptions(array(
        "selector" => "template=basic-page",
        "callback" => function($item, $level, $states){
            return array(
                "item" => "<a href='$item->url'>$item->title</a>",
                "listOpen" => "<li class='$states'>",
                "listClose" => "</li>",
                "wrapperOpen" => "<ol>",
                "wrapperClose" => "</ol>"
            );
        }
    )
);

$content .= $nav->render($pages->get("/"));

I'm not sure, there's possibly still something that isn't covered or I missed but seems working fine here.

Thanks

 

  • Like 2

Share this post


Link to post
Share on other sites
13 hours ago, Soma said:

I commited a new version with some of your input and various fixes.

Thanks for the update!

A few little things...

1. $debug variable has been left behind in the module and may be undefined.

2. The default selector should be an empty string.

3. Wouldn't it be more efficient to set is_first and is_last according to array key like you did in MarkupSimpleNavigation?

 

In terms of understanding the module, I find the way that $defaultOptions, $defaultStates, $enableStates, $levels and $collapsed are set to be a bit counter-intuitive. To my way of thinking, you only want to set properties to the module itself for settings that are intended to be global (applying to all menus in your site). Because when these options are set as properties of the module they persist for all subsequent menus unless you explicitly change them.

But things like $levels and $collapsed are things that you want to set individually for a single menu - you don't want these settings affecting other menus. So I'd rather be passing these settings in as arguments to the render() method - that way I don't have to keep track and reset them for menus that are rendered subsequently.

Similar for setting menu options for all levels of a menu - that's why I thought something like a specific array key 'all' could be used when passing in the menu options. So I'm just setting default options for that one menu and not affecting other menus.

To be clear, I still like the idea of setting some global $defaultOptions, $defaultStates, etc - but for things like setting a levels limit or setting options for all levels of a specific menu it's less error-prone if these don't affect other menus.

  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Mike Rockett
      Jumplinks for ProcessWire
      Release: 1.5.56
      Composer: rockett/jumplinks
      Jumplinks is an enhanced version of the original ProcessRedirects by Antti Peisa.
      The Process module manages your permanent and temporary redirects (we'll call these "jumplinks" from now on, unless in reference to redirects from another module), useful for when you're migrating over to ProcessWire from another system/platform. Each jumplink supports wildcards, shortening the time needed to create them.
      Unlike similar modules for other platforms, wildcards in Jumplinks are much easier to work with, as Regular Expressions are not fully exposed. Instead, parameters wrapped in curly braces are used - these are described in the documentation.
      Under Development: 2.0, to be powered by FastRoute
      As of version 1.5.0, Jumplinks requires at least ProcessWire 2.6.1 to run.
      View on GitLab
      Download via the Modules Directory
      Read the docs
      Features
      The most prominent features include:
      Basic jumplinks (from one fixed route to another) Parameter-based wildcards with "Smart" equivalents Mapping Collections (for converting ID-based routes to their named-equivalents without the need to create multiple jumplinks) Destination Selectors (for finding and redirecting to pages containing legacy location information) Timed Activation (activate and/or deactivate jumplinks at specific times) 404-Monitor (for creating jumplinks based on 404 hits) Additionally, the following features may come in handy:
      Stale jumplink management Legacy domain support for slow migrations An importer (from CSV or ProcessRedirects) Feedback & Feature Requests
      I’d love to know what you think of this module. Please provide some feedback on the module as a whole, or even regarding smaller things that make it whole. Also, please feel free to submit feature requests and their use-cases.
      Note: Features requested so far have been added to the to-do list, and will be added to 2.0, and not the current dev/master branches.
      Open Source

      Jumplinks is an open-source project, and is free to use. In fact, Jumplinks will always be open-source, and will always remain free to use. Forever. If you would like to support the development of Jumplinks, please consider making a small donation via PayPal.
      Enjoy! :)
    • By BitPoet
      As threatened in Ryan's announcement for 3.0.139, I built a little module for sliding toggles as a replacement for checkboxes. Styling of the input is CSS3 only (with all the usual caveats about older browsers), no JS necessary, and may still be a bit "rough around the edges", so to speak, since I didn't have much time for testing on different devices or brushing things up enough so I'd feel comfortable pushing it to the module directory. But here's the link to the GitHub repo for now:
      InputfieldSlideToggle
      Fieldtype and Inputfield that implements smartphone-style toggles as replacement for checkbox inputs. The visualization is CSS-only, no additional JS necessary.
      Status
      Still very alpha, use with caution!
      Features / Field Settings
      Size
      You can render the toggles in four different sizes: small, medium, large and extra large.
      Off Color
      Currently, "unchecked" toggles can be displayed either in grey (default) or red.
      On Color
      "Checked" toggles can be rendered in one of these colors: blue (default), black, green, grey, orange or red.
      Screenshots

      Some examples with checkbox label


      View all Size and Color Combinations
      Small toggles Medium toggles Big toggles Extra big toggles  









    • By Orkun
      Hi Guys
      I needed to add extended functionalities for the InputfieldDatetime Module (module is from processwire version 2.7.3) because of a Request of Customer.
      So I duplicated the module and placed it under /site/modules/.
      I have added 3 new Settings to the InputfieldDatetime Module.
      1. Day Restriction - Restrict different days based on weekdays selection (e.g. saturday, sunday) - WORKING

       
      2. Time Slots - Define Time slots based on custom Integer Value (max is 60 for 1 hour) - WORKING

       
      3. Time Range Rules per Weekday - Define a minTime and MaxTime per Weekday (e.g. Opening Hours of a Restaurant) - NOT WORKING PROPERLY

       
      The Problem
      Time Slots and Day Restriction working fine so far. But the Time Range Rules per Weekday doesn't work right.
      What should happen is, that when you click on a date, it should update the minTime and maxTime of the Time Select.
      But the change on the select only happens if you select a date 2 times or when you select a date 1 time and then close the datepicker and reopen it again.
      The time select doesn't get change when you select a date 1 time and don't close the picker.
      Here is the whole extended InputfieldDatetime Module.
      The Files that I have changed:
      InputfieldDatetime.module InputfieldDatetime.js jquery-ui-timepicker-addon.js (https://trentrichardson.com/examples/timepicker/) - updated it to the newest version, because minTime and maxTime Option was only available in the new version  
      Thats the Part of the JS that is not working correctly:
      if(datetimerules && datetimerules.length){ options.onSelect = function(date, inst) { var day = $(this).datetimepicker("getDate").getDay(); day = day.toString(); var mintime = $(this).attr('data-weekday'+day+'-mintime'); var maxtime = $(this).attr('data-weekday'+day+'-maxtime'); console.log("weekday: "+day); console.log("minTime: "+mintime); console.log("maxTime: "+maxtime); var optionsAll = $(this).datetimepicker( "option", "all" ); optionsAll.minTime = mintime; optionsAll.maxTime = maxtime; $(this).datetimepicker('destroy'); $(this).datetimepicker(optionsAll); $(this).datetimepicker('refresh'); //$.datepicker._selectDate($(this).attr("id"),date); //$.datepicker._base_getDateDatepicker(); // var inst = $.datepicker._getInst($(this)); // $.datepicker._updateDatepicker(inst); /*$(this).datetimepicker('destroy'); InputfieldDatetimeDatepicker($(this), mintime, maxtime); $(this).datetimepicker('refresh'); */ // $(this).datetimepicker('option', {minTime: mintime, maxTime: maxtime}); } } Can you have a look and find out what the Problem is?
      InputfieldDatetime.zip
       
      Kind Regards
      Orkun
    • By teppo
      This module tracks changes, additions, removals etc. of public (as in "not under admin") pages of your site. Like it's name says, it doesn't attempt to be a version control system or anything like that - just a log of what's happened.
      At the moment it's still a work in progress and will most likely be a victim of many ruthless this-won't-work-let's-try-that-instead cycles, but I believe I've nailed basic functionality well enough to post it here.. so, once again, I'll be happy to hear any comments you folks can provide
      https://modules.processwire.com/modules/process-changelog/
      https://github.com/teppokoivula/ProcessChangelog
      How does it work?
      Exactly like it's (sort of) predecessor, Process Changelog actually consists of two modules: Process Changelog and Process Changelog Hooks. Hooks module exists only to serve main module by hooking into various functions within Pages class, collecting data of performed operations, refining it and keeping up a log of events in it's own custom database table (process_changelog.) Visible part is managed by Process Changelog, which provides users a (relatively) pretty view of the contents of said log table.
      How do you use it?
      When installed this module adds new page called Changelog under Admin > Setup which provides you with a table view of collected data and basic filtering tools See attached screenshots to get a general idea about what that page should look like after a while.
      For detailed installation instructions etc. see README.md.
       


    • By Gadgetto
      Status update links (inside this thread) for SnipWire development will be always posted here:
      2019-08-08
      2019-06-15
      2019-06-02
      2019-05-25
      If you are interested, you can test the current state of development:
      https://github.com/gadgetto/SnipWire
      Please note that the software is not yet intended for use in a production system (alpha version).
      If you like, you can also submit feature requests and suggestions for improvement. I also accept pull requests.
      ---- INITIAL POST FROM 2019-05-25 ----
      I wanted to let you know that I am currently working on a new ProcessWire module that fully integrates the Snipcart Shopping Cart System into ProcessWire. (this is a customer project, so I had to postpone the development of my other module GroupMailer).
      The new module SnipWire offers full integration of the Snipcart Shopping Cart System into ProcessWire.
      Here are some highlights:
      simple setup with (optional) pre-installed templates, product fields, sample products (quasi a complete shop system to get started immediately) store dashboard with all data from the snipcart system (no change to the snipcart dashboard itself required) Integrated REST API for controlling and querying snipcart data webhooks to trigger events from Snipcart (new order, new customer, etc.) multi currency support self-defined/configurable tax rates etc. Development is already well advanced and I plan to release the module in the next 2-3 months.
      I'm not sure yet if this will be a "Pro" module or if it will be made available for free.
      I would be grateful for suggestions and hints!
      (please have a look at the screenshots to get an idea what I'm talking about)
       




×
×
  • Create New...