Skip to content

Commit b9c2d52

Browse files
committed
feat(toc): use:toc action & <Toc> component for building table of contents
1 parent c13a9c6 commit b9c2d52

62 files changed

Lines changed: 1817 additions & 21 deletions

Some content is hidden

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

.changeset/good-shirts-prove.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@svelte-put/toc": major
3+
---
4+
5+
Introduction of `use:toc` and `<Toc>` component for building table of contents

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,13 @@ Useful svelte stuff to put in your projects
3737
| [@svelte-put/intersect][github.intersect] | wrapper for IntersectionObserver | [![npm.intersect.badge]][npm.intersect] | [Changelog][github.intersect.changelog] | [Docs][github.intersect.docs] | [REPL][repl.intersect] |
3838
| [@svelte-put/clickoutside][github.clickoutside] | event for clicking outside node | [![npm.clickoutside.badge]][npm.clickoutside] | [Changelog][github.clickoutside.changelog] | [Docs][github.clickoutside.docs] | [REPL][repl.clickoutside] |
3939
| [@svelte-put/shortcut][github.shortcut] | add keyboard shortcuts to node | [![npm.shortcut.badge]][npm.shortcut] | [Changelog][github.shortcut.changelog] | [Docs][github.shortcut.docs] |
40+
| [@svelte-put/toc][github.toc] | action & component for building table of contents | [![npm.toc.badge]][npm.toc] | [Changelog][github.toc.changelog] | [Docs][github.toc.docs] |
4041

4142
### Miscellaneous
4243

4344
| Package | Short Description | Version | Changelog | Docs | REPL |
4445
| --- | --- | --- | --- | --- | --- |
45-
| [@svelte-put/avatar][github.avatar] | component & utilities for avatar | [![npm.avatar.badge]][npm.avatar] | [Changelog][github.avatar.changelog] | [Docs][github.avatar.docs] | |
46+
| [@svelte-put/avatar][github.avatar] | component & utilities for avatar | [![npm.avatar.badge]][npm.avatar] | [Changelog][github.avatar.changelog] | [Docs][github.avatar.docs] | |
4647

4748
Note:
4849

