├── .github └── ISSUE_TEMPLATE │ ├── bug_report.yaml │ ├── config.yml │ ├── feature_request.yaml │ └── support_request.yaml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config.codekit3 ├── docs ├── .sidebar.json ├── README.md ├── developers │ ├── events.md │ ├── extensibility.md │ ├── graphql.md │ ├── nav.md │ └── node.md ├── feature-tour │ └── overview.md ├── get-started │ ├── configuration.md │ ├── installation-setup.md │ └── requirements.md ├── getting-elements │ └── node-queries.md └── template-guides │ ├── available-variables.md │ ├── breadcrumbs.md │ ├── eager-loading.md │ ├── navigation-field.md │ └── rendering-nodes.md └── src ├── Navigation.php ├── assetbundles └── NavigationAsset.php ├── base ├── NodeType.php ├── NodeTypeInterface.php └── PluginTrait.php ├── console └── controllers │ └── NavsController.php ├── controllers ├── BaseController.php ├── NavsController.php └── NodesController.php ├── elements ├── Node.php ├── conditions │ ├── NodeCondition.php │ └── TypeConditionRule.php └── db │ └── NodeQuery.php ├── events ├── NavEvent.php ├── NodeActiveEvent.php ├── NodeEvent.php ├── RegisterElementEvent.php └── RegisterNodeTypeEvent.php ├── fieldlayoutelements ├── ClassesField.php ├── CustomAttributesField.php ├── NewWindowField.php ├── NodeTypeElements.php └── UrlSuffixField.php ├── fields └── NavigationField.php ├── gql ├── arguments │ └── NodeArguments.php ├── interfaces │ └── NodeInterface.php ├── queries │ └── NodeQuery.php ├── resolvers │ └── NodeResolver.php └── types │ ├── CustomAttributeType.php │ ├── NodeType.php │ └── generators │ ├── CustomAttributeGenerator.php │ └── NodeGenerator.php ├── helpers ├── Gql.php ├── Plugin.php └── ProjectConfigData.php ├── icon-mask.svg ├── icon.svg ├── integrations └── NodeFeedMeElement.php ├── migrations ├── Install.php └── m231229_000000_content_refactor.php ├── models ├── Nav.php ├── Nav_SiteSettings.php └── Settings.php ├── nodetypes ├── CustomType.php ├── PassiveType.php └── SiteType.php ├── records ├── Nav.php ├── Nav_SiteSettings.php └── Node.php ├── resources ├── dist │ ├── css │ │ └── navigation.css │ ├── fonts │ │ ├── fa-regular-400.eot │ │ ├── fa-regular-400.svg │ │ ├── fa-regular-400.ttf │ │ ├── fa-regular-400.woff │ │ ├── fa-regular-400.woff2 │ │ ├── fa-solid-900.eot │ │ ├── fa-solid-900.svg │ │ ├── fa-solid-900.ttf │ │ ├── fa-solid-900.woff │ │ └── fa-solid-900.woff2 │ └── js │ │ ├── navigation.js │ │ └── navigation.js.map └── src │ ├── js │ ├── _jquery.serializejson.min.js │ └── navigation.js │ └── scss │ ├── _font-awesome.scss │ └── navigation.scss ├── services ├── Breadcrumbs.php ├── Elements.php ├── Navs.php ├── NodeTypes.php └── Nodes.php ├── templates ├── _field │ ├── input.html │ └── settings.html ├── _integrations │ └── feed-me │ │ ├── column.html │ │ ├── fields │ │ └── nested-node.html │ │ ├── groups.html │ │ └── map.html ├── _layouts │ └── index.html ├── _special │ └── render.html ├── _types │ ├── custom │ │ └── modal.html │ └── site │ │ ├── modal.html │ │ └── settings.html ├── navs │ ├── _build.html │ ├── _edit.html │ └── index.html └── settings │ ├── _panes │ └── general.html │ └── index.html ├── translations ├── en │ └── navigation.php ├── fr │ └── navigation.php ├── hu │ └── navigation.php └── nl │ └── navigation.php └── variables └── NavigationVariable.php /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us improve. 3 | body: 4 | - type: markdown 5 | attributes: 6 | value: | 7 | Before you send through your bug report, please ensure you have taken these steps first: 8 | 9 | ✅ I‘ve searched the [documentation](https://verbb.io/craft-plugins/navigation/docs). 10 | ✅ I‘ve searched open and closed issues. 11 | 12 | - type: textarea 13 | id: bug-description 14 | attributes: 15 | label: Describe the bug 16 | description: Describe the bug and what behaviour you expect if the bug is fixed. 17 | placeholder: "I have an issue where..." 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: steps-to-reproduce 22 | attributes: 23 | label: Steps to reproduce 24 | description: Detail how we can reproduce this issue. 25 | value: | 26 | 1. 27 | 2. 28 | validations: 29 | required: true 30 | - type: input 31 | id: craft-version 32 | attributes: 33 | label: Craft CMS version 34 | description: What version of Craft CMS you‘re using. **Do not write "latest"**. 35 | validations: 36 | required: true 37 | - type: input 38 | id: plugin-version 39 | attributes: 40 | label: Plugin version 41 | description: What version of the plugin you‘re using. **Do not write "latest"**. 42 | validations: 43 | required: true 44 | - type: input 45 | id: multi-site 46 | attributes: 47 | label: Multi-site? 48 | description: Whether your install is a multi-site. 49 | placeholder: | 50 | "Yes" or "No" 51 | - type: textarea 52 | id: additional-context 53 | attributes: 54 | label: Additional context 55 | description: Provide any additional information you think might be useful. The more information you provide the easier it‘ll be for use to fix this bug! 56 | placeholder: | 57 | "I also have X plugin installed..." or "This only happens on production..." -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Documentation 4 | url: https://verbb.io/craft-plugins/navigation/docs 5 | about: Read the official documentation. 6 | - name: Craft Discord 7 | url: https://craftcms.com/discord 8 | about: Community discussion and support. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea or enhancement. 3 | labels: 'feature request' 4 | body: 5 | - type: textarea 6 | id: feature-description 7 | attributes: 8 | label: What are you trying to do? 9 | description: A description of what you want to happen. 10 | placeholder: "I would like to see..." 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: proposed-solution 15 | attributes: 16 | label: What's your proposed solution? 17 | description: A description of how you think this could be solved, including any alternatives that you considered. 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: additional-context 22 | attributes: 23 | label: Additional context 24 | description: Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support_request.yaml: -------------------------------------------------------------------------------- 1 | name: Question 2 | description: Ask a question about this plugin. DO NOT use this to submit bug reports. 3 | labels: 'question' 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Before you send through your question, please ensure you have taken these steps first: 9 | 10 | ✅ I‘ve searched the [documentation](https://verbb.io/craft-plugins/navigation/docs). 11 | ✅ I‘ve searched open and closed issues. 12 | ✅ This is not a bug report, just a general question. 13 | 14 | - type: textarea 15 | id: question 16 | attributes: 17 | label: Question 18 | description: A question about the plugin or how it works. 19 | placeholder: "Is it possible to do..." 20 | validations: 21 | required: true 22 | - type: textarea 23 | id: additional-context 24 | attributes: 25 | label: Additional context 26 | description: Add any other context or screenshots about your question here. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # CRAFT ENVIRONMENT 2 | .env.php 3 | .env.sh 4 | .env 5 | 6 | # COMPOSER 7 | /vendor 8 | 9 | # BUILD FILES 10 | /bower_components/* 11 | /node_modules/* 12 | /build/* 13 | /yarn-error.log 14 | 15 | # MISC FILES 16 | .cache 17 | .DS_Store 18 | .idea 19 | .project 20 | .settings 21 | .map 22 | *.esproj 23 | *.sublime-workspace 24 | *.sublime-project 25 | *.tmproj 26 | *.tmproject 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © Verbb 2 | 3 | Permission is hereby granted to any person obtaining a copy of this software 4 | (the “Software”) to use, copy, modify, merge, publish and/or distribute copies 5 | of the Software, and to permit persons to whom the Software is furnished to do 6 | so, subject to the following conditions: 7 | 8 | 1. **Don’t plagiarize.** The above copyright notice and this license shall be 9 | included in all copies or substantial portions of the Software. 10 | 11 | 2. **Don’t use the same license on more than one project.** Each licensed copy 12 | of the Software shall be actively installed in no more than one production 13 | environment at a time. 14 | 15 | 3. **Don’t mess with the licensing features.** Software features related to 16 | licensing shall not be altered or circumvented in any way, including (but 17 | not limited to) license validation, payment prompts, feature restrictions, 18 | and update eligibility. 19 | 20 | 4. **Pay up.** Payment shall be made immediately upon receipt of any notice, 21 | prompt, reminder, or other message indicating that a payment is owed. 22 | 23 | 5. **Follow the law.** All use of the Software shall not violate any applicable 24 | law or regulation, nor infringe the rights of any other person or entity. 25 | 26 | Failure to comply with the foregoing conditions will automatically and 27 | immediately result in termination of the permission granted hereby. This 28 | license does not include any right to receive updates to the Software or 29 | technical support. Licensees bear all risk related to the quality and 30 | performance of the Software and any modifications made or obtained to it, 31 | including liability for actual and consequential harm, such as loss or 32 | corruption of data, and any necessary service, repair, or correction. 33 | 34 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 35 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 37 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER 38 | LIABILITY, INCLUDING SPECIAL, INCIDENTAL AND CONSEQUENTIAL DAMAGES, WHETHER IN 39 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 40 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Navigation icon

2 |

Navigation for Craft CMS

3 | 4 | Navigation is a Craft CMS plugin to help manage navigation menus for you site. Supports linking to existing elements like entries, categories and products, as well as custom URLs. 5 | 6 | ## Features 7 | - Create multiple navigations 8 | - Create navigation nodes for entries, assets, categories and Commerce products 9 | - Create custom URLs 10 | - Enable/disable, open links in a new window, or apply additional CSS classes 11 | - Automatically updates nodes when linked elements status or title changes 12 | - Navigation nodes are elements for flexible querying 13 | - Support for third-party elements with hooks 14 | - Support for multi-site navigations 15 | - Simple `render()` Twig function, or roll your own 16 | - Generate breadcrumbs easily based on your URL segments 17 | - Tool to migrate your menus if you've used [A&M Nav for Craft 2](https://github.com/am-impact/amnav) or [Navee for Craft 2](https://github.com/fromtheoutfit/navee) 18 | 19 | ## Documentation 20 | Visit the [Navigation Plugin page](https://verbb.io/craft-plugins/navigation) for all documentation, guides, pricing and developer resources. 21 | 22 | ## Credit & Thanks 23 | A big shoutout to [A&M Nav](https://github.com/am-impact/amnav) for their awesome plugin for Craft 2. 24 | 25 | ## Support 26 | Get in touch with us via the [Navigation Support page](https://verbb.io/craft-plugins/navigation/support) or by [creating a Github issue](/verbb/navigation/issues) 27 | 28 |

29 | 30 | 31 | Verbb 32 | 33 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verbb/navigation", 3 | "description": "Create navigation menus for your site.", 4 | "type": "craft-plugin", 5 | "version": "3.0.8", 6 | "keywords": [ 7 | "craft", 8 | "cms", 9 | "craftcms", 10 | "craft-plugin", 11 | "navigation", 12 | "menu" 13 | ], 14 | "support": { 15 | "email": "support@verbb.io", 16 | "issues": "https://github.com/verbb/navigation/issues?state=open", 17 | "source": "https://github.com/verbb/navigation", 18 | "docs": "https://github.com/verbb/navigation", 19 | "rss": "https://github.com/verbb/navigation/commits/v2.atom" 20 | }, 21 | "license": "proprietary", 22 | "authors": [ 23 | { 24 | "name": "Verbb", 25 | "homepage": "https://verbb.io" 26 | } 27 | ], 28 | "require": { 29 | "php": "^8.2", 30 | "craftcms/cms": "^5.0.0", 31 | "verbb/base": "^3.0.0" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "verbb\\navigation\\": "src/" 36 | } 37 | }, 38 | "extra": { 39 | "name": "Navigation", 40 | "handle": "navigation", 41 | "changelogUrl": "https://raw.githubusercontent.com/verbb/navigation/craft-5/CHANGELOG.md", 42 | "class": "verbb\\navigation\\Navigation" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /docs/.sidebar.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Get Started", 4 | "collapsable": false, 5 | "children": [ 6 | "get-started/installation-setup", 7 | "get-started/requirements", 8 | "get-started/configuration" 9 | ] 10 | }, 11 | { 12 | "title": "Feature Tour", 13 | "collapsable": false, 14 | "children": [ 15 | "feature-tour/overview" 16 | ] 17 | }, 18 | { 19 | "title": "Template Guides", 20 | "collapsable": false, 21 | "children": [ 22 | "template-guides/available-variables", 23 | "template-guides/rendering-nodes", 24 | "template-guides/breadcrumbs", 25 | "template-guides/navigation-field", 26 | "template-guides/eager-loading" 27 | ] 28 | }, 29 | { 30 | "title": "Getting Elements", 31 | "collapsable": false, 32 | "children": [ 33 | "getting-elements/node-queries" 34 | ] 35 | }, 36 | { 37 | "title": "Developers", 38 | "collapsable": false, 39 | "children": [ 40 | "developers/node", 41 | "developers/nav", 42 | "developers/events", 43 | "developers/extensibility", 44 | "developers/graphql" 45 | ] 46 | } 47 | ] 48 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verbb/navigation/44683d87c0e0f0650e198d8a75b3aff2b4836195/docs/README.md -------------------------------------------------------------------------------- /docs/developers/events.md: -------------------------------------------------------------------------------- 1 | # Events 2 | Events can be used to extend the functionality of Navigation. 3 | 4 | ## Nav related events 5 | 6 | ### The `beforeSaveNav` event 7 | Plugins can get notified before a navigation is saved 8 | 9 | ```php 10 | use verbb\navigation\events\NavEvent; 11 | use verbb\navigation\services\Navs; 12 | use yii\base\Event; 13 | 14 | Event::on(Navs::class, Navs::EVENT_BEFORE_SAVE_NAV, function(NavEvent $e) { 15 | // Do something 16 | }); 17 | ``` 18 | 19 | ### The `afterSaveNav` event 20 | Plugins can get notified after a navigation has been saved 21 | 22 | ```php 23 | use verbb\navigation\events\NavEvent; 24 | use verbb\navigation\services\Navs; 25 | use yii\base\Event; 26 | 27 | Event::on(Navs::class, Navs::EVENT_AFTER_SAVE_NAV, function(NavEvent $e) { 28 | // Do something 29 | }); 30 | ``` 31 | 32 | ### The `beforeDeleteNav` event 33 | Plugins can get notified before a navigation is deleted 34 | 35 | ```php 36 | use verbb\navigation\events\NavEvent; 37 | use verbb\navigation\services\Navs; 38 | use yii\base\Event; 39 | 40 | Event::on(Navs::class, Navs::EVENT_BEFORE_DELETE_NAV, function(NavEvent $event) { 41 | // Do something 42 | }); 43 | ``` 44 | 45 | ### The `afterDeleteNav` event 46 | Plugins can get notified after a navigation has been deleted 47 | 48 | ```php 49 | use verbb\navigation\events\NavEvent; 50 | use verbb\navigation\services\Navs; 51 | use yii\base\Event; 52 | 53 | Event::on(Navs::class, Navs::EVENT_AFTER_DELETE_NAV, function(NavEvent $event) { 54 | // Do something 55 | }); 56 | ``` 57 | 58 | 59 | ## Node related events 60 | 61 | ### The `beforeSaveNode` event 62 | Plugins can get notified before a node is saved. Event handlers can prevent the node from getting sent by setting `$event->isValid` to false. 63 | 64 | ```php 65 | use craft\events\ModelEvent; 66 | use verbb\navigation\elements\Node; 67 | use yii\base\Event; 68 | 69 | Event::on(Node::class, Node::EVENT_BEFORE_SAVE, function(ModelEvent $event) { 70 | $node = $event->sender; 71 | $event->isValid = false; 72 | }); 73 | ``` 74 | 75 | ### The `afterSaveNode` event 76 | Plugins can get notified after a node has been saved 77 | 78 | ```php 79 | use craft\events\ModelEvent; 80 | use verbb\navigation\elements\Node; 81 | use yii\base\Event; 82 | 83 | Event::on(Node::class, Node::EVENT_AFTER_SAVE, function(ModelEvent $event) { 84 | $node = $event->sender; 85 | }); 86 | ``` 87 | 88 | ### The `modifyNodeActive` event 89 | Plugins can modify the active state of a node. 90 | 91 | ```php 92 | use verbb\navigation\elements\Node; 93 | use yii\base\Event; 94 | 95 | Event::on(Node::class, Node::EVENT_NODE_ACTIVE, function(Event $event) { 96 | $node = $event->node; 97 | $event->isActive = true; 98 | }); 99 | ``` 100 | 101 | ### The `beforeMoveElement` event 102 | Plugins can get notified before a node is moved in its structure. 103 | 104 | ```php 105 | use verbb\navigation\elements\Node; 106 | use craft\events\MoveElementEvent; 107 | use craft\services\Structures; 108 | use yii\base\Event; 109 | 110 | Event::on(Structures::class, Structures::EVENT_BEFORE_MOVE_ELEMENT, function(MoveElementEvent $event) { 111 | $element = $event->element; 112 | 113 | if ($element instanceof Node) { 114 | // ... 115 | } 116 | }); 117 | ``` 118 | 119 | ### The `afterMoveElement` event 120 | Plugins can get notified after a node is moved in its structure. 121 | 122 | ```php 123 | use verbb\navigation\elements\Node; 124 | use craft\events\MoveElementEvent; 125 | use craft\services\Structures; 126 | use yii\base\Event; 127 | 128 | Event::on(Structures::class, Structures::EVENT_AFTER_MOVE_ELEMENT, function(MoveElementEvent $event) { 129 | $element = $event->element; 130 | 131 | if ($element instanceof Node) { 132 | // ... 133 | } 134 | }); 135 | ``` 136 | -------------------------------------------------------------------------------- /docs/developers/extensibility.md: -------------------------------------------------------------------------------- 1 | # Extensibility 2 | 3 | ## Elements 4 | You can add your own custom elements to be compatible with Navigation by using the provided events. The below shows an example of how entries are set up. 5 | 6 | ```php 7 | namespace modules\sitemodule; 8 | 9 | use verbb\navigation\services\Elements; 10 | use verbb\navigation\events\RegisterElementEvent; 11 | use yii\base\Event; 12 | 13 | Event::on(Elements::class, Elements::EVENT_REGISTER_NAVIGATION_ELEMENT, function(RegisterElementEvent $event) { 14 | $event->elements['entries'] = [ 15 | 'label' => Craft::t('navigation', 'Entries'), 16 | 'button' => Craft::t('navigation', 'Add an entry'), 17 | 'type' => 'craft\\elements\\Entry', 18 | ]; 19 | }); 20 | ``` 21 | 22 | ## Node Types 23 | Node types allow you to define your own type of nodes for various cases. You might like to have certain types of "Custom URL" nodes for instance. 24 | 25 | You'll need to first create a class to implement your node type. Here's an example for a Group. 26 | 27 | ```php 28 | types[] = Group::class; 76 | }); 77 | ``` 78 | -------------------------------------------------------------------------------- /docs/developers/nav.md: -------------------------------------------------------------------------------- 1 | # Nav 2 | A `Nav` object contains multiple navigation nodes. Whenever you're dealing with a nav in your template, you have access to the following. 3 | 4 | ## Attributes 5 | 6 | Attribute | Description 7 | --- | --- 8 | `id` | ID for the nav. 9 | `name` | The name of the nav. 10 | `handle` | The handle of the nav. 11 | `instructions` | Any additional instructions for the nav, often for internal use in the control panel. 12 | `maxLevels` | The maximum number of nested level of nodes a nav can allow. 13 | `maxnodes` | The maximum number of nodes a nav can allow. 14 | `propagateNodes` | Whether the propagate (copy) nodes across all your sites. 15 | -------------------------------------------------------------------------------- /docs/developers/node.md: -------------------------------------------------------------------------------- 1 | # Node 2 | Whenever you're dealing with a node in your template, you're actually working with a `Node` object. 3 | 4 | ## Attributes 5 | 6 | Attribute | Description 7 | --- | --- 8 | `id` | ID for the node. 9 | `elementId` | The linked element ID (if not custom). 10 | `element` | The linked element (if not custom). 11 | `navId` | The ID for the nav this node belongs to. 12 | `url` | URL for this node. Either the linked element or custom. 13 | `nodeUri` | URI for this node. Either the linked element or custom. 14 | `title` | Title for this node. Either the linked element or custom. 15 | `link` | Full HTML link (combined url and title). 16 | `type` | The class name for the type of node. 17 | `classes` | Any additional CSS classes added to the node. 18 | `customAttributes` | A list of attributes as provided in the table. Use `attribute` and `value` for each row. 19 | `urlSuffix` | If provided, a suffix (think anchor or query string) added on to the URL. 20 | `target` | Returns either `_blank` or an empty string, should this node open in a new window. 21 | `newWindow` | Whether this node should open in a new window. 22 | `active` | Whether the URL matches the current URL. 23 | `hasActiveChild` | Whether the node has an active child. 24 | `nav` | The [Navigation](docs:developers/nav) model this node belongs to. 25 | `status` | The current status of the node. 26 | `children` | A collection of child nodes (if any). 27 | `level` | The level this node resides in, if using nested nodes. 28 | 29 | ## Methods 30 | 31 | Method | Description 32 | --- | --- 33 | `getTypeLabel()` | The display name for the type of node. 34 | `isElement()` | Whether the node is an "Element" node type (it links to an Entry, Category, etc). 35 | `isCustom()` | Whether the node is a "Custom URL" node type. 36 | `isPassive()` | Whether the node is a "Passive" node type. 37 | `isSite()` | Whether the node is a "Site" node type. 38 | 39 | ### `customAttributes` 40 | As attributes are stored in a table for the node, you'll need to loop through them to output them. Each row has an `attribute` and `value` property, as defined in the table field for the node. These correspond with the column names. 41 | 42 | ```twig 43 | 44 | {{- node.title -}} 45 | 46 | ``` 47 | 48 | ### `linkAttributes` 49 | A helper function to assist with generating attributes for an anchor tag. 50 | 51 | ```twig 52 | 53 | 54 | {# Would produce the following HTML #} 55 | 56 | 57 | {# For a node that opens in a new window #} 58 | 59 | 60 | {# For a node with a custom class #} 61 | 62 | 63 | {# For a node with a custom attributes #} 64 | 65 | ``` 66 | 67 | You can also pass in any additional attributes you require at the template level: 68 | 69 | ```twig 70 | 74 | 75 | {# Would produce the following HTML #} 76 | 77 | ``` 78 | 79 | These will be merged recursively with attributes defined in the node. For example, we might have a class `node-class` defined in the node's settings. As you can see, this is merged in with `another-class` we define in our templates. 80 | 81 | ## Custom Fields 82 | As you can have custom fields attached to each node, you can access their content via their field handles. For instance, you might have added a Plain Text field to your navigation's field layout, with a handle `myPlainTextfield`, which you could access via: 83 | 84 | ```twig 85 | {{ node.myPlainTextfield }} 86 | ``` 87 | 88 | ## Element Custom Fields 89 | As nodes can be linked to an element, you can also fetch those custom fields attached to that element. For example, you might have a Homepage entry, which you've added as a node to your navigation. On this entry, you have a Plain Text field with a handle of `myPlainTextfield`. You could access it via: 90 | 91 | ```twig 92 | {{ node.element.myPlainTextfield }} 93 | ``` 94 | 95 | However, you'll want to be mindful that when looping through all the other nodes in your navigation that not all nodes are linked to entries, and not all linked entries contain this field. You'll likely receive errors that `myPlainTextfield` is not a valid attribute. So, you'll want to provide some conditional handling of this. 96 | 97 | ```twig 98 | {# Check that this node links to an element, and it has the field we want #} 99 | {% if node.element and node.element.myPlainTextfield %} 100 | {{ node.element.myPlainTextfield }} 101 | {% endif %} 102 | 103 | {# Check for a specific element, via its slug #} 104 | {% if node.element and node.element.slug == 'homepage' %} 105 | {{ node.element.myPlainTextfield }} 106 | {% endif %} 107 | ``` 108 | -------------------------------------------------------------------------------- /docs/feature-tour/overview.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | You'll first want to create a navigation - this contains your navigation nodes with links to elements or custom URLs. Head to "Navigation" on the main sidebar menu, and click "New Navigation" on the top-right. 3 | 4 | Once saved, click on the navigation title to start adding your nodes. 5 | 6 | Using the right-hand side menu, add either elements or custom URLs. Once added, they'll appear on the left-hand side in a structure. Hover over any navigation node to see some actions - move, edit and delete. Any changes you make to this structure will reflect the changes on your live site. 7 | 8 | When linking to an existing element, the Title and Enabled settings will always reflect the original element's state. So, if you change the linked element's title or disable it, it'll reflect this change in the node. You can override the Title and Enabled settings to become independent of your element changes - if you wanted a custom title for instance. -------------------------------------------------------------------------------- /docs/get-started/configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | Create a `navigation.php` file under your `/config` directory with the following options available to you. You can also use multi-environment options to change these per environment. 3 | 4 | The below shows the defaults already used by Navigation, so you don't need to add these options unless you want to modify the values. 5 | 6 | ```php 7 | [ 11 | 'pluginName' => 'Navigation', 12 | 'bypassProjectConfig' => false, 13 | ] 14 | ]; 15 | ``` 16 | 17 | ## Configuration options 18 | - `pluginName` - Optionally change the name of the plugin. 19 | - `bypassProjectConfig` - Prevents navigations from being saved to Project Config. Be sure you know what you're doing with this! 20 | 21 | ## Control Panel 22 | You can also manage configuration settings through the Control Panel by visiting Settings → Navigation. 23 | -------------------------------------------------------------------------------- /docs/get-started/installation-setup.md: -------------------------------------------------------------------------------- 1 | # Installation & Setup 2 | You can install Navigation via the plugin store, or through Composer. 3 | 4 | ## Craft Plugin Store 5 | To install **Navigation**, navigate to the _Plugin Store_ section of your Craft control panel, search for `Navigation`, and click the _Try_ button. 6 | 7 | ## Composer 8 | You can also add the package to your project using Composer and the command line. 9 | 10 | 1. Open your terminal and go to your Craft project: 11 | ```shell 12 | cd /path/to/project 13 | ``` 14 | 15 | 2. Then tell Composer to require the plugin, and Craft to install it: 16 | ```shell 17 | composer require verbb/navigation && php craft plugin/install navigation 18 | ``` 19 | 20 | ## Licensing 21 | You can try Navigation in a development environment for as long as you like. Once your site goes live, you are required to purchase a license for the plugin. 22 | 23 | For more information, see [Craft's Commercial Plugin Licensing](https://craftcms.com/docs/4.x/plugins.html#commercial-plugin-licensing). 24 | -------------------------------------------------------------------------------- /docs/get-started/requirements.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | ## Craft CMS 4 | Navigation requires Craft CMS 5.0 or greater. 5 | 6 | ## PHP 7 | Navigation requires PHP 8.2 or greater. -------------------------------------------------------------------------------- /docs/template-guides/available-variables.md: -------------------------------------------------------------------------------- 1 | # Available Variables 2 | 3 | The following methods are available to call in your Twig templates: 4 | 5 | ### `craft.navigation.nodes(params)` 6 | The `params` parameter can be either a string for the [Nav](docs:developers/nav) handle, or an object of [NodeQuery](docs:getting-elements/node-queries) params. You can also chain these same params to this function call. 7 | 8 | ```twig 9 | {# Fetch the `mainMenu` nodes #} 10 | {% set nodes = craft.navigation.nodes('mainMenu').all() %} 11 | 12 | {# Chain params to the `nodes()` function #} 13 | {% set nodes = craft.navigation.nodes() 14 | .handle('mainMenu') 15 | .site('default') 16 | .all() %} 17 | 18 | {# Or, pass them as an object #} 19 | {% set nodes = craft.navigation.nodes({ 20 | handle: 'mainMenu', 21 | site: 'default', 22 | }).all() %} 23 | ``` 24 | 25 | See [Node Queries](docs:getting-elements/node-queries) 26 | 27 | ### `craft.navigation.render(params, options)` 28 | The `params` parameter can be either a string for the [Nav](docs:developers/nav) handle, an object of [NodeQuery](docs:getting-elements/node-queries) params or a [NodeQuery](docs:getting-elements/node-queries) itself. 29 | 30 | ```twig 31 | {# Render the `mainMenu` navigation #} 32 | {{ craft.navigation.render('mainMenu') }} 33 | 34 | {# Render the `mainMenu` navigation for the `default` site #} 35 | {{ craft.navigation.render({ 36 | handle: 'mainMenu', 37 | site: 'default', 38 | }) }} 39 | 40 | {# The same as above, but using a `NodeQuery` #} 41 | {% set nodeQuery = craft.navigation.nodes('mainMenu').site('default') %} 42 | 43 | {{ craft.navigation.render(nodeQuery) }} 44 | ``` 45 | 46 | See [Rendering Nodes](docs:template-guides/rendering-nodes) 47 | 48 | ### `craft.navigation.breadcrumbs(options)` 49 | See [Breadcrumbs](docs:template-guides/breadcrumbs) 50 | 51 | ### `craft.navigation.getActiveNode(params, includeChildren)` 52 | The `params` parameter can be either a string for the [Nav](docs:developers/nav) handle, an object of [NodeQuery](docs:getting-elements/node-queries) params or a [NodeQuery](docs:getting-elements/node-queries) itself. 53 | 54 | See [Rendering Nodes](docs:template-guides/rendering-nodes) 55 | 56 | ### `craft.navigation.tree(params)` 57 | Returns a full tree structure of nodes as a nested array. 58 | 59 | The `params` parameter can be either a string for the [Nav](docs:developers/nav) handle, an object of [NodeQuery](docs:getting-elements/node-queries) params or a [NodeQuery](docs:getting-elements/node-queries) itself. 60 | 61 | ### `craft.navigation.getNavById(id)` 62 | Returns the navigation for the provided id. 63 | 64 | ### `craft.navigation.getNavByHandle(handle)` 65 | Returns the navigation for the provided handle. 66 | -------------------------------------------------------------------------------- /docs/template-guides/breadcrumbs.md: -------------------------------------------------------------------------------- 1 | # Breadcrumbs 2 | 3 | ## `craft.navigation.breadcrumbs(options)` 4 | You can retrieve a list of breadcrumbs based on the current URL. They are not based on your navigation items, and instead use the current URL segments. The function will look up any element that matches the URI for the segment. If not found, the segment itself will be used. 5 | 6 | ```twig 7 | {% for crumb in craft.navigation.breadcrumbs() %} 8 | {{ crumb.title }} 9 | {% endfor %} 10 | ``` 11 | 12 | The `crumb` variable returned from the `breadcrumbs()` function will be an array with the following options. This will either contain information on a matched element, or information derived from the segment. 13 | 14 | ### Properties 15 | 16 | | Property | Description 17 | | - | - 18 | | `title` | The title of the segment. Either the element's title, or derived from the segment. 19 | | `url` | The absolute URL for the segment, for the current site. 20 | | `segment` | The segment portion of the current URL. 21 | | `isElement` | Whether the segment is an element or not. 22 | | `element` | The element object (if an element). 23 | | `elementId` | The ID of the element (if an element). 24 | | `elementType` | The type of element (if an element). 25 | 26 | You can also pass in options to the `breadcrumbs()` function. For example, you could limit the number of breadcrumb items returned. 27 | 28 | ```twig 29 | {% for crumb in craft.navigation.breadcrumbs({ limit: 10 }) %} 30 | {{ crumb.title }} 31 | {% endfor %} 32 | ``` 33 | 34 | ### Available Options 35 | 36 | | Options | Description 37 | | - | - 38 | | `limit` | The number to limit returned breadcrumbs item by. 39 | -------------------------------------------------------------------------------- /docs/template-guides/eager-loading.md: -------------------------------------------------------------------------------- 1 | # Eager-Loading 2 | Craft features a concept called [Eager-Loading](https://craftcms.com/docs/3.x/dev/eager-loading-elements.html), allowing some significant performance benefits when dealing with elements. 3 | 4 | We can make use of this too, to speed up rendering of navigation nodes. However, you'll only really see benefits from eager-loading when your navigation have multiple levels. A single level navigation won't get any benefit from eager-loading. 5 | 6 | ## craft.navigation.render() 7 | If you're using the `craft.navigation.render()` Twig function, there's nothing you need to do! Navigation eager-loads nested navigations automatically. 8 | 9 | ## craft.navigation.nodes() 10 | Let's take a look at an example navigation setup. We have the following navigation structure, consisting of 3-levels of nodes. 11 | 12 | ``` 13 | - Node 1 14 | - Node 1-1 15 | - Node 1-2 16 | - Node 1-3 17 | - Node 1-4 18 | - Node 2 19 | - Node 3 20 | - Node 3-1 21 | - Node 3-2 22 | - Node 3-3 23 | - Node 4 24 | - Node 5 25 | - Node 6 26 | - Node 6-1 27 | - Node 6-2 28 | - Node 6-3 29 | - Node 7 30 | - Node 8 31 | - Node 9 32 | - Node 10 33 | ``` 34 | 35 | And we'll use the following Twig to output the nodes: 36 | 37 | ```twig 38 | {% set nodes = craft.navigation.nodes('mainMenu').level(1).all() %} 39 | 40 | {% for node in nodes %} 41 | {{ node.link }} 42 | 43 | {% for subnode in node.children.all() %} 44 | {{ subnode.link }} 45 | {% endfor %} 46 | {% endfor %} 47 | ``` 48 | 49 | Whilst this will work fine, we're also producing a lot of database queries. The above should generate close to **32 queries** to fetch nested nodes. We can improve this with eager-loading the children and descendants. 50 | 51 | ```twig 52 | {% set nodes = craft.navigation.nodes('mainMenu').level(1).with(['children']).all() %} 53 | 54 | {% for node in nodes %} 55 | {{ node.link }} 56 | 57 | {% for subnode in node.children %} 58 | {{ subnode.link }} 59 | {% endfor %} 60 | {% endfor %} 61 | ``` 62 | 63 | There's two main things to note here, we're using `with(['children'])` in our query, and we're not using `all()` to loop through sub nodes. This will bring our query count down to **10 queries** - a vast improvement over the former template. 64 | 65 | If you have a third-level in your navigation, you'll need to eager-load those to, and so on - depending on how many levels your navigation has. 66 | 67 | ```twig 68 | {% set nodes = craft.navigation.nodes('mainMenu').level(1).with(['children.children']).all() %} 69 | 70 | {% for node in nodes %} 71 | {{ node.link }} 72 | 73 | {% for subnode in node.children %} 74 | {{ subnode.link }} 75 | 76 | {% for innernode in subnode.children %} 77 | {{ innernode.link }} 78 | {% endfor %} 79 | {% endfor %} 80 | {% endfor %} 81 | ``` -------------------------------------------------------------------------------- /docs/template-guides/navigation-field.md: -------------------------------------------------------------------------------- 1 | # Navigation Field 2 | 3 | You can use the navigation field to allow entries and other elements to select a navigation to show. In your templates, when calling the field (ie `{{ entry.navigationField }}`) you'll be returned the handle for the navigation. 4 | 5 | ```twig 6 | {{ craft.navigation.render(entry.navigationField) }} 7 | ``` -------------------------------------------------------------------------------- /docs/template-guides/rendering-nodes.md: -------------------------------------------------------------------------------- 1 | # Rendering Nodes 2 | You have two options for outputting your menu: 3 | 4 | ## Render Function 5 | 6 | ### craft.navigation.render() 7 | The easy option - let Navigation output the list items for you. This will generate a nested `