npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

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

About

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

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

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

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

Open Software & Tools

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

© 2024 – Pkg Stats / Ryan Hefner

tree-menus

v1.0.4

Published

Menu manage and custom tree field

Downloads

267

Readme

Get Started

✨ Features

  • Consumable menu data which can be used to render navigation and other menus in a frontend app.
  • Easily manage menus with either a flat or nested structure.
  • Customize the title, url, isProtected, and link target of menu items.
  • Extend the schema and UI with custom attributes for menu items.
  • Support for all Strapi field types in the UI.
  • Support i18n translations for menu attributes.
  • Support RBAC permissions for plugin.

💎 Installation

yarn add strapi-plugin-tree-menus@latest

Hot fix issue between vite and traverse

  • Unsupported fieldtype: plugin::tree-menus.tree
  • Vite 'global is not defined'

https://stackoverflow.com/questions/72114775/vite-global-is-not-defined

to fix enable global to browser:

  • create vite.config.ts in [root]/src/admin
import { mergeConfig, type UserConfig } from 'vite'

export default (config: UserConfig) => {
  // Important: always return the modified config
  return mergeConfig(config, {
    resolve: {
      alias: {
        '@': '/src',
      },
    },
    optimizeDeps: {
      esbuildOptions: {
        // Node.js global to browser globalThis
        define: {
          global: 'globalThis',
        },
      },
    },
  })
}

Don't forget to restart or rebuild your Strapi app when installing a new plugin.

🔧 Configuration

| property | type (default) | description | | ----------- | -------------- | ---------------------- | | fieldSchema | object ({}) | Schema for menu items. |

fieldSchema

The fieldSchema prop is an object that defines the schema for menu items. This is where you can add custom attributes to the MenuItem schema. The example below demonstrates how to add a custom example_field attribute to the MenuItem schema.

Example

// ./config/plugins.js`
'use strict'

module.exports = {
  'tree-menus': {
    config: {
      fieldSchema: {
        attributes: [
          {
            id: 'title',
            label: 'Title',
            placeholder: 'Enter item title',
            type: 'text',
            validationType: 'string',
            value: 'New items',
            required: true,
            validations: [
              {
                type: 'required',
                params: ['this field is required'],
              },
              {
                type: 'max',
                params: [100, 'Title cannot be more than 100 characters'],
              },
              {
                type: 'default',
                params: ['New items'],
              },
            ],
          },
          {
            id: 'url',
            label: 'Url',
            placeholder: 'Enter url',
            type: 'text',
            validationType: 'string',
            value: '/',
            required: true,
            validations: [
              {
                type: 'required',
                params: ['this field is required'],
              },
              {
                type: 'max',
                params: [200, 'Url cannot be more than 200 characters'],
              },
              {
                type: 'default',
                params: ['/'],
              },
            ],
          },
          {
            id: 'target',
            label: 'Target',
            placeholder: 'Enter target',
            type: 'select',
            validationType: 'mixed',
            value: '_self',
            required: true,
            validations: [
              {
                type: 'oneOf',
                params: [
                  ['_blank', '_parent', '_self', '_top'],
                  'this field needs to be one of the following: _blank, _parent, _self, _top',
                ],
              },
              {
                type: 'default',
                params: ['_self'],
              },
            ],
            options: [
              {
                key: '_blank',
                value: '_blank',
                metadatas: {
                  intlLabel: {
                    id: 'tree-menus.target.options._blank',
                    defaultMessage: 'New window (_blank)',
                  },
                  disabled: false,
                  hidden: false,
                },
              },
              {
                key: '_parent',
                value: '_parent',
                metadatas: {
                  intlLabel: {
                    id: 'tree-menus.target.options._parent',
                    defaultMessage: 'Parent window (_parent)',
                  },
                  disabled: false,
                  hidden: false,
                },
              },
              {
                key: '_self',
                value: '_self',
                metadatas: {
                  intlLabel: {
                    id: 'tree-menus.target.options._self',
                    defaultMessage: 'Same window (_self)',
                  },
                  disabled: false,
                  hidden: false,
                },
              },
              {
                key: '_top',
                value: '_top',
                metadatas: {
                  intlLabel: {
                    id: 'tree-menus.target.options._top',
                    defaultMessage: 'Top window (_top)',
                  },
                  disabled: false,
                  hidden: false,
                },
              },
            ],
          },
          {
            id: 'isProtected',
            label: 'isProtected',
            placeholder: 'Choose isProtected',
            type: 'bool',
            validationType: 'boolean',
            value: false,
            required: true,
            validations: [
              {
                type: 'required',
                params: ['Need to choose isProtected'],
              },
              {
                type: 'default',
                params: [false],
              },
            ],
          },
        ],
      },
    },
  },
}

