starlight-mega-menu 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +34 -0
- package/.github/ISSUE_TEMPLATE/config.yml +2 -0
- package/.github/ISSUE_TEMPLATE/documentation.md +21 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- package/.github/workflows/auto-merge.yml +15 -0
- package/.github/workflows/enforce-repo-settings.yml +19 -0
- package/.github/workflows/release.yml +72 -0
- package/.github/workflows/require-linked-issue.yml +23 -0
- package/CLAUDE.md +35 -0
- package/CONTRIBUTING.md +85 -0
- package/LICENSE +21 -0
- package/components/Header.astro +125 -0
- package/components/MegaMenu.tsx +137 -0
- package/components/MegaMenuMobile.tsx +206 -0
- package/components/mega-menu.css +671 -0
- package/env.d.ts +4 -0
- package/index.ts +59 -0
- package/libs/vite.ts +21 -0
- package/package.json +33 -0
- package/release.config.js +24 -0
- package/tsconfig.json +3 -0
- package/types.ts +50 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug Report
|
|
3
|
+
description: Report a bug or unexpected behavior
|
|
4
|
+
labels: ["bug"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Describe the Bug
|
|
8
|
+
|
|
9
|
+
A clear and concise description of what the bug is.
|
|
10
|
+
|
|
11
|
+
## Steps to Reproduce
|
|
12
|
+
|
|
13
|
+
1. Go to '...'
|
|
14
|
+
2. Click on '...'
|
|
15
|
+
3. Scroll down to '...'
|
|
16
|
+
4. See error
|
|
17
|
+
|
|
18
|
+
## Expected Behavior
|
|
19
|
+
|
|
20
|
+
A clear and concise description of what you expected to happen.
|
|
21
|
+
|
|
22
|
+
## Actual Behavior
|
|
23
|
+
|
|
24
|
+
A clear and concise description of what actually happened.
|
|
25
|
+
|
|
26
|
+
## Environment
|
|
27
|
+
|
|
28
|
+
- OS: [e.g., macOS 14.0, Ubuntu 22.04]
|
|
29
|
+
- Browser: [e.g., Chrome 120, Firefox 121]
|
|
30
|
+
- Version/Commit: [e.g., commit SHA or tag]
|
|
31
|
+
|
|
32
|
+
## Additional Context
|
|
33
|
+
|
|
34
|
+
Add any other context, screenshots, or log output about the problem here.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Documentation
|
|
3
|
+
description: Suggest a documentation improvement or report missing docs
|
|
4
|
+
labels: ["documentation"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What Needs to Be Documented
|
|
8
|
+
|
|
9
|
+
Describe the topic, feature, or area that needs documentation.
|
|
10
|
+
|
|
11
|
+
## Current State
|
|
12
|
+
|
|
13
|
+
Describe the current state of the documentation (missing, incomplete, outdated, unclear).
|
|
14
|
+
|
|
15
|
+
## Suggested Improvement
|
|
16
|
+
|
|
17
|
+
Describe what the documentation should cover or how it should be improved.
|
|
18
|
+
|
|
19
|
+
## Additional Context
|
|
20
|
+
|
|
21
|
+
Add any references, examples, or links that would help with the documentation.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature Request
|
|
3
|
+
description: Suggest a new feature or improvement
|
|
4
|
+
labels: ["enhancement"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Feature Description
|
|
8
|
+
|
|
9
|
+
A clear and concise description of the feature you'd like.
|
|
10
|
+
|
|
11
|
+
## Use Case / Motivation
|
|
12
|
+
|
|
13
|
+
Explain the problem this feature would solve or the value it would provide.
|
|
14
|
+
|
|
15
|
+
## Proposed Solution
|
|
16
|
+
|
|
17
|
+
Describe how you'd like this feature to work.
|
|
18
|
+
|
|
19
|
+
## Alternatives Considered
|
|
20
|
+
|
|
21
|
+
Describe any alternative solutions or features you've considered.
|
|
22
|
+
|
|
23
|
+
## Additional Context
|
|
24
|
+
|
|
25
|
+
Add any other context, mockups, or references about the feature request here.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
Brief description of the changes in this PR.
|
|
4
|
+
|
|
5
|
+
## Related Issue
|
|
6
|
+
|
|
7
|
+
Closes #
|
|
8
|
+
|
|
9
|
+
## Changes
|
|
10
|
+
|
|
11
|
+
-
|
|
12
|
+
|
|
13
|
+
## Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Linked to a GitHub issue (required — CI will block merge without it)
|
|
16
|
+
- [ ] Tested locally
|
|
17
|
+
- [ ] Follows project conventions
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: Auto Merge
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, reopened, ready_for_review]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
pull-requests: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
auto-merge:
|
|
13
|
+
uses: robinmordasiewicz/f5xc-template/.github/workflows/auto-merge.yml@main
|
|
14
|
+
secrets:
|
|
15
|
+
REPO_ADMIN_TOKEN: ${{ secrets.REPO_ADMIN_TOKEN }}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: Enforce Repository Settings
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: '0 */6 * * *'
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
paths:
|
|
9
|
+
- '.github/config/**'
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
enforce:
|
|
17
|
+
uses: robinmordasiewicz/f5xc-template/.github/workflows/enforce-repo-settings.yml@main
|
|
18
|
+
secrets:
|
|
19
|
+
repo-admin-token: ${{ secrets.REPO_ADMIN_TOKEN }}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths-ignore:
|
|
7
|
+
- '.github/**'
|
|
8
|
+
- '.editorconfig'
|
|
9
|
+
- '.gitignore'
|
|
10
|
+
- 'CLAUDE.md'
|
|
11
|
+
- 'CONTRIBUTING.md'
|
|
12
|
+
- 'README.md'
|
|
13
|
+
- 'LICENSE'
|
|
14
|
+
- 'release.config.js'
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: write
|
|
19
|
+
issues: write
|
|
20
|
+
pull-requests: write
|
|
21
|
+
id-token: write
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
release:
|
|
25
|
+
name: Semantic Release
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, 'chore(release):')"
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout
|
|
30
|
+
uses: actions/checkout@v4
|
|
31
|
+
with:
|
|
32
|
+
fetch-depth: 0
|
|
33
|
+
|
|
34
|
+
- name: Setup Node.js
|
|
35
|
+
uses: actions/setup-node@v4
|
|
36
|
+
with:
|
|
37
|
+
node-version: 'lts/*'
|
|
38
|
+
registry-url: 'https://registry.npmjs.org'
|
|
39
|
+
|
|
40
|
+
- name: Install semantic-release
|
|
41
|
+
run: >
|
|
42
|
+
npm install --no-save
|
|
43
|
+
semantic-release
|
|
44
|
+
@semantic-release/commit-analyzer
|
|
45
|
+
@semantic-release/release-notes-generator
|
|
46
|
+
@semantic-release/npm
|
|
47
|
+
@semantic-release/github
|
|
48
|
+
|
|
49
|
+
- name: Run semantic-release
|
|
50
|
+
run: |
|
|
51
|
+
max_attempts=3
|
|
52
|
+
attempt=1
|
|
53
|
+
delay=10
|
|
54
|
+
while [ $attempt -le $max_attempts ]; do
|
|
55
|
+
echo "Attempt $attempt of $max_attempts"
|
|
56
|
+
if npx semantic-release; then
|
|
57
|
+
echo "semantic-release succeeded"
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
if [ $attempt -eq $max_attempts ]; then
|
|
61
|
+
echo "semantic-release failed after $max_attempts attempts"
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
echo "Retrying in ${delay}s..."
|
|
65
|
+
sleep $delay
|
|
66
|
+
delay=$((delay * 2))
|
|
67
|
+
attempt=$((attempt + 1))
|
|
68
|
+
done
|
|
69
|
+
env:
|
|
70
|
+
GITHUB_TOKEN: ${{ secrets.REPO_ADMIN_TOKEN }}
|
|
71
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
72
|
+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Require Linked Issue
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request_target:
|
|
5
|
+
types: [opened, edited, reopened, synchronize]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
issues: read
|
|
9
|
+
pull-requests: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
check-linked-issue:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
name: Check linked issues
|
|
15
|
+
steps:
|
|
16
|
+
- uses: nearform-actions/github-action-check-linked-issues@v1
|
|
17
|
+
with:
|
|
18
|
+
exclude-branches: "dependabot/**"
|
|
19
|
+
custom-message: >-
|
|
20
|
+
This PR must be linked to a GitHub issue. Use 'Closes #123'
|
|
21
|
+
in the PR description, or link an issue from the sidebar.
|
|
22
|
+
env:
|
|
23
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Claude Code Project Instructions
|
|
2
|
+
|
|
3
|
+
## Repository Workflow
|
|
4
|
+
|
|
5
|
+
This repo enforces a strict governance workflow. Follow it exactly:
|
|
6
|
+
|
|
7
|
+
1. **Create a GitHub issue** before making any changes
|
|
8
|
+
2. **Create a feature branch** from `main` — never commit to `main` directly
|
|
9
|
+
3. **Open a PR** that links to the issue using `Closes #N`
|
|
10
|
+
4. **CI must pass** — the "Check linked issues" check blocks PRs without a linked issue
|
|
11
|
+
5. **Merge** — squash merge preferred, branch auto-deletes after merge
|
|
12
|
+
|
|
13
|
+
## Use the `/ship` Skill
|
|
14
|
+
|
|
15
|
+
When available, use `/ship` to handle the full workflow (issue creation, branch, commit, PR) in one step.
|
|
16
|
+
|
|
17
|
+
## Branch Naming
|
|
18
|
+
|
|
19
|
+
Use the format `<prefix>/<issue-number>-short-description`:
|
|
20
|
+
|
|
21
|
+
- `feature/42-add-grid-layout`
|
|
22
|
+
- `fix/17-correct-animation`
|
|
23
|
+
- `docs/8-update-guide`
|
|
24
|
+
|
|
25
|
+
## Rules
|
|
26
|
+
|
|
27
|
+
- Never push directly to `main`
|
|
28
|
+
- Never force push
|
|
29
|
+
- Every PR must link to an issue
|
|
30
|
+
- Fill out the PR template completely
|
|
31
|
+
- Follow conventional commit messages (`feat:`, `fix:`, `docs:`)
|
|
32
|
+
|
|
33
|
+
## Reference
|
|
34
|
+
|
|
35
|
+
Read `CONTRIBUTING.md` for full governance details.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
This document describes the workflow and rules that all contributors — human and AI — must follow.
|
|
4
|
+
|
|
5
|
+
## Workflow Overview
|
|
6
|
+
|
|
7
|
+
Every change follows this path:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Issue → Branch → PR (linked to issue) → CI passes → Merge → Branch auto-deleted
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
No exceptions. PRs without a linked issue will be blocked by CI.
|
|
14
|
+
|
|
15
|
+
## Step 1: Create an Issue
|
|
16
|
+
|
|
17
|
+
Every change starts with a GitHub issue. Use one of the provided templates:
|
|
18
|
+
|
|
19
|
+
- **Bug Report** — for bugs and unexpected behavior
|
|
20
|
+
- **Feature Request** — for new features and improvements
|
|
21
|
+
- **Documentation** — for docs improvements or missing content
|
|
22
|
+
|
|
23
|
+
Blank issues are disabled. Pick the template that best fits your change.
|
|
24
|
+
|
|
25
|
+
## Step 2: Create a Feature Branch
|
|
26
|
+
|
|
27
|
+
Branch from `main` using one of these naming conventions:
|
|
28
|
+
|
|
29
|
+
| Prefix | Use for | Example |
|
|
30
|
+
|--------|---------|---------|
|
|
31
|
+
| `feature/` | New features | `feature/42-add-grid-layout` |
|
|
32
|
+
| `fix/` | Bug fixes | `fix/17-correct-animation-timing` |
|
|
33
|
+
| `docs/` | Documentation | `docs/8-update-setup-guide` |
|
|
34
|
+
|
|
35
|
+
Format: `<prefix>/<issue-number>-short-description`
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git checkout main
|
|
39
|
+
git pull origin main
|
|
40
|
+
git checkout -b feature/42-add-grid-layout
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Step 3: Make Changes and Commit
|
|
44
|
+
|
|
45
|
+
- Write small, focused commits
|
|
46
|
+
- Use conventional commit messages:
|
|
47
|
+
- `feat: add grid layout support`
|
|
48
|
+
- `fix: correct animation timing`
|
|
49
|
+
- `docs: update setup guide`
|
|
50
|
+
|
|
51
|
+
## Step 4: Open a Pull Request
|
|
52
|
+
|
|
53
|
+
1. Push your branch and open a PR against `main`
|
|
54
|
+
2. **Link the issue** — use `Closes #42` in the PR description, or link from the sidebar
|
|
55
|
+
3. Fill out the PR template (it loads automatically)
|
|
56
|
+
4. The **"Check linked issues"** CI check will block merge if no issue is linked
|
|
57
|
+
|
|
58
|
+
## Step 5: Review and Merge
|
|
59
|
+
|
|
60
|
+
- All CI checks must pass before merge
|
|
61
|
+
- Auto-merge is enabled — PRs merge automatically once all checks pass
|
|
62
|
+
- Squash merge is preferred
|
|
63
|
+
- The branch is automatically deleted after merge (`delete_branch_on_merge` is enabled)
|
|
64
|
+
|
|
65
|
+
## Branch Protection Rules
|
|
66
|
+
|
|
67
|
+
The `main` branch is protected. The following rules are enforced:
|
|
68
|
+
|
|
69
|
+
- No direct pushes to `main` — all changes go through PRs
|
|
70
|
+
- No force pushes
|
|
71
|
+
- Required status check: **"Check linked issues"** must pass
|
|
72
|
+
- Admin enforcement enabled — these rules apply to everyone
|
|
73
|
+
|
|
74
|
+
## AI Assistant Guidelines
|
|
75
|
+
|
|
76
|
+
If you are Claude Code, Copilot, or another AI coding assistant, follow these rules:
|
|
77
|
+
|
|
78
|
+
1. **Always create a GitHub issue before writing code.** No issue = no work.
|
|
79
|
+
2. **Always work on a feature branch.** Never commit directly to `main`.
|
|
80
|
+
3. **Always link the PR to the issue.** Use `Closes #N` in the PR description.
|
|
81
|
+
4. **Use the `/ship` skill** when available — it handles the full Issue → Branch → PR flow.
|
|
82
|
+
5. **Never force push** or attempt to bypass branch protection.
|
|
83
|
+
6. **Fill out the PR template checklist** completely.
|
|
84
|
+
7. **Follow the branch naming convention**: `feature/<issue>-desc`, `fix/<issue>-desc`, `docs/<issue>-desc`.
|
|
85
|
+
8. **Respect CODEOWNERS** — `@robinmordasiewicz` is the default reviewer.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Robin Mordasiewicz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
import config from 'virtual:starlight/user-config';
|
|
3
|
+
|
|
4
|
+
import LanguageSelect from 'virtual:starlight/components/LanguageSelect';
|
|
5
|
+
import Search from 'virtual:starlight/components/Search';
|
|
6
|
+
import SiteTitle from 'virtual:starlight/components/SiteTitle';
|
|
7
|
+
import SocialIcons from 'virtual:starlight/components/SocialIcons';
|
|
8
|
+
import ThemeSelect from 'virtual:starlight/components/ThemeSelect';
|
|
9
|
+
import megaMenuConfig from 'virtual:starlight-mega-menu/config';
|
|
10
|
+
import MegaMenu from './MegaMenu.tsx';
|
|
11
|
+
import MegaMenuMobile from './MegaMenuMobile.tsx';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Render the `Search` component if Pagefind is enabled or the default search component has been overridden.
|
|
15
|
+
*/
|
|
16
|
+
const shouldRenderSearch =
|
|
17
|
+
config.pagefind || config.components.Search !== '@astrojs/starlight/components/Search.astro';
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<div class="header sl-mega-menu-header">
|
|
21
|
+
<div class="title-wrapper sl-flex">
|
|
22
|
+
<SiteTitle />
|
|
23
|
+
</div>
|
|
24
|
+
<nav class="smm-desktop-nav">
|
|
25
|
+
<MegaMenu client:load config={megaMenuConfig} />
|
|
26
|
+
</nav>
|
|
27
|
+
<div class="sl-flex print:hidden">
|
|
28
|
+
{shouldRenderSearch && <Search />}
|
|
29
|
+
</div>
|
|
30
|
+
<div class="sl-hidden md:sl-flex print:hidden right-group">
|
|
31
|
+
<div class="sl-flex social-icons">
|
|
32
|
+
<SocialIcons />
|
|
33
|
+
</div>
|
|
34
|
+
<ThemeSelect />
|
|
35
|
+
<LanguageSelect />
|
|
36
|
+
</div>
|
|
37
|
+
<div class="smm-mobile-toggle">
|
|
38
|
+
<MegaMenuMobile client:load config={megaMenuConfig} />
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
@layer starlight.core {
|
|
44
|
+
.header {
|
|
45
|
+
display: flex;
|
|
46
|
+
gap: var(--sl-nav-gap);
|
|
47
|
+
justify-content: space-between;
|
|
48
|
+
align-items: center;
|
|
49
|
+
height: 100%;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.title-wrapper {
|
|
53
|
+
/* Prevent long titles overflowing and covering the search and menu buttons on narrow viewports. */
|
|
54
|
+
overflow: clip;
|
|
55
|
+
/* Avoid clipping focus ring around link inside title wrapper. */
|
|
56
|
+
padding: 0.25rem;
|
|
57
|
+
margin: -0.25rem;
|
|
58
|
+
min-width: 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.right-group,
|
|
62
|
+
.social-icons {
|
|
63
|
+
gap: 1rem;
|
|
64
|
+
align-items: center;
|
|
65
|
+
}
|
|
66
|
+
.social-icons::after {
|
|
67
|
+
content: '';
|
|
68
|
+
height: 2rem;
|
|
69
|
+
border-inline-end: 1px solid var(--sl-color-gray-5);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Desktop nav is hidden on small screens */
|
|
73
|
+
.smm-desktop-nav {
|
|
74
|
+
display: none;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Mobile toggle is visible on small screens */
|
|
78
|
+
.smm-mobile-toggle {
|
|
79
|
+
display: flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@media (min-width: 50rem) {
|
|
84
|
+
:global(:root[data-has-sidebar]) {
|
|
85
|
+
--__sidebar-pad: calc(2 * var(--sl-nav-pad-x));
|
|
86
|
+
}
|
|
87
|
+
:global(:root:not([data-has-toc])) {
|
|
88
|
+
--__toc-width: 0rem;
|
|
89
|
+
}
|
|
90
|
+
.header {
|
|
91
|
+
--__sidebar-width: max(0rem, var(--sl-content-inline-start, 0rem) - var(--sl-nav-pad-x));
|
|
92
|
+
--__main-column-fr: calc(
|
|
93
|
+
(
|
|
94
|
+
100% + var(--__sidebar-pad, 0rem) - var(--__toc-width, var(--sl-sidebar-width)) -
|
|
95
|
+
(2 * var(--__toc-width, var(--sl-nav-pad-x))) - var(--sl-content-inline-start, 0rem) -
|
|
96
|
+
var(--sl-content-width)
|
|
97
|
+
) / 2
|
|
98
|
+
);
|
|
99
|
+
display: grid;
|
|
100
|
+
grid-template-columns:
|
|
101
|
+
/* 1 (site title) */
|
|
102
|
+
auto
|
|
103
|
+
/* 2 (mega-menu nav) */
|
|
104
|
+
1fr
|
|
105
|
+
/* 3 (search box) */
|
|
106
|
+
auto
|
|
107
|
+
/* 4 (right items) */
|
|
108
|
+
auto;
|
|
109
|
+
align-content: center;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Show desktop nav, hide mobile toggle */
|
|
113
|
+
.smm-desktop-nav {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
justify-content: center;
|
|
117
|
+
min-width: 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.smm-mobile-toggle {
|
|
121
|
+
display: none;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
</style>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as NavigationMenu from '@radix-ui/react-navigation-menu';
|
|
2
|
+
import type { MegaMenuConfig, MegaMenuPanel, MegaMenuCategory, MegaMenuLink as MegaMenuLinkType, MegaMenuFooter } from '../types';
|
|
3
|
+
|
|
4
|
+
function ChevronDown({ className }: { className?: string }) {
|
|
5
|
+
return (
|
|
6
|
+
<svg
|
|
7
|
+
className={className}
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
width="12"
|
|
10
|
+
height="12"
|
|
11
|
+
viewBox="0 0 12 12"
|
|
12
|
+
fill="none"
|
|
13
|
+
aria-hidden="true"
|
|
14
|
+
>
|
|
15
|
+
<path
|
|
16
|
+
d="M2.5 4.5L6 8L9.5 4.5"
|
|
17
|
+
stroke="currentColor"
|
|
18
|
+
strokeWidth="1.5"
|
|
19
|
+
strokeLinecap="round"
|
|
20
|
+
strokeLinejoin="round"
|
|
21
|
+
/>
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function PanelContent({ panel }: { panel: MegaMenuPanel }) {
|
|
27
|
+
const layout = panel.layout ?? 'list';
|
|
28
|
+
const columns = panel.columns ?? 2;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
className="smm-panel"
|
|
33
|
+
data-layout={layout}
|
|
34
|
+
style={layout === 'grid' ? { '--smm-columns': columns } as React.CSSProperties : undefined}
|
|
35
|
+
>
|
|
36
|
+
{panel.categories?.map((category) => (
|
|
37
|
+
<CategorySection key={category.title} category={category} />
|
|
38
|
+
))}
|
|
39
|
+
{panel.footer && <PanelFooter footer={panel.footer} />}
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function CategorySection({ category }: { category: MegaMenuCategory }) {
|
|
45
|
+
return (
|
|
46
|
+
<div className="smm-category">
|
|
47
|
+
<h3 className="smm-category-title">{category.title}</h3>
|
|
48
|
+
<ul className="smm-category-list">
|
|
49
|
+
{category.items.map((item) => (
|
|
50
|
+
<li key={item.href}>
|
|
51
|
+
<LinkItem item={item} />
|
|
52
|
+
</li>
|
|
53
|
+
))}
|
|
54
|
+
</ul>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function LinkItem({ item }: { item: MegaMenuLinkType }) {
|
|
60
|
+
return (
|
|
61
|
+
<NavigationMenu.Link className="smm-menu-link" href={item.href}>
|
|
62
|
+
{item.icon && (
|
|
63
|
+
<span className="smm-link-icon" dangerouslySetInnerHTML={{ __html: item.icon }} />
|
|
64
|
+
)}
|
|
65
|
+
<span className="smm-link-text">
|
|
66
|
+
<span className="smm-link-label">{item.label}</span>
|
|
67
|
+
{item.description && (
|
|
68
|
+
<span className="smm-link-description">{item.description}</span>
|
|
69
|
+
)}
|
|
70
|
+
</span>
|
|
71
|
+
</NavigationMenu.Link>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function PanelFooter({ footer }: { footer: MegaMenuFooter }) {
|
|
76
|
+
return (
|
|
77
|
+
<div className="smm-panel-footer">
|
|
78
|
+
<NavigationMenu.Link className="smm-footer-link" href={footer.href}>
|
|
79
|
+
<span className="smm-footer-label">{footer.label}</span>
|
|
80
|
+
{footer.description && (
|
|
81
|
+
<span className="smm-footer-description">{footer.description}</span>
|
|
82
|
+
)}
|
|
83
|
+
<svg
|
|
84
|
+
className="smm-footer-arrow"
|
|
85
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
86
|
+
width="14"
|
|
87
|
+
height="14"
|
|
88
|
+
viewBox="0 0 14 14"
|
|
89
|
+
fill="none"
|
|
90
|
+
aria-hidden="true"
|
|
91
|
+
>
|
|
92
|
+
<path
|
|
93
|
+
d="M1.75 7H12.25M12.25 7L7.875 2.625M12.25 7L7.875 11.375"
|
|
94
|
+
stroke="currentColor"
|
|
95
|
+
strokeWidth="1.5"
|
|
96
|
+
strokeLinecap="round"
|
|
97
|
+
strokeLinejoin="round"
|
|
98
|
+
/>
|
|
99
|
+
</svg>
|
|
100
|
+
</NavigationMenu.Link>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export default function MegaMenu({ config }: { config: MegaMenuConfig }) {
|
|
106
|
+
return (
|
|
107
|
+
<NavigationMenu.Root className="smm-root" delayDuration={200} skipDelayDuration={300}>
|
|
108
|
+
<NavigationMenu.List className="smm-list">
|
|
109
|
+
{config.items.map((item) =>
|
|
110
|
+
item.content ? (
|
|
111
|
+
<NavigationMenu.Item key={item.label} value={item.label}>
|
|
112
|
+
<NavigationMenu.Trigger className="smm-trigger">
|
|
113
|
+
{item.label}
|
|
114
|
+
<ChevronDown className="smm-chevron" />
|
|
115
|
+
</NavigationMenu.Trigger>
|
|
116
|
+
<NavigationMenu.Content className="smm-content">
|
|
117
|
+
<PanelContent panel={item.content} />
|
|
118
|
+
</NavigationMenu.Content>
|
|
119
|
+
</NavigationMenu.Item>
|
|
120
|
+
) : (
|
|
121
|
+
<NavigationMenu.Item key={item.label}>
|
|
122
|
+
<NavigationMenu.Link className="smm-link" href={item.href}>
|
|
123
|
+
{item.label}
|
|
124
|
+
</NavigationMenu.Link>
|
|
125
|
+
</NavigationMenu.Item>
|
|
126
|
+
)
|
|
127
|
+
)}
|
|
128
|
+
<NavigationMenu.Indicator className="smm-indicator">
|
|
129
|
+
<div className="smm-arrow" />
|
|
130
|
+
</NavigationMenu.Indicator>
|
|
131
|
+
</NavigationMenu.List>
|
|
132
|
+
<div className="smm-viewport-wrapper">
|
|
133
|
+
<NavigationMenu.Viewport className="smm-viewport" />
|
|
134
|
+
</div>
|
|
135
|
+
</NavigationMenu.Root>
|
|
136
|
+
);
|
|
137
|
+
}
|