@@ -55,7 +56,6 @@ These are some packages that will be added in the future (as soon as I find time
5556

5657
| Package | Category | Short Description |
5758
| --- | --- | --- |
58-
| @svelte-put/toc | action & component | table of contents builder |
5959
| @svelte-put/popover | action | trigger tooltip & detailed popover, using [popperjs](https://popper.js.org/) |
6060
| @svelte-put/modal | utility | open async modal (that you can call programmatically and `await`) |
6161
| @svelte-put/noti | utility | fire async toast-like notification |
@@ -187,6 +187,10 @@ pnpm turbo
187187
[github.avatar.changelog]: https://github.com/vnphanquang/svelte-put/blob/main/packages/misc/avatar/CHANGELOG.md
188188
[github.avatar.docs]: https://github.com/vnphanquang/svelte-put/blob/main/packages/misc/avatar/api/docs/index.md
189189
190+
[github.toc]: https://github.com/vnphanquang/svelte-put/tree/main/packages/actions/toc
191+
[github.toc.changelog]: https://github.com/vnphanquang/svelte-put/blob/main/packages/actions/toc/CHANGELOG.md
192+
[github.toc.docs]: https://github.com/vnphanquang/svelte-put/blob/main/packages/actions/toc/api/docs/index.md
193+
190194
<!-- heading badge -->
191195
[semantic-release]: https://github.com/semantic-release/semantic-release
192196
[semantic-release.badge]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
@@ -204,6 +208,8 @@ pnpm turbo
204208
[npm.shortcut]: https://www.npmjs.com/package/@svelte-put/shortcut
205209
[npm.avatar.badge]: https://img.shields.io/npm/v/@svelte-put/avatar
206210
[npm.avatar]: https://www.npmjs.com/package/@svelte-put/avatar
211+
[npm.toc.badge]: https://img.shields.io/npm/v/@svelte-put/toc
212+
[npm.toc]: https://www.npmjs.com/package/@svelte-put/toc
207213
208214
<!-- svelte REPL -->
209215
[repl.movable]: https://svelte.dev/repl/88a7c1fc2e134db7b58786d5f385fc5d

packages/actions/toc/.eslintrc.cjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/* eslint-disable @typescript-eslint/no-var-requires */
2+
const shared = require('@svelte-put/eslint-config/svelte-kit.cjs');
3+
4+
module.exports = { ...shared };

packages/actions/toc/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/lib
2+
/.svelte-kit

packages/actions/toc/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Changelog

packages/actions/toc/README.md

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
<div align="center">
2+
3+
# `@svelte-put/toc`
4+
5+
[![npm.badge]][npm] [![bundlephobia.badge]][bundlephobia]
6+
7+
Svelte `<Toc>` component and `use:toc` action for building table of contents
8+
9+
</div>
10+
11+
## Table of Contents
12+
13+
<details open>
14+
<summary>Show / hide</summary>
15+
16+
- [`@svelte-put/toc`](#svelte-puttoc)
17+
- [Table of Contents](#table-of-contents)
18+
- [`svelte-put`](#svelte-put)
19+
- [Installation](#installation)
20+
- [Acknowledgement](#acknowledgement)
21+
- [Usage](#usage)
22+
- [Action](#action)
23+
- [Component](#component)
24+
- [Documentation](#documentation)
25+
- [Typescript support](#typescript-support)
26+
27+
</details>
28+
29+
## `svelte-put`
30+
31+
This package is part of the [@svelte-put][github.monorepo] family. For contributing guideline and more, refer to its [readme][github.monorepo].
32+
33+
## Installation
34+
35+
```bash
36+
npm install -D @svelte-put/toc
37+
```
38+
39+
```bash
40+
yarn add -D @svelte-put/toc
41+
```
42+
43+
```bash
44+
pnpm add -D @svelte-put/toc
45+
```
46+
47+
## [Changelog][github.changelog]
48+
49+
## Acknowledgement
50+
51+
This package rely on svelte action strategy and attempts to stay minimal. If you are looking for a declarative, component-oriented solution, check out [Janosh][janosh]'s [svelte-toc].
52+
53+
## Usage
54+
55+
### Action
56+
57+
The `use:toc` action will search for all matching DOM elements and
58+
transform them to support hash navigation (anchor element with id attribute).
59+
By default, all heading elements are collected. You can customize the behavior
60+
with the action parameters [TocParameters][github.api.TocParameters]
61+
62+
<details open>
63+
<summary>Show / hide</summary>
64+
65+
```svelte
66+
<script>
67+
import { toc } from '@svelte-put/toc';
68+
import type { TocEventDetails } from '@svelte-put/toc';
69+
70+
function onToc(e: CustomEvent<TocEventDetails>) {
71+
const { items } = e.detail.items;
72+
for (const item of items) {
73+
const { id, element, text, p, anchor } = item;
74+
// do stuff
75+
console.log(item);
76+
}
77+
}
78+
</script>
79+
80+
<svelte:body use:toc on:toc={onToc} />
81+
82+
<h1>Heading</h1>
83+
<h2 id="manual-id">Some other content</h2>
84+
<h2>Content without explicit id will be slugified into an id with maximum length of 50 characters</h2>
85+
86+
```
87+
88+
</details>
89+
90+
After `toc` has completed its execution, the HTML in example above will be transformed, to:
91+
92+
<details open>
93+
<summary>Show / hide</summary>
94+
95+
```html
96+
<h1 class="toc-element" style="position: relative;">
97+
<a
98+
class="toc-anchor"
99+
href="#heading"
100+
style="position: relative;"
101+
>
102+
Heading
103+
</a>
104+
<p
105+
class="toc-p"
106+
id="note"
107+
style="bottom: 100%; position: absolute; visibility: hidden; margin-top: -96px; height: 96px;"
108+
></p>
109+
</h1>
110+
111+
<h2 class="toc-element" style="position: relative;">
112+
<a
113+
class="toc-anchor"
114+
href="#manual-id"
115+
style="position: relative;"
116+
>
117+
Some other content
118+
</a>
119+
<p
120+
class="toc-p"
121+
id="note"
122+
style="bottom: 100%; position: absolute; visibility: hidden; margin-top: -96px; height: 96px;"
123+
></p>
124+
</h2>
125+
126+
<h2 class="toc-element" style="position: relative;">
127+
<a
128+
class="toc-anchor"
129+
href="#content-without-explicit-id-will-be-slugified-into"
130+
style="position: relative;"
131+
>
132+
Content without explicit id will be slugified into an id with maximum length of 50 characters
133+
</a>
134+
<p
135+
class="toc-p"
136+
id="note"
137+
style="bottom: 100%; position: absolute; visibility: hidden; margin-top: -96px; height: 96px;"
138+
></p>
139+
</h2>
140+
```
141+
142+
</details>
143+
144+
### Component
145+
146+
The exported `Toc` component internally uses `<svelte:body use:toc />`; customization can be done by
147+
providing [TocParameters][github.api.TocParameters] through the `parameters` prop.
148+
149+
The component will print out a simple `ul` with some default margin for `li` depending on the heading level. For a simple blog post, this should be enough. For more complex use cases, you can override one of [TocSlots][github.api.TocSlots].
150+
151+
If the component interface does not provide enough customization flexibility for you, consider open an [issue][github.issues] to discuss or use the `use:toc` action interface instead.
152+
153+
Consider the same example as in the previous section but now we use the component instead:
154+
155+
<details open>
156+
<summary>Show / hide</summary>
157+
158+
```svelte
159+
<script>
160+
import Toc from '@svelte-put/toc/Toc.svelte';
161+
</script>
162+
163+
<Toc on:toc>
164+
<svelte:fragment slot="anchor" let:item>
165+
<a href="#{item.id}" class="custom-anchor-tag">{item.text}</a>
166+
</svelte:fragment>
167+
</Toc>
168+
169+
<!-- some headings like the previous example -->
170+
```
171+
172+
</details>
173+
174+
The HTML generated will look similar to
175+
176+
<details open>
177+
<summary>Show / hide</summary>
178+
179+
```html
180+
<ul class="toc-ul">
181+
<li class="toc-li toc-li--h1">
182+
<a class="custom-anchor-tag" href="#heading">
183+
Heading
184+
</a>
185+
</li>
186+
<li class="toc-li toc-li--h2">
187+
<a class="custom-anchor-tag" href="#manual-id">
188+
Some other content
189+
</a>
190+
</li>
191+
<li class="toc-li toc-li--h2">
192+
<a class="custom-anchor-tag" href="#content-without-explicit-id-will-be-slugified-into">
193+
Content without explicit id will be slugified into an id with maximum length of 50 characters
194+
</a>
195+
</li>
196+
</ul>
197+
```
198+
199+
And the CSS for this list is
200+
201+
202+
<details open>
203+
<summary>Show / hide</summary>
204+
205+
```css
206+
.toc-li.toc-li--h1 {
207+
font-weight: bold;
208+
}
209+
.toc-li.toc-li--h2 {
210+
margin-left: 1rem;
211+
}
212+
```
213+
214+
</details>
215+
216+
217+
</details>
218+
219+
## Documentation
220+
221+
### Typescript support
222+
223+
<details open>
224+
<summary> app.d.ts: show / hide </summary>
225+
226+
```typescript
227+
/// <reference types="@sveltejs/kit" />
228+
/// <reference types="svelte" />
229+
230+
// Typescript support in svelte-kit, see
231+
// https://github.com/sveltejs/language-tools/blob/master/docs/preprocessors/typescript.md#im-using-an-attributeevent-on-a-dom-element-and-it-throws-a-type-error
232+
233+
declare namespace svelte.JSX {
234+
interface HTMLAttributes<T> {
235+
// on:toc
236+
ontoc?: (event: CustomEvent<import('@svelte-put/toc').TocEventDetails>) => void;
237+
}
238+
}
239+
```
240+
241+
</details>
242+
243+
For detailed documentation, see the [extracted API][github.api].
244+
245+
<!-- github specifics -->
246+
[github.monorepo]: https://github.com/vnphanquang/svelte-put
247+
[github.changelog]: https://github.com/vnphanquang/svelte-put/blob/main/packages/actions/toc/CHANGELOG.md
248+
[github.issues]: https://github.com/vnphanquang/svelte-put/issues?q=
249+
[github.api]: https://github.com/vnphanquang/svelte-put/blob/main/packages/actions/toc/api/docs/index.md
250+
[github.api.TocProps]: https://github.com/vnphanquang/svelte-put/blob/main/packages/actions/toc/api/docs/toc.tocprops.md
251+
[github.api.TocSlots]: https://github.com/vnphanquang/svelte-put/blob/main/packages/actions/toc/api/docs/toc.tocslots.md
252+
[github.api.TocParameters]: https://github.com/vnphanquang/svelte-put/blob/main/packages/actions/toc/api/docs/toc.tocparameters.md
253+
254+
<!-- heading badge -->
255+
[npm.badge]: https://img.shields.io/npm/v/@svelte-put/toc
256+
[npm]: https://www.npmjs.com/package/@svelte-put/toc
257+
[bundlephobia.badge]: https://img.shields.io/bundlephobia/minzip/@svelte-put/toc?label=minzipped
258+
[bundlephobia]: https://bundlephobia.com/package/@svelte-put/toc
259+
260+
<!-- external resources -->
261+
[svelte-toc]: https://github.com/janosh/svelte-toc
262+
[janosh]: https://github.com/janosh
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
3+
"extends": "@svelte-put/apirc/base.json",
4+
"projectFolder": ".",
5+
"messages": {
6+
"extractorMessageReporting": {
7+
"ae-wrong-input-file-type": {
8+
"logLevel": "warning"
9+
}
10+
}
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md)
4+
5+
## API Reference
6+
7+
## Packages
8+
9+
| Package | Description |
10+
| --- | --- |
11+
| [@svelte-put/toc](./toc.md) | Svelte action <code>use:toc</code> and component <code>&lt;Toc&gt;</code> for building table of contents |
12+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [@svelte-put/toc](./toc.md) &gt; [DEFAULT\_TOC\_PARAMETERS](./toc.default_toc_parameters.md)
4+
5+
## DEFAULT\_TOC\_PARAMETERS variable
6+
7+
The default [TocParameters](./toc.tocparameters.md) options for `toc` action
8+
9+
<b>Signature:</b>
10+
11+
```typescript
12+
DEFAULT_TOC_PARAMETERS: TocParameters
13+
```

0 commit comments

Comments
 (0)