Enable menus in Documentation plugin

Default Strapi documentation included the tree-menus documentation.

Supported field types

The following field types in the table below are supported. Some fields use a different type value for the schema and input type.

| Field | Schema Type | Input Type | | ----------- | ------------------------------------------- | ---------------------------- | | Boolean | boolean | bool | | Date | date, time, datetime | same | | Email | email | same | | Enumeration | enumeration | select | | Media | media | same | | Number | integer, biginteger, decimal, float | number | | Password | password | same | | Rich Text | richtext | wysiwyg | | Text | string, text | string, text, textarea |

The following field types are NOT supported:

  • Component
  • Dynamic Zone
  • UID
  • JSON

NOTE: By default, rich text fields are not supported unless a custom plugin overrides the core WYSIWYG editor, which is covered in the Strapi guide to creating a new WYSIWYG field in the admin panel.

📘 User Guide

Create

On the menus plugin home page, use the "Create new menu" button to get started. You will need to provide a title and a unique slug value for the new menu. Saving the menu before adding menu items is recommended but not required.

Clone

Choosing to clone an existing menu will take you to the edit view as usual, but this time it will be pre-populated with another menu's data. Once the cloned menu is saved, a brand new menu and menu items are created.

Delete

Deleting a menu will also delete all of it's menu items.

Edit

When clicking on a menu item in the left column, it will reveal action buttons to move the item, delete it, or give it a submenu.

The right column will reveal the edit UI for that item, where the title is the only required field.

⚡ API Usage

Fetching menus data is the same as fetching any other data using Strapi's REST API features.

Don't forget to enable the public methods for Menu and MenuItem in the Users and Permissions settings, like find and findOne.

Endpoints

| request | endpoint | description | | -------- | ---------------------------------- | ----------------- | | GET | /api/tree-menus/menu | Fetch all menus. | | GET | /api/tree-menus/menu/:id | Fetch one menu. | | POST | /api/tree-menus/menu/:id | Create a menu. | | PUT | /api/tree-menus/menu/:id | Update a menu. | | DELETE | /api/tree-menus/menu/:id | Delete a menu. | | POST | /api/tree-menus/menu/bulk-delete | Delete many menu. |

Basic example

Fetch a menu with the documentId. Nothing is populated by default.

await fetch('/api/tree-menus/menu/${documentId}')
Response
{
  "data": {
    "id": 1,
    "documentId": "jke4feqw23h1",
    "title": "Main Menu",
    "slug": "main-menu",
    "items": [
      {
        "id": "1",
        "title": "Home",
        "url": "/",
        "target": "_self",
        "isProtected": false,
        "children": []
      },
      {
        "id": "2",
        "title": "About",
        "url": "/about",
        "target": "_self",
        "isProtected": false,
        "children": [
          {
            "id": "2.1",
            "title": "Our Team",
            "url": "/about/our-team",
            "target": "_self",
            "isProtected": false,
            "children": []
          },
          {
            "id": "2.2",
            "title": "Our Mission",
            "url": "/about/our-mission",
            "target": "_self",
            "isProtected": false,
            "children": []
          }
        ]
      }
    ],
    "createdAt": "2024-10-07T08:00:00.000Z",
    "updatedAt": "2024-10-07T08:00:00.000Z",
    "publishedAt": "2024-10-07T08:00:00.000Z"
  },
  "meta": {}
}

💩 Troubleshooting

In general

Remember to rebuild your app after making changes to some config or other code.

yarn build
# OR
yarn develop

Custom MenuItem attributes save in the schema or config.

If you are having trouble saving custom attributes in the MenuItem schema, make sure that the fieldSchema object is properly configured in the config/plugins.js file. fieldSchema only supports the following field types: string, text, number, bool, select, date, time, datetime, email.

I can't see config fieldSchema in the Strapi admin panel.

❤️ Support or Donate

If you are enjoying this plugin and feel extra appreciative, you can buy me a beer or 3 🍺🍺🍺.

🚧 Roadmap

📸 Gallery