Skip to content

Commit

Permalink
[add] Nav Dropdown component & Offcanvas hiding
Browse files Browse the repository at this point in the history
[optimize] update Usage section of Read Me document
  • Loading branch information
TechQuery committed Jan 18, 2024
1 parent 2dc2013 commit b9d71d8
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 30 deletions.
63 changes: 48 additions & 15 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,70 @@

## Usage

### Installation

```shell
npm install boot-cell iterable-observer @nuintun/qrcode
npm install dom-renderer web-cell boot-cell
npm install parcel @parcel/config-default @parcel/transformer-typescript-tsc -D
```

#### `package.json`

```json
{
"scripts": {
"start": "parcel source/index.html --open",
"build": "parcel build source/index.html --public-url ."
}
}
```

#### `tsconfig.json`

```json
{
"compilerOptions": {
"target": "ES6",
"module": "ES2020",
"moduleResolution": "Node",
"useDefineForClassFields": true,
"jsx": "react-jsx",
"jsxImportSource": "dom-renderer"
}
}
```

`index.html`
#### `.parcelrc`

```json
{
"extends": "@parcel/config-default",
"transformers": {
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
}
}
```

### `source/index.html`

```html
<link
rel="stylesheet"
href="https://unpkg.com/[email protected]/dist/dialog-polyfill.css"
/>
<link
rel="stylesheet"
href="https://unpkg.com/[email protected]/dist/css/bootstrap.min.css"
/>
<link
rel="stylesheet"
href="https://unpkg.com/[email protected].2/font/bootstrap-icons.css"
href="https://unpkg.com/[email protected].3/font/bootstrap-icons.css"
/>
<link
rel="stylesheet"
href="https://unpkg.com/@fortawesome/[email protected]/css/all.min.css"
/>
<script
crossorigin
src="https://polyfill.app/api/polyfill?features=es.array.flat,es.object.from-entries,regenerator-runtime,intersection-observer,resize-observer"
></script>
<script src="https://unpkg.com/[email protected]/dist/dialog-polyfill.js"></script>
<script src="https://unpkg.com/[email protected]/dist/share-min.js"></script>
<script src="https://unpkg.com/@webcomponents/[email protected]/custom-elements-es5-adapter.js"></script>
<script src="https://unpkg.com/@webcomponents/[email protected]/webcomponents-bundle.js"></script>
<script src="https://polyfill.web-cell.dev/feature/ECMAScript.js"></script>
<script src="https://polyfill.web-cell.dev/feature/WebComponents.js"></script>
<script src="https://polyfill.web-cell.dev/feature/ElementInternals.js"></script>
<script src="https://polyfill.web-cell.dev/feature/Dialog.js"></script>
<script src="https://polyfill.web-cell.dev/feature/Share.js"></script>
```

