Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update astro-toc #10

Merged
merged 12 commits into from
Oct 26, 2023
51 changes: 51 additions & 0 deletions .changeset/perfect-dots-switch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
"astro-toc": minor
---

## **astro-toc**

**Feat:** Apply passed in scoped styles

```jsx
<style>
.text-large {
font-size: 3rem;
}
</style>
<TOC toc={toc} class="text-large" />
```

---

**Feat:** Use `depth` prop to set initial depth

```jsx
<TOC toc={toc} depth={2} />
```

---

**BREAKING CHANGE:** `use` prop for custom component

Previously `use` prop would default to `<menu>`. Default is now `<ul>`. Use `as` prop to define list type.

```diff
<TOC
toc={toc}
use={MyComponent}
- />
+ as="menu"
+ />
```

---

**BREAKING CHANGE:** Changed `maxDepth` prop type to number

```diff
<TOC
toc={toc}
- maxDepth="2"
+ maxDepth={2}
/>
```
94 changes: 36 additions & 58 deletions packages/astro-toc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<img src="https://raw.githubusercontent.com/theisel/astro-toc/main/logo.svg" width="240" alt="astro-toc logo">
</div>

&nbsp;

# astro-toc

![license](https://img.shields.io/npm/l/astro-toc?style=flat-square)
Expand All @@ -11,86 +13,62 @@ Table of Contents (ToC) generator for [Astro](https://astro.build/)

[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/theisel/astro-toc/tree/main/demo)

&nbsp;

## Table of Contents

- [Install](#install)
- [Usage](#usage)
- [License](#license)

&nbsp;

## Install

```
npm install astro-toc
```bash
$ npm install astro-toc
# $ pnpm add astro-toc
# $ yarn add astro-toc
```

## Usage
&nbsp;

### Classic

This render mode can render your `ToC` as `<ul>`, `<ol>` or `<menu>`; set the `as` property to select your flavour.

| Property | Type | Description |
| :------------------ | :---------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| toc | `{ depth: number; title: string; url?: string; [string]: any }[]` | `depth` and `title` are required. |
| as `optional` | `string` | [bullet](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul) \| [number](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol) \| [menu](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu) |
| maxDepth `optional` | `number` | How many levels should be rendered |
## Usage

```ts
```jsx
---
import { TOC } from "astro-toc";

/* .astro */
const toc = [
{depth: 1, title: "Tours", url: /* optional*/},
{depth: 2, title: "Walking Tours"},
{depth: 3, title: "City Walking Tours"},
{depth: 3, title: "River Walking Tours"},
{depth: 2, title: "Boat Tours"},
{depth: 1, title: "Restaurants"},
{depth: 2, title: "Fine Dining"},
{depth: 2, title: "Gastropub"},
{depth: 2, title: "Fast Food"},
{depth: 1, title: "Hotels"},
{depth: 2, title: "Spa Hotels"},
{depth: 2, title: "Bed & Breakfast"},
{ depth: 1, title: "List Item 1" },
{ depth: 2, title: "List Item 1.1" },
{ depth: 3, title: "List Item 1.1.1" },
{ depth: 3, title: "List Item 1.1.2" },
{ depth: 2, title: "List Item 1.2" },
{ depth: 1, title: "List Item 2" },
{ depth: 2, title: "List Item 2.1" },
{ depth: 2, title: "List Item 2.2" },
{ depth: 2, title: "List Item 2.3" },
{ depth: 1, title: "List Item 3" },
{ depth: 2, title: "List Item 3.1" },
{ depth: 2, title: "List Item 3.2" },
];
---

<TOC toc={toc} />
```

### Component

When using a custom component it uses the [\<menu\>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu) element.
Render your table of contents as `<ul>`, `<ol>` or `<menu>`; set the `as` property to select your flavour. Pass in `use` prop to use custom component.

Unlike the [classic](#classic) render method `title` isn't required and the payload will be passed to the component.
### Props

| Property | Type | Description |
| :------------------ | :----------------------------------- | :-------------------------------------- |
| toc | `{ depth: number; [string]: any }[]` | `depth` is required. |
| use | `(props) => any` | [Astro](https://astro.build/) component |
| maxDepth `optional` | `number` \| `string` | How many levels should be rendered |
| Property | Type | Description |
| :------------------ | :------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| toc | `{ depth: number; [string]: any } []` | Table of contents data.<br/>`title` is required when custom component isn't used. |
| as `optional` | `string` | [bullet](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul) (default) \| [number](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol) \| [menu](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu) |
| use `optional` | `(props: any) => any` | Custom component |
| depth `optional` | `number` | Initial depth (default 1) |
| maxDepth `optional` | `number` | How many levels to render |

```ts
---
import { TOC } from "astro-toc";
import MyComponent from "path/to/MyComponent.astro";

const toc = [
{depth: 1, title: "Tours"},
{depth: 2, title: "Walking Tours"},
{depth: 3, title: "City Walking Tours"},
{depth: 3, title: "River Walking Tours"},
{depth: 2, title: "Boat Tours"},
{depth: 1, title: "Restaurants"},
{depth: 2, title: "Fine Dining"},
{depth: 2, title: "Gastropub"},
{depth: 2, title: "Fast Food"},
{depth: 1, title: "Hotels"},
];
---

<TOC toc={toc} use={MyComponent} />
```
&nbsp;

## License

Expand Down
44 changes: 18 additions & 26 deletions packages/astro-toc/lib/TOC.astro
Original file line number Diff line number Diff line change
@@ -1,53 +1,45 @@
---
interface ToC {
depth: number;
[k: string]: any;
}

interface BaseProps<T> {
toc: (T & ToC)[];
type BaseProps<T> = {
toc: (T & {
depth: number;
})[];
as?: "bullet" | "number" | "menu";
maxDepth?: number | string;
}
depth?: number;
maxDepth?: number;
use: (props: any) => any;
};

type ClassicProps = BaseProps<{
title: string;
url?: string;
}>;

interface UseComponentProps
extends BaseProps<{
[key: string]: any;
}> {
use: (props: any /* Can we do better than `any`? */) => any;
}
type UseComponentProps = BaseProps<{
[key: string]: any;
}>;

export type Props = ClassicProps | UseComponentProps;

const { toc, depth = 1, ...props } = Astro.props;
const { use: Cmp, class: className } = props;
const as = props.as ?? props.style ?? "bullet";
const maxDepth = Number(props.maxDepth);
const hasMaxDepth = Number.isInteger(maxDepth);
const { toc, depth = 1, ...props } = Astro.props as Props;
const { as = "bullet", use: Cmp, maxDepth, ...styleProps } = props; /* styleProps; `class` & `data-astro-cid-*` */
const headings = toc.filter((it) => it.depth === depth);
const Tag = Cmp ?? "menu" === as ? "menu" : "number" === as ? "ol" : "ul";
const Tag = "bullet" === as ? "ul" : "number" === as ? "ol" : "menu";
---

<Tag data-astro-toc={depth}>
<Tag data-astro-toc={depth} {...depth === 1 ? styleProps : {}}>
{
headings.map((it, idx) => {
const nextHeading = headings[idx + 1];
const subHeadings = toc.slice(
(toc as any).indexOf(it) + 1,
nextHeading ? (toc as any).indexOf(nextHeading) : undefined
);
const hasSubHeadings = subHeadings.length > 0;
const shouldRenderSubHeadings = hasMaxDepth ? maxDepth > it.depth : hasSubHeadings;
const shouldRenderSubHeadings = maxDepth ? maxDepth > it.depth : subHeadings.length > 0;

return (
<li data-astro-toc={depth}>
{Cmp ? <Cmp {...it} class={className} /> : it.url ? <a href={it.url}>{it.title}</a> : it.title}
{shouldRenderSubHeadings ? <Astro.self toc={subHeadings} depth={it.depth + 1} {...props} /> : null}
{Cmp ? <Cmp {...it} /> : it.url ? <a href={it.url}>{it.title}</a> : it.title}
{shouldRenderSubHeadings && <Astro.self toc={subHeadings} depth={it.depth + 1} {...props} />}
</li>
);
})
Expand Down
2 changes: 1 addition & 1 deletion packages/astro-toc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"license": "ISC",
"repository": {
"type": "git",
"url": "git+https://github.com/theisel/astro-toc.git",
"url": "https://github.com/theisel/astro-toc.git",
"directory": "packages/astro-toc"
},
"bugs": {
Expand Down
3 changes: 2 additions & 1 deletion packages/astro-toc/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"extends": "astro/tsconfigs/strict"
"extends": "astro/tsconfigs/strict",
"include": ["lib"]
}