Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions components/icon-rail-nav/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ A distinctive navigation component for Retool apps, inspired by Supabase's dashb

![Icon Rail Nav](./cover.png)

---

## Why this component?

Most nav components force a tradeoff: compact (just icons, labels hidden) or verbose (takes up a ton of horizontal space). Icon Rail Nav gives you both — a tight **48px icon rail** that's always visible, a **contextual panel** that shows sub-items or descriptions for whatever's selected, and a **hover-reveal** that slides the full labels out from behind the rail when users need them.
Expand All @@ -21,7 +19,7 @@ The result: screen-efficient nav with zero navigation guesswork.
- **Sub-items** for deep navigation hierarchies
- **Badges, sections, and descriptions** per item
- **Dark and light themes** with custom accent colors
- **Custom icons** — built-in set, raw SVG strings, or image URLs
- **Custom icons** — built-in set, image URLs, or data URLs
- **Page and app navigation** via model state and event handlers
- **Developer help panel** with schema reference, icon picker, and copy-paste starter JSON (hideable before shipping)
- **Parse error surfacing** with helpful error messages for invalid JSON
Expand All @@ -31,7 +29,7 @@ The result: screen-efficient nav with zero navigation guesswork.
## Quickstart

1. Add the Icon Rail Nav component to your Retool app
2. Set the `ItemsJSON` property in the model panel — use the starter template below
2. Set the `Menu Items JSON` property in the model panel — use the starter template below
3. Wire the `Navigate` event handler → `Go to page` → `{{ iconRailNav1.model.activePage }}`
4. Done! Click an item to navigate.

Expand Down Expand Up @@ -70,19 +68,25 @@ The result: screen-efficient nav with zero navigation guesswork.

| Property | Type | Default | Description |
|---|---|---|---|
| `ItemsJSON` | string | `""` | JSON array of nav items (see schema below) |
| `BottomItemsJSON` | string | `""` | JSON array of items pinned to the bottom of the rail (e.g. Settings) |
| `menuEditorVisibility` | `"show"`/`"hide"` | `"hide"` | Shows or hides the visual menu item editor. |
| `helpVisibility` | `"show"`/`"hide"` | `"show"` | Shows or hides the setup help drawer. |
| `itemsJson` | string | `""` | Menu Items JSON: JSON array of nav items (see schema below) |
| `bottomItemsJson` | string | `""` | JSON array of items pinned to the bottom of the rail (e.g. Settings) |
| `activeItem` | string | `"item-1"` | The id of the currently selected item |
| `activeSubItem` | string | `""` | The id of the currently selected sub-item |
| `activePage` | string | `""` | The `page` property of the last-clicked item/sub-item — use in navigate handler |
| `activeApp` | string | `""` | The `app` property of the last-clicked item/sub-item |
| `projectName` | string | `"production-db"` | Label in the contextual panel header |
| `projectName` | string | `"Main Menu"` | Dynamic/bindable label in the contextual panel header |
| `projectStatus` | string | `"online"` | Status dot color: `"online"` (green), `"offline"` (gray), `"paused"` (amber) |
| `theme` | string | `"dark"` | `"dark"` or `"light"` — controls the contextual panel only (rail is always dark) |
| `railBg` | string | `""` | Custom rail background color (hex) |
| `panelBg` | string | `""` | Custom contextual panel background color (hex) |
| `accentColor` | string | `"#3ecf8e"` | Highlight color for active items, badges, and accents |
| `ShowHelp` | boolean | `true` | Shows the `?` help button for developers. Turn OFF before shipping to end users. |
| `padding` | number | `0` | Outer padding in pixels. Use a negative value to compensate for Retool wrapper inset. |
| `menuItems` | array | `[]` | Hidden visual editor output or directly bound array of nav item objects. Takes priority over `itemsJson` when non-empty. |
| `pageOptions` | array | `[]` | Optional page dropdown choices for the visual editor. Use strings or `{ label, value }` objects. |
| `appOptions` | array | `[]` | Optional app dropdown choices for the visual editor. Use strings or `{ label, value }` objects. |
| `menuJsonDraft` | string | `""` | Hidden JSON output for optional `saveMenu` handlers. |

---