## Components
Expand Down
2 changes: 1 addition & 1 deletion source/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const Button: FC<ButtonProps> = ({
tabIndex={disabled ? -1 : tabIndex}
ariaDisabled={disabled + ''}
ariaPressed={active + ''}
{...props}
{...{ href, ...props }}
>
{children}
</a>
Expand Down
35 changes: 33 additions & 2 deletions source/Nav.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { JsxProps } from 'dom-renderer';
import { JsxChildren } from 'dom-renderer';
import { FC, WebCell, WebCellProps, component } from 'web-cell';

import { ButtonProps } from './Button';
import { Dropdown, DropdownMenu, DropdownToggle } from './Dropdown';
import { OffcanvasNavbar } from './Navbar';

export interface NavLinkProps extends JsxProps<HTMLAnchorElement> {
export interface NavLinkProps extends WebCellProps<HTMLAnchorElement> {
active?: boolean;
}

Expand All @@ -18,6 +20,35 @@ export const NavLink: FC<NavLinkProps> = ({
</a>
);

export interface NavDropdownProps
extends Omit<NavLinkProps, 'title'>,
Pick<ButtonProps, 'disabled' | 'onClick'> {
title: JsxChildren;
}

export const NavDropdown: FC<NavDropdownProps> = ({
title,
disabled,
onClick,
children
}) => (
<Dropdown className="nav-item">
<DropdownToggle
className="nav-link"
href="#"
disabled={disabled}
onClick={event => {
event.preventDefault();
onClick?.(event);
}}
>
{title}
</DropdownToggle>

<DropdownMenu>{children}</DropdownMenu>
</Dropdown>
);

export interface Nav extends WebCell {}

@component({
Expand Down
40 changes: 37 additions & 3 deletions source/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { JsxProps, VNode } from 'dom-renderer';
import { observable } from 'mobx';
import { FC, WebCellProps, attribute, component, observer } from 'web-cell';
import { uniqueID } from 'web-utility';
import {
FC,
WebCell,
WebCellProps,
attribute,
component,
observer
} from 'web-cell';
import { delegate, uniqueID } from 'web-utility';

import { Container, ContainerProps } from './Grid';
import {
Expand Down Expand Up @@ -73,12 +80,14 @@ export interface OffcanvasNavbarProps
brand?: VNode;
}

export interface OffcanvasNavbar extends WebCell {}

@component({
tagName: 'offcanvas-navbar',
mode: 'open'
})
@observer
export class OffcanvasNavbar extends HTMLElement {
export class OffcanvasNavbar extends HTMLElement implements WebCell {
declare props: OffcanvasNavbarProps;

@attribute
Expand Down Expand Up @@ -124,6 +133,30 @@ export class OffcanvasNavbar extends HTMLElement {
@observable
accessor closeButton = true;

connectedCallback() {
globalThis.addEventListener?.('keyup', this.close, true);

this.addEventListener('click', this.handleLink);
}

disconnectedCallback() {
globalThis.removeEventListener?.('keyup', this.close, true);

this.addEventListener('click', this.handleLink);
}

close = (event?: KeyboardEvent | MouseEvent) => {
if (
event instanceof KeyboardEvent &&
!['Escape', 'Enter'].includes(event.key)
)
return;

this.open = false;
};

handleLink = delegate('.nav-link', this.close);

renderContent() {
const { variant, bg, expand, fixed, sticky, fluid, brand } = this,
{ title, titleId, offcanvasId, open, closeButton } = this;
Expand All @@ -141,6 +174,7 @@ export class OffcanvasNavbar extends HTMLElement {
id={offcanvasId}
aria-labelledby={titleId}
show={open}
onHide={this.close}
>
<OffcanvasHeader
closeButton={closeButton}
Expand Down
21 changes: 12 additions & 9 deletions source/Offcanvas.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { JsxProps } from 'dom-renderer';
import { FC } from 'web-cell';
import { FC, WebCellProps } from 'web-cell';
import { uniqueID } from 'web-utility';

import { CloseButton } from './Button';

export const OffcanvasTitle: FC<JsxProps<HTMLHeadingElement>> = ({
export const OffcanvasTitle: FC<WebCellProps<HTMLHeadingElement>> = ({
className = '',
children,
...props
Expand All @@ -14,7 +13,7 @@ export const OffcanvasTitle: FC<JsxProps<HTMLHeadingElement>> = ({
</h5>
);

export interface OffcanvasHeaderProps extends JsxProps<HTMLDivElement> {
export interface OffcanvasHeaderProps extends WebCellProps<HTMLDivElement> {
closeButton?: boolean;
onHide?: () => any;
}
Expand All @@ -33,7 +32,7 @@ export const OffcanvasHeader: FC<OffcanvasHeaderProps> = ({
</div>
);

export const OffcanvasBody: FC<JsxProps<HTMLDivElement>> = ({
export const OffcanvasBody: FC<WebCellProps<HTMLDivElement>> = ({
className = '',
children,
...props
Expand All @@ -43,7 +42,8 @@ export const OffcanvasBody: FC<JsxProps<HTMLDivElement>> = ({
</div>
);

export interface OffcanvasProps extends JsxProps<HTMLDivElement> {
export interface OffcanvasProps
extends Omit<OffcanvasHeaderProps, 'closeButton'> {
backdrop?: boolean | 'static';
show?: boolean;
}
Expand All @@ -52,6 +52,7 @@ export const Offcanvas: FC<OffcanvasProps> = ({
className = '',
backdrop = true,
show,
onHide,
children,
...props
}) => (
Expand All @@ -66,7 +67,8 @@ export const Offcanvas: FC<OffcanvasProps> = ({
>
{children}
</div>
{show && <div className="offcanvas-backdrop show" />}

{show && <div className="offcanvas-backdrop show" onClick={onHide} />}
</>
);

Expand All @@ -80,11 +82,12 @@ export const OffcanvasBox: FC<OffcanvasBoxProps> = ({
title,
titleId = uniqueID(),
closeButton,
onHide,
children,
...props
}) => (
<Offcanvas {...props} aria-labelledby={titleId}>
<OffcanvasHeader closeButton={closeButton}>
<Offcanvas {...{ ...props, onHide }} aria-labelledby={titleId}>
<OffcanvasHeader {...{ closeButton, onHide }}>
<OffcanvasTitle id={titleId}>{title}</OffcanvasTitle>
</OffcanvasHeader>
<OffcanvasBody>{children}</OffcanvasBody>
Expand Down

0 comments on commit b9d71d8

Please sign in to comment.