Skip to content

Commit 0bca97c

Browse files
committed
feat: Initial implementation
0 parents  commit 0bca97c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+603
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*.tgz
2+
node_modules
3+
coverage
4+
.nyc_output
5+
package

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package-lock=false

.travis.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
language: node_js
2+
3+
node_js:
4+
- 12
5+
6+
addons:
7+
chrome: stable
8+
firefox: latest

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2019 CFWare, LLC
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.

README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# @cfware/nav-menu
2+
3+
[![Travis CI][travis-image]][travis-url]
4+
[![Greenkeeper badge][gk-image]](https://greenkeeper.io/)
5+
[![NPM Version][npm-image]][npm-url]
6+
[![NPM Downloads][downloads-image]][downloads-url]
7+
[![MIT][license-image]](LICENSE)
8+
9+
Navigation menu element
10+
11+
12+
### Install @cfware/nav-menu
13+
14+
```sh
15+
npm i @cfware/nav-menu
16+
```
17+
18+
19+
## Usage
20+
21+
Import to your application:
22+
```js
23+
import '@cfware/nav-menu';
24+
```
25+
26+
Use to create a navigation element:
27+
```html
28+
<style>
29+
nav-section {
30+
--expander-font-family: 'Font Family to use when rendering  and ';
31+
}
32+
</style>
33+
<nav-menu>
34+
<nav-section title="General">
35+
<nav-item title="Welcome"></nav-item>
36+
<nav-item href=software title="Software"></nav-item>
37+
</nav-section>
38+
</nav-menu>
39+
```
40+
41+
42+
## Running tests
43+
44+
Testing is provided by ava and xo.
45+
46+
```sh
47+
npm install
48+
npm test
49+
```
50+
51+
[npm-image]: https://img.shields.io/npm/v/@cfware/nav-menu.svg
52+
[npm-url]: https://npmjs.org/package/@cfware/nav-menu
53+
[travis-image]: https://travis-ci.org/cfware/nav-menu.svg?branch=master
54+
[travis-url]: https://travis-ci.org/cfware/nav-menu
55+
[gk-image]: https://badges.greenkeeper.io/cfware/nav-menu.svg
56+
[downloads-image]: https://img.shields.io/npm/dm/@cfware/nav-menu.svg
57+
[downloads-url]: https://npmjs.org/package/@cfware/nav-menu
58+
[license-image]: https://img.shields.io/npm/l/@cfware/nav-menu.svg

nav-item.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import {ShadowElement, html} from '@cfware/shadow-element';
2+
3+
class NavItem extends ShadowElement {
4+
constructor() {
5+
super();
6+
7+
const observer = new MutationObserver(() => this.dispatchEvent(new Event('hiddenchange')));
8+
observer.observe(this, {
9+
attributes: true,
10+
attributeFilter: ['hidden']
11+
});
12+
}
13+
14+
get pathname() {
15+
return new URL(this.href, document.baseURI).pathname;
16+
}
17+
18+
get template() {
19+
return html`
20+
<style>
21+
a {
22+
display: block;
23+
position: relative;
24+
margin-left: .85rem;
25+
padding: .83rem .95rem .83rem .85rem;
26+
color: #000a;
27+
text-decoration: none;
28+
line-height: 1;
29+
border-left: .2rem solid transparent;
30+
}
31+
32+
a:hover {
33+
color: #005d90;
34+
}
35+
36+
a:focus {
37+
background-color: #8881;
38+
}
39+
40+
a:active {
41+
background-color: #0001;
42+
}
43+
44+
:host(:not(:first-child)) a {
45+
border-top: 1px solid #8882;
46+
}
47+
48+
:host([active]) a {
49+
border-left-color: unset;
50+
}
51+
</style>
52+
<a href=${this.href}>${this.title}</a>
53+
`;
54+
}
55+
}
56+
57+
NavItem.define('nav-item', {
58+
stringProps: {
59+
title: '',
60+
href: '.'
61+
},
62+
booleanProps: ['active']
63+
});

nav-menu.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {ShadowElement, html} from '@cfware/shadow-element';
2+
import './nav-section';
3+
import './nav-item';
4+
5+
class NavMenu extends ShadowElement {
6+
update() {
7+
const {pathname} = location;
8+
const items = [...this.querySelectorAll('nav-item')];
9+
const matches = items
10+
.filter(item => pathname.startsWith(item.pathname))
11+
.sort((a, b) => b.pathname.length - a.pathname.length);
12+
13+
items.forEach(item => {
14+
item.active = item === matches[0];
15+
});
16+
17+
const sections = [...this.querySelectorAll('nav-section:not([hidden])')];
18+
if (sections.length === 0) {
19+
return;
20+
}
21+
22+
const activeSections = sections.filter(section => {
23+
if (!section.active) {
24+
section.active = section.querySelectorAll('nav-item[active]:not([hidden])').length !== 0;
25+
}
26+
27+
return section.active;
28+
});
29+
30+
if (matches.length === 0 || activeSections.length === 0) {
31+
sections[0].active = true;
32+
}
33+
}
34+
35+
get template() {
36+
return html`
37+
<style>
38+
:host {
39+
width: 15rem;
40+
font-weight: 400;
41+
-webkit-user-select: none;
42+
}
43+
</style>
44+
<slot />
45+
`;
46+
}
47+
}
48+
49+
NavMenu.define('nav-menu');

nav-section.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import {ShadowElement, html} from '@cfware/shadow-element';
2+
3+
class NavSection extends ShadowElement {
4+
constructor() {
5+
super();
6+
7+
this.addEventListener('hiddenchange', () => this._updateHidden(), true);
8+
}
9+
10+
_updateHidden() {
11+
this.hidden = !this.querySelector('*:not([hidden])');
12+
}
13+
14+
get template() {
15+
return html`
16+
<style>
17+
:host {
18+
background: #00000005;
19+
white-space: nowrap;
20+
}
21+
22+
.title {
23+
background-color: #ddd;
24+
color: #000;
25+
cursor: pointer;
26+
margin: 0;
27+
padding: .75rem 1rem;
28+
}
29+
30+
:host(:not(:first-child)) .title {
31+
border-top: 1px solid #0001;
32+
}
33+
34+
.title::before {
35+
display: inline-block;
36+
font-family: var(--expander-font-family);
37+
content: '';
38+
width: 1.25rem;
39+
text-align: center;
40+
margin-right: 0.5rem;
41+
}
42+
43+
:host([active]) .title::before {
44+
content: '';
45+
}
46+
47+
:host(:not([active])) ::slotted(nav-item) {
48+
display: none;
49+
}
50+
</style>
51+
<div class="title" onclick=${() => this.toggleAttribute('active')}>${this.title}</div>
52+
<slot onslotchange=${() => this._updateHidden()} />
53+
`;
54+
}
55+
}
56+
57+
NavSection.define('nav-section', {
58+
stringProps: {
59+
title: ''
60+
},
61+
booleanProps: ['active']
62+
});

nyc.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict';
2+
3+
module.exports = require('@cfware/nyc')
4+
.fullCoverage()
5+
.settings;

package.json

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"name": "@cfware/nav-menu",
3+
"version": "0.1.0",
4+
"description": "Navigation menu element",
5+
"main": "nav-menu.js",
6+
"scripts": {
7+
"release": "standard-version --sign",
8+
"test": "xo && nyc ava -v"
9+
},
10+
"author": "Corey Farrell",
11+
"license": "MIT",
12+
"files": [
13+
"nav-*.js"
14+
],
15+
"keywords": [
16+
"custom-elements",
17+
"navigation",
18+
"menu",
19+
"html"
20+
],
21+
"repository": {
22+
"type": "git",
23+
"url": "git+https://github.com/cfware/nav-menu.git"
24+
},
25+
"bugs": {
26+
"url": "https://github.com/cfware/nav-menu/issues"
27+
},
28+
"homepage": "https://github.com/cfware/nav-menu#readme",
29+
"dependencies": {
30+
"@cfware/shadow-element": "^0.7.0"
31+
},
32+
"devDependencies": {
33+
"@cfware/ava-selenium-manager": "^0.2.1",
34+
"@cfware/eslint-config-browser": "^0.3.0",
35+
"@cfware/fastify-test-helper": "^0.2.3",
36+
"@cfware/nyc": "^0.4.2",
37+
"@fortawesome/fontawesome-free": "=5.8.2",
38+
"ava": "^1.4.1",
39+
"chromedriver": "^74.0.0",
40+
"geckodriver": "^1.16.2",
41+
"nyc": "^14.1.0",
42+
"standard-version": "^6.0.1",
43+
"xo": "^0.24.0"
44+
},
45+
"xo": {
46+
"overrides": [
47+
{
48+
"files": [
49+
"nav-*.js"
50+
],
51+
"extends": "@cfware/eslint-config-browser"
52+
}
53+
]
54+
}
55+
}

0 commit comments

Comments
 (0)