Expand All @@ -92,7 +96,7 @@ The result: screen-efficient nav with zero navigation guesswork.
{
id: string; // required, unique
label: string; // required, display text
icon: string; // built-in key, SVG string, or image URL
icon: string; // built-in key, image URL, or data URL
page?: string; // Retool page name to navigate to
app?: string; // Retool app name to open
badge?: string; // small pill text, e.g. "New" or "Beta"
Expand All @@ -118,8 +122,7 @@ The result: screen-efficient nav with zero navigation guesswork.

`table` • `code` • `database` • `lock` • `storage` • `function` • `realtime` • `clock` • `info` • `logs` • `chart` • `settings`

For custom icons, use:
- **Raw SVG:** `"<svg ...>...</svg>"`
For custom icons, use a hosted image URL or a data URL:
- **URL:** `"https://example.com/icon.png"` (must be CORS-enabled)
- **Data URL:** `"data:image/png;base64,iVBORw0KGgo..."` (always works)

Expand All @@ -132,6 +135,7 @@ For custom icons, use:
| `itemClick` | A top-level rail item was clicked |
| `subItemClick` | A sub-item was clicked in the contextual panel |
| `navigate` | An item or sub-item with a `page`/`app` was clicked — wire this to `Go to page` action |
| `saveMenu` | The visual editor's Save event button was clicked |

---

Expand Down Expand Up @@ -162,6 +166,13 @@ For app navigation:

Inspired by the Supabase dashboard sidebar. Built for the Retool custom component contest.

## Contest submission checklist

- Exported component lives at `src/components/IconRailNav`
- `src/index.tsx` exports only `IconRailNav`
- Run `npm run typecheck` and `npm test` before submitting
- Add a final `cover.png` screenshot/GIF under 2MB for the gallery PR

## License

MIT
Binary file modified components/icon-rail-nav/cover.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions components/icon-rail-nav/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"id": "icon-rail-nav",
"title": "Icon Rail Nav",
"author": "@rgreen",
"shortDescription": "A screen-efficient navigation component with a compact icon rail, contextual sub-item panel, and slide-reveal labels on hover. Inspired by Supabase.",
"tags": ["Navigation", "Sidebar", "Layout", "UI"]
"shortDescription": "A space-saving navigation with a 48px icon rail, contextual sub-item panel, and slide-reveal labels on hover.",
"tags": ["Navigation", "UI Components", "Custom"]
}
64 changes: 64 additions & 0 deletions components/icon-rail-nav/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"name": "icon-rail-nav",
"version": "1.0.0",
"description": "A collapsible icon rail navigation sidebar for Retool.",
"license": "MIT",
"scripts": {
"dev": "npx retool-ccl dev",
"deploy": "npx retool-ccl deploy",
"test": "vitest run",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@tryretool/custom-component-support": "^1.0.0",
"react-dom": "^18.2.0",
"react": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"typescript": "^5.0.0",
"vitest": "^4.0.17"
},
"retoolCustomComponentLibraryConfig": {
"name": "RodGreenLibrary",
"label": "RodGreen Library",
"description": "My Retool Experiments",
"entryPoint": "src/index.tsx",
"outputPath": "dist",
"components": [
{
"id": "IconRailNav",
"name": "Icon Rail Nav",
"description": "Icon rail + contextual panel nav. Hover reveals item labels. Click shows sub-items or description in the right panel.",
"model": {
"menuEditorVisibility": "hide",
"helpVisibility": "show",
"itemsJson": "[{\"id\":\"item-1\",\"label\":\"Item 1\",\"icon\":\"table\",\"description\":\"Description for Item 1.\"},{\"id\":\"item-2\",\"label\":\"Item 2\",\"icon\":\"code\",\"description\":\"Description for Item 2.\"},{\"id\":\"item-3\",\"label\":\"Item 3\",\"icon\":\"database\",\"subItems\":[{\"id\":\"item-3-a\",\"label\":\"Sub Item A\"},{\"id\":\"item-3-b\",\"label\":\"Sub Item B\"},{\"id\":\"item-3-c\",\"label\":\"Sub Item C\"}]},{\"id\":\"item-4\",\"label\":\"Item 4\",\"icon\":\"chart\",\"badge\":\"New\",\"description\":\"Description for Item 4.\"}]",
"projectName": "Main Menu",
"projectStatus": "online",
"activeItem": "item-1",
"activeSubItem": "",
"activePage": "",
"activeApp": "",
"menuJsonDraft": "",
"bottomItemsJson": "[{\"id\":\"settings\",\"label\":\"Settings\",\"icon\":\"settings\",\"description\":\"Configure your project settings.\"}]",
"theme": "dark",
"railBg": "",
"panelBg": "",
"accentColor": "#3ecf8e",
"menuItems": [],
"pageOptions": [],
"appOptions": [],
"padding": 0
},
"events": [
{ "label": "Item clicked", "name": "itemClick" },
{ "label": "Sub-item clicked", "name": "subItemClick" },
{ "label": "Navigate", "name": "navigate" },
{ "label": "Save menu", "name": "saveMenu" }
]
}
]
}
}
Loading
Loading