nextjs-slides 0.1.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/README.md +184 -0
- package/dist/cn.d.ts +5 -0
- package/dist/cn.js +9 -0
- package/dist/cn.js.map +1 -0
- package/dist/get-slide.d.ts +33 -0
- package/dist/get-slide.js +16 -0
- package/dist/get-slide.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/primitives.d.ts +75 -0
- package/dist/primitives.js +151 -0
- package/dist/primitives.js.map +1 -0
- package/dist/slide-deck.d.ts +8 -0
- package/dist/slide-deck.js +121 -0
- package/dist/slide-deck.js.map +1 -0
- package/dist/slide-demo-content.d.ts +7 -0
- package/dist/slide-demo-content.js +24 -0
- package/dist/slide-demo-content.js.map +1 -0
- package/dist/slide-link.d.ts +11 -0
- package/dist/slide-link.js +28 -0
- package/dist/slide-link.js.map +1 -0
- package/dist/slides.css +105 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# nextjs-slides
|
|
2
|
+
|
|
3
|
+
Composable slide deck primitives for Next.js — powered by React 19 ViewTransitions, Tailwind CSS v4, and sugar-high syntax highlighting.
|
|
4
|
+
|
|
5
|
+
Build full presentations from React components with URL-based routing, keyboard navigation, progress indicators, and smooth slide transitions — all declarative.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install nextjs-slides
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Peer dependencies: `next >=15`, `react >=19`, `tailwindcss >=4`.
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
### 1. Import the stylesheet
|
|
18
|
+
|
|
19
|
+
In your root layout or global CSS:
|
|
20
|
+
|
|
21
|
+
```css
|
|
22
|
+
@import "nextjs-slides/styles.css";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 2. Define your slides
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
// app/slides/slides.tsx
|
|
29
|
+
import {
|
|
30
|
+
Slide,
|
|
31
|
+
SlideTitle,
|
|
32
|
+
SlideSubtitle,
|
|
33
|
+
SlideBadge,
|
|
34
|
+
SlideCode,
|
|
35
|
+
} from "nextjs-slides";
|
|
36
|
+
|
|
37
|
+
export const slides = [
|
|
38
|
+
<Slide key="intro">
|
|
39
|
+
<SlideBadge>Welcome</SlideBadge>
|
|
40
|
+
<SlideTitle>My Presentation</SlideTitle>
|
|
41
|
+
<SlideSubtitle>Built with nextjs-slides</SlideSubtitle>
|
|
42
|
+
</Slide>,
|
|
43
|
+
|
|
44
|
+
<Slide key="code" align="left">
|
|
45
|
+
<SlideTitle>Code Example</SlideTitle>
|
|
46
|
+
<SlideCode title="hello.ts">{`const greeting = "Hello, world!";
|
|
47
|
+
console.log(greeting);`}</SlideCode>
|
|
48
|
+
</Slide>,
|
|
49
|
+
];
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3. Create the layout (provider)
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
// app/slides/layout.tsx
|
|
56
|
+
"use client";
|
|
57
|
+
|
|
58
|
+
import { SlideDeck } from "nextjs-slides";
|
|
59
|
+
import { slides } from "./slides";
|
|
60
|
+
|
|
61
|
+
export default function SlidesLayout({
|
|
62
|
+
children,
|
|
63
|
+
}: {
|
|
64
|
+
children: React.ReactNode;
|
|
65
|
+
}) {
|
|
66
|
+
return <SlideDeck slides={slides}>{children}</SlideDeck>;
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 4. Add the routes
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
// app/slides/page.tsx
|
|
74
|
+
import { redirect } from "next/navigation";
|
|
75
|
+
|
|
76
|
+
export default function SlidesPage() {
|
|
77
|
+
redirect("/slides/1");
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
// app/slides/[page]/page.tsx
|
|
83
|
+
import { getSlide, generateSlideParams } from "nextjs-slides";
|
|
84
|
+
import { slides } from "../slides";
|
|
85
|
+
|
|
86
|
+
export const generateStaticParams = () => generateSlideParams(slides);
|
|
87
|
+
|
|
88
|
+
export default async function SlidePage({
|
|
89
|
+
params,
|
|
90
|
+
}: {
|
|
91
|
+
params: Promise<{ page: string }>;
|
|
92
|
+
}) {
|
|
93
|
+
return getSlide(await params, slides);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
That's it. Navigate to `/slides` and you have a full slide deck.
|
|
98
|
+
|
|
99
|
+
## `<SlideDeck>` Props
|
|
100
|
+
|
|
101
|
+
| Prop | Type | Default | Description |
|
|
102
|
+
| -------------- | ----------------- | ------------ | --------------------------------------- |
|
|
103
|
+
| `slides` | `ReactNode[]` | **required** | Your slides array |
|
|
104
|
+
| `basePath` | `string` | `"/slides"` | URL prefix for slide routes |
|
|
105
|
+
| `showProgress` | `boolean` | `true` | Show dot progress indicator |
|
|
106
|
+
| `showCounter` | `boolean` | `true` | Show "3 / 10" counter |
|
|
107
|
+
| `className` | `string` | — | Additional class for the deck container |
|
|
108
|
+
| `children` | `React.ReactNode` | **required** | Route content (from Next.js) |
|
|
109
|
+
|
|
110
|
+
## Primitives
|
|
111
|
+
|
|
112
|
+
### Layout
|
|
113
|
+
|
|
114
|
+
- **`<Slide>`** — Full-screen slide container with decorative border. Props: `align` (`"center"` | `"left"`), `className`.
|
|
115
|
+
- **`<SlideSplitLayout>`** — Two-column layout with vertical divider. Props: `left`, `right`, `className`.
|
|
116
|
+
|
|
117
|
+
### Typography
|
|
118
|
+
|
|
119
|
+
- **`<SlideTitle>`** — Large bold heading (responsive h1).
|
|
120
|
+
- **`<SlideSubtitle>`** — Muted secondary text.
|
|
121
|
+
- **`<SlideBadge>`** — Inverted pill badge.
|
|
122
|
+
- **`<SlideHeaderBadge>`** — Italic accent label.
|
|
123
|
+
- **`<SlideNote>`** — Small muted footnote.
|
|
124
|
+
|
|
125
|
+
### Content
|
|
126
|
+
|
|
127
|
+
- **`<SlideCode>`** — Syntax-highlighted code block (sugar-high). Props: `title`, `className`. Pass code as `children` string.
|
|
128
|
+
- **`<SlideList>`** / **`<SlideListItem>`** — Bullet list.
|
|
129
|
+
- **`<SlideDemo>`** — Interactive component container. Keyboard/click navigation is disabled inside. Props: `label`, `className`.
|
|
130
|
+
|
|
131
|
+
### Structured
|
|
132
|
+
|
|
133
|
+
- **`<SlideStatementList>`** / **`<SlideStatement>`** — Title + description pairs with border separators.
|
|
134
|
+
- **`<SlideSpeaker>`** — Avatar + name + title row.
|
|
135
|
+
- **`<SlideSpeakerGrid>`** — 2-column speaker grid.
|
|
136
|
+
- **`<SlideSpeakerList>`** — Vertical speaker stack.
|
|
137
|
+
|
|
138
|
+
### Navigation
|
|
139
|
+
|
|
140
|
+
- **`<SlideLink>`** — Styled link for navigation. Props: `href`, `variant` (`"primary"` | `"ghost"`), `className`.
|
|
141
|
+
|
|
142
|
+
## Keyboard Navigation
|
|
143
|
+
|
|
144
|
+
| Key | Action |
|
|
145
|
+
| ------------ | -------------- |
|
|
146
|
+
| `→` or Space | Next slide |
|
|
147
|
+
| `←` | Previous slide |
|
|
148
|
+
|
|
149
|
+
Interactive areas (`<SlideDemo>`, inputs, textareas) don't trigger navigation.
|
|
150
|
+
|
|
151
|
+
## Custom Base Path
|
|
152
|
+
|
|
153
|
+
Host slides at a different URL:
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
<SlideDeck slides={slides} basePath="/deck">
|
|
157
|
+
{children}
|
|
158
|
+
</SlideDeck>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Then route at `/deck/[page]/page.tsx`.
|
|
162
|
+
|
|
163
|
+
## Breakout Pages
|
|
164
|
+
|
|
165
|
+
Pages inside the slides folder but outside the `[page]` route render without deck navigation — useful for live demos:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
app/slides/
|
|
169
|
+
layout.tsx ← SlideDeck provider
|
|
170
|
+
[page]/page.tsx ← Slide routes
|
|
171
|
+
demo/page.tsx ← Breakout page (no deck chrome)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Tailwind CSS
|
|
175
|
+
|
|
176
|
+
The primitives use Tailwind utility classes with standard CSS variable tokens (`--foreground`, `--background`, `--muted-foreground`, `--primary`, etc.). These are compatible with shadcn/ui themes or any Tailwind v4 setup that defines these variables.
|
|
177
|
+
|
|
178
|
+
## Animations
|
|
179
|
+
|
|
180
|
+
Slide transitions use the React 19 `<ViewTransition>` component with `addTransitionType()`. The CSS in `nextjs-slides/styles.css` defines the `::view-transition-*` animations. Override them in your own CSS to customize.
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT
|
package/dist/cn.d.ts
ADDED
package/dist/cn.js
ADDED
package/dist/cn.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cn.ts"],"sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":"AAAA,SAAS,YAA6B;AACtC,SAAS,eAAe;AAEjB,SAAS,MAAM,QAAsB;AAC1C,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;","names":[]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the current slide from params and the slides array.
|
|
3
|
+
* Use in your `[page]/page.tsx` dynamic route.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { getSlide } from 'nextjs-slides';
|
|
8
|
+
* import { slides } from '../slides';
|
|
9
|
+
*
|
|
10
|
+
* export default async function SlidePage({ params }: { params: Promise<{ page: string }> }) {
|
|
11
|
+
* return getSlide(await params, slides);
|
|
12
|
+
* }
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
declare function getSlide(params: {
|
|
16
|
+
page: string;
|
|
17
|
+
}, slides: React.ReactNode[]): React.ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* Generates static params for all slides. Use with `generateStaticParams`.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* import { generateSlideParams } from 'nextjs-slides';
|
|
24
|
+
* import { slides } from '../slides';
|
|
25
|
+
*
|
|
26
|
+
* export const generateStaticParams = () => generateSlideParams(slides);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
declare function generateSlideParams(slides: React.ReactNode[]): {
|
|
30
|
+
page: string;
|
|
31
|
+
}[];
|
|
32
|
+
|
|
33
|
+
export { generateSlideParams, getSlide };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { notFound } from "next/navigation";
|
|
2
|
+
function getSlide(params, slides) {
|
|
3
|
+
const index = Number(params.page) - 1;
|
|
4
|
+
if (isNaN(index) || index < 0 || index >= slides.length) {
|
|
5
|
+
notFound();
|
|
6
|
+
}
|
|
7
|
+
return slides[index];
|
|
8
|
+
}
|
|
9
|
+
function generateSlideParams(slides) {
|
|
10
|
+
return slides.map((_, i) => ({ page: String(i + 1) }));
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
generateSlideParams,
|
|
14
|
+
getSlide
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=get-slide.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/get-slide.tsx"],"sourcesContent":["import { notFound } from 'next/navigation';\n\n/**\n * Resolves the current slide from params and the slides array.\n * Use in your `[page]/page.tsx` dynamic route.\n *\n * @example\n * ```tsx\n * import { getSlide } from 'nextjs-slides';\n * import { slides } from '../slides';\n *\n * export default async function SlidePage({ params }: { params: Promise<{ page: string }> }) {\n * return getSlide(await params, slides);\n * }\n * ```\n */\nexport function getSlide(params: { page: string }, slides: React.ReactNode[]): React.ReactNode {\n const index = Number(params.page) - 1;\n\n if (isNaN(index) || index < 0 || index >= slides.length) {\n notFound();\n }\n\n return slides[index];\n}\n\n/**\n * Generates static params for all slides. Use with `generateStaticParams`.\n *\n * @example\n * ```tsx\n * import { generateSlideParams } from 'nextjs-slides';\n * import { slides } from '../slides';\n *\n * export const generateStaticParams = () => generateSlideParams(slides);\n * ```\n */\nexport function generateSlideParams(slides: React.ReactNode[]) {\n return slides.map((_, i) => ({ page: String(i + 1) }));\n}\n"],"mappings":"AAAA,SAAS,gBAAgB;AAgBlB,SAAS,SAAS,QAA0B,QAA4C;AAC7F,QAAM,QAAQ,OAAO,OAAO,IAAI,IAAI;AAEpC,MAAI,MAAM,KAAK,KAAK,QAAQ,KAAK,SAAS,OAAO,QAAQ;AACvD,aAAS;AAAA,EACX;AAEA,SAAO,OAAO,KAAK;AACrB;AAaO,SAAS,oBAAoB,QAA2B;AAC7D,SAAO,OAAO,IAAI,CAAC,GAAG,OAAO,EAAE,MAAM,OAAO,IAAI,CAAC,EAAE,EAAE;AACvD;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { SlideDeck } from './slide-deck.js';
|
|
2
|
+
export { generateSlideParams, getSlide } from './get-slide.js';
|
|
3
|
+
export { Slide, SlideBadge, SlideCode, SlideDemo, SlideHeaderBadge, SlideList, SlideListItem, SlideNote, SlideSpeaker, SlideSpeakerGrid, SlideSpeakerList, SlideSplitLayout, SlideStatement, SlideStatementList, SlideSubtitle, SlideTitle } from './primitives.js';
|
|
4
|
+
export { SlideLink } from './slide-link.js';
|
|
5
|
+
export { SlideAlign, SlideDeckConfig, SlideLinkVariant } from './types.js';
|
|
6
|
+
import 'react/jsx-runtime';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { SlideDeck } from "./slide-deck";
|
|
2
|
+
import { getSlide, generateSlideParams } from "./get-slide";
|
|
3
|
+
import { Slide, SlideSplitLayout } from "./primitives";
|
|
4
|
+
import { SlideTitle, SlideSubtitle, SlideBadge, SlideHeaderBadge, SlideNote } from "./primitives";
|
|
5
|
+
import { SlideCode, SlideList, SlideListItem, SlideDemo } from "./primitives";
|
|
6
|
+
import { SlideStatementList, SlideStatement } from "./primitives";
|
|
7
|
+
import { SlideSpeaker, SlideSpeakerGrid, SlideSpeakerList } from "./primitives";
|
|
8
|
+
import { SlideLink } from "./slide-link";
|
|
9
|
+
export {
|
|
10
|
+
Slide,
|
|
11
|
+
SlideBadge,
|
|
12
|
+
SlideCode,
|
|
13
|
+
SlideDeck,
|
|
14
|
+
SlideDemo,
|
|
15
|
+
SlideHeaderBadge,
|
|
16
|
+
SlideLink,
|
|
17
|
+
SlideList,
|
|
18
|
+
SlideListItem,
|
|
19
|
+
SlideNote,
|
|
20
|
+
SlideSpeaker,
|
|
21
|
+
SlideSpeakerGrid,
|
|
22
|
+
SlideSpeakerList,
|
|
23
|
+
SlideSplitLayout,
|
|
24
|
+
SlideStatement,
|
|
25
|
+
SlideStatementList,
|
|
26
|
+
SlideSubtitle,
|
|
27
|
+
SlideTitle,
|
|
28
|
+
generateSlideParams,
|
|
29
|
+
getSlide
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Provider\nexport { SlideDeck } from './slide-deck';\n\n// Routing helpers\nexport { getSlide, generateSlideParams } from './get-slide';\n\n// Primitives — Layout\nexport { Slide, SlideSplitLayout } from './primitives';\n\n// Primitives — Typography\nexport { SlideTitle, SlideSubtitle, SlideBadge, SlideHeaderBadge, SlideNote } from './primitives';\n\n// Primitives — Content\nexport { SlideCode, SlideList, SlideListItem, SlideDemo } from './primitives';\n\n// Primitives — Statements\nexport { SlideStatementList, SlideStatement } from './primitives';\n\n// Primitives — Speakers\nexport { SlideSpeaker, SlideSpeakerGrid, SlideSpeakerList } from './primitives';\n\n// Navigation\nexport { SlideLink } from './slide-link';\n\n// Types\nexport type { SlideAlign, SlideLinkVariant, SlideDeckConfig } from './types';\n"],"mappings":"AACA,SAAS,iBAAiB;AAG1B,SAAS,UAAU,2BAA2B;AAG9C,SAAS,OAAO,wBAAwB;AAGxC,SAAS,YAAY,eAAe,YAAY,kBAAkB,iBAAiB;AAGnF,SAAS,WAAW,WAAW,eAAe,iBAAiB;AAG/D,SAAS,oBAAoB,sBAAsB;AAGnD,SAAS,cAAc,kBAAkB,wBAAwB;AAGjE,SAAS,iBAAiB;","names":[]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { SlideAlign } from './types.js';
|
|
3
|
+
|
|
4
|
+
declare function Slide({ children, align, className, }: {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
align?: SlideAlign;
|
|
7
|
+
className?: string;
|
|
8
|
+
}): react_jsx_runtime.JSX.Element;
|
|
9
|
+
declare function SlideSplitLayout({ left, right, className, }: {
|
|
10
|
+
left: React.ReactNode;
|
|
11
|
+
right: React.ReactNode;
|
|
12
|
+
className?: string;
|
|
13
|
+
}): react_jsx_runtime.JSX.Element;
|
|
14
|
+
declare function SlideTitle({ children, className }: {
|
|
15
|
+
children: React.ReactNode;
|
|
16
|
+
className?: string;
|
|
17
|
+
}): react_jsx_runtime.JSX.Element;
|
|
18
|
+
declare function SlideSubtitle({ children, className }: {
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
className?: string;
|
|
21
|
+
}): react_jsx_runtime.JSX.Element;
|
|
22
|
+
declare function SlideBadge({ children, className }: {
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
className?: string;
|
|
25
|
+
}): react_jsx_runtime.JSX.Element;
|
|
26
|
+
declare function SlideHeaderBadge({ children, className }: {
|
|
27
|
+
children: React.ReactNode;
|
|
28
|
+
className?: string;
|
|
29
|
+
}): react_jsx_runtime.JSX.Element;
|
|
30
|
+
declare function SlideCode({ children, className, title }: {
|
|
31
|
+
children: string;
|
|
32
|
+
className?: string;
|
|
33
|
+
title?: string;
|
|
34
|
+
}): react_jsx_runtime.JSX.Element;
|
|
35
|
+
declare function SlideList({ children, className }: {
|
|
36
|
+
children: React.ReactNode;
|
|
37
|
+
className?: string;
|
|
38
|
+
}): react_jsx_runtime.JSX.Element;
|
|
39
|
+
declare function SlideListItem({ children, className }: {
|
|
40
|
+
children: React.ReactNode;
|
|
41
|
+
className?: string;
|
|
42
|
+
}): react_jsx_runtime.JSX.Element;
|
|
43
|
+
declare function SlideNote({ children, className }: {
|
|
44
|
+
children: React.ReactNode;
|
|
45
|
+
className?: string;
|
|
46
|
+
}): react_jsx_runtime.JSX.Element;
|
|
47
|
+
declare function SlideDemo({ children, className, label, }: {
|
|
48
|
+
children: React.ReactNode;
|
|
49
|
+
className?: string;
|
|
50
|
+
label?: string;
|
|
51
|
+
}): react_jsx_runtime.JSX.Element;
|
|
52
|
+
declare function SlideStatementList({ children, className }: {
|
|
53
|
+
children: React.ReactNode;
|
|
54
|
+
className?: string;
|
|
55
|
+
}): react_jsx_runtime.JSX.Element;
|
|
56
|
+
declare function SlideStatement({ title, description, className, }: {
|
|
57
|
+
title: string;
|
|
58
|
+
description?: string;
|
|
59
|
+
className?: string;
|
|
60
|
+
}): react_jsx_runtime.JSX.Element;
|
|
61
|
+
declare function SlideSpeaker({ name, title, className }: {
|
|
62
|
+
name: string;
|
|
63
|
+
title: string;
|
|
64
|
+
className?: string;
|
|
65
|
+
}): react_jsx_runtime.JSX.Element;
|
|
66
|
+
declare function SlideSpeakerGrid({ children, className }: {
|
|
67
|
+
children: React.ReactNode;
|
|
68
|
+
className?: string;
|
|
69
|
+
}): react_jsx_runtime.JSX.Element;
|
|
70
|
+
declare function SlideSpeakerList({ children, className }: {
|
|
71
|
+
children: React.ReactNode;
|
|
72
|
+
className?: string;
|
|
73
|
+
}): react_jsx_runtime.JSX.Element;
|
|
74
|
+
|
|
75
|
+
export { Slide, SlideBadge, SlideCode, SlideDemo, SlideHeaderBadge, SlideList, SlideListItem, SlideNote, SlideSpeaker, SlideSpeakerGrid, SlideSpeakerList, SlideSplitLayout, SlideStatement, SlideStatementList, SlideSubtitle, SlideTitle };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { highlight } from "sugar-high";
|
|
3
|
+
import { cn } from "./cn";
|
|
4
|
+
import { SlideDemoContent } from "./slide-demo-content";
|
|
5
|
+
function Slide({
|
|
6
|
+
children,
|
|
7
|
+
align = "center",
|
|
8
|
+
className
|
|
9
|
+
}) {
|
|
10
|
+
return /* @__PURE__ */ jsxs(
|
|
11
|
+
"div",
|
|
12
|
+
{
|
|
13
|
+
className: cn(
|
|
14
|
+
"nxs-slide relative flex h-dvh w-dvw flex-col justify-center gap-8 px-12 py-20 sm:px-24 md:px-32 lg:px-40",
|
|
15
|
+
align === "center" && "items-center text-center",
|
|
16
|
+
align === "left" && "items-start text-left",
|
|
17
|
+
className
|
|
18
|
+
),
|
|
19
|
+
children: [
|
|
20
|
+
/* @__PURE__ */ jsx("div", { className: "border-foreground/10 pointer-events-none absolute inset-4 border sm:inset-6", "aria-hidden": true }),
|
|
21
|
+
/* @__PURE__ */ jsx(
|
|
22
|
+
"div",
|
|
23
|
+
{
|
|
24
|
+
className: cn(
|
|
25
|
+
"relative z-10 flex max-w-4xl flex-col gap-6",
|
|
26
|
+
align === "center" && "items-center",
|
|
27
|
+
align === "left" && "items-start"
|
|
28
|
+
),
|
|
29
|
+
children
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
function SlideSplitLayout({
|
|
37
|
+
left,
|
|
38
|
+
right,
|
|
39
|
+
className
|
|
40
|
+
}) {
|
|
41
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("nxs-slide relative flex h-dvh w-dvw", className), children: [
|
|
42
|
+
/* @__PURE__ */ jsx("div", { className: "border-foreground/10 pointer-events-none absolute inset-4 border sm:inset-6", "aria-hidden": true }),
|
|
43
|
+
/* @__PURE__ */ jsx("div", { className: "relative z-10 flex w-1/2 flex-col justify-center px-12 py-20 sm:px-16 md:px-20 lg:px-24", children: left }),
|
|
44
|
+
/* @__PURE__ */ jsx("div", { className: "bg-foreground/10 absolute top-4 bottom-4 left-1/2 z-10 w-px sm:top-6 sm:bottom-6", "aria-hidden": true }),
|
|
45
|
+
/* @__PURE__ */ jsx("div", { className: "relative z-10 flex w-1/2 flex-col justify-center", children: right })
|
|
46
|
+
] });
|
|
47
|
+
}
|
|
48
|
+
function SlideTitle({ children, className }) {
|
|
49
|
+
return /* @__PURE__ */ jsx(
|
|
50
|
+
"h1",
|
|
51
|
+
{
|
|
52
|
+
className: cn("text-foreground text-4xl font-extrabold sm:text-5xl md:text-6xl lg:text-7xl", className),
|
|
53
|
+
style: { letterSpacing: "-0.04em" },
|
|
54
|
+
children
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
function SlideSubtitle({ children, className }) {
|
|
59
|
+
return /* @__PURE__ */ jsx("p", { className: cn("text-muted-foreground text-lg sm:text-xl md:text-2xl", className), children });
|
|
60
|
+
}
|
|
61
|
+
function SlideBadge({ children, className }) {
|
|
62
|
+
return /* @__PURE__ */ jsx(
|
|
63
|
+
"span",
|
|
64
|
+
{
|
|
65
|
+
className: cn(
|
|
66
|
+
"bg-foreground text-background inline-block rounded-full px-4 py-1.5 text-sm font-semibold tracking-wide",
|
|
67
|
+
className
|
|
68
|
+
),
|
|
69
|
+
children
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
function SlideHeaderBadge({ children, className }) {
|
|
74
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-3", className), children: /* @__PURE__ */ jsx("span", { className: "text-foreground text-xl font-semibold tracking-tight italic sm:text-2xl", children }) });
|
|
75
|
+
}
|
|
76
|
+
function SlideCode({ children, className, title }) {
|
|
77
|
+
const html = highlight(children);
|
|
78
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("w-full max-w-2xl", className), children: [
|
|
79
|
+
title && /* @__PURE__ */ jsx("div", { className: "text-muted-foreground mb-2 text-xs font-medium tracking-wider uppercase", children: title }),
|
|
80
|
+
/* @__PURE__ */ jsx("pre", { className: "nxs-code-block border-foreground/10 bg-foreground/[0.03] overflow-x-auto border p-6 text-left font-mono text-[13px] leading-[1.7] sm:text-sm", children: /* @__PURE__ */ jsx("code", { dangerouslySetInnerHTML: { __html: html } }) })
|
|
81
|
+
] });
|
|
82
|
+
}
|
|
83
|
+
function SlideList({ children, className }) {
|
|
84
|
+
return /* @__PURE__ */ jsx("ul", { className: cn("flex flex-col gap-4 text-left", className), children });
|
|
85
|
+
}
|
|
86
|
+
function SlideListItem({ children, className }) {
|
|
87
|
+
return /* @__PURE__ */ jsxs("li", { className: cn("text-foreground/70 flex items-start gap-3 text-lg sm:text-xl", className), children: [
|
|
88
|
+
/* @__PURE__ */ jsx("span", { className: "bg-foreground/40 mt-2 block h-1.5 w-1.5 shrink-0 rounded-full", "aria-hidden": true }),
|
|
89
|
+
/* @__PURE__ */ jsx("span", { children })
|
|
90
|
+
] });
|
|
91
|
+
}
|
|
92
|
+
function SlideNote({ children, className }) {
|
|
93
|
+
return /* @__PURE__ */ jsx("p", { className: cn("text-muted-foreground/50 mt-4 text-sm", className), children });
|
|
94
|
+
}
|
|
95
|
+
function SlideDemo({
|
|
96
|
+
children,
|
|
97
|
+
className,
|
|
98
|
+
label
|
|
99
|
+
}) {
|
|
100
|
+
return /* @__PURE__ */ jsxs("div", { "data-slide-interactive": true, className: cn("w-full max-w-2xl", className), children: [
|
|
101
|
+
label && /* @__PURE__ */ jsx("div", { className: "text-muted-foreground mb-2 text-xs font-medium tracking-wider uppercase", children: label }),
|
|
102
|
+
/* @__PURE__ */ jsx("div", { className: "border-foreground/10 bg-foreground/[0.03] border p-6", children: /* @__PURE__ */ jsx(SlideDemoContent, { children }) })
|
|
103
|
+
] });
|
|
104
|
+
}
|
|
105
|
+
function SlideStatementList({ children, className }) {
|
|
106
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex w-full flex-col", className), children });
|
|
107
|
+
}
|
|
108
|
+
function SlideStatement({
|
|
109
|
+
title,
|
|
110
|
+
description,
|
|
111
|
+
className
|
|
112
|
+
}) {
|
|
113
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("border-foreground/10 border-t px-8 py-8 last:border-b sm:px-12 md:px-16", className), children: [
|
|
114
|
+
/* @__PURE__ */ jsx("h3", { className: "text-foreground text-lg font-bold sm:text-xl md:text-2xl", children: title }),
|
|
115
|
+
description && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mt-1 text-sm sm:text-base", children: description })
|
|
116
|
+
] });
|
|
117
|
+
}
|
|
118
|
+
function SlideSpeaker({ name, title, className }) {
|
|
119
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-4", className), children: [
|
|
120
|
+
/* @__PURE__ */ jsx("div", { className: "bg-muted h-12 w-12 shrink-0 rounded-full", "aria-hidden": true }),
|
|
121
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
122
|
+
/* @__PURE__ */ jsx("p", { className: "text-foreground/90 text-sm font-medium tracking-widest uppercase", children: name }),
|
|
123
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm tracking-wider uppercase", children: title })
|
|
124
|
+
] })
|
|
125
|
+
] });
|
|
126
|
+
}
|
|
127
|
+
function SlideSpeakerGrid({ children, className }) {
|
|
128
|
+
return /* @__PURE__ */ jsx("div", { className: cn("grid grid-cols-1 gap-6 sm:grid-cols-2", className), children });
|
|
129
|
+
}
|
|
130
|
+
function SlideSpeakerList({ children, className }) {
|
|
131
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col gap-6", className), children });
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
Slide,
|
|
135
|
+
SlideBadge,
|
|
136
|
+
SlideCode,
|
|
137
|
+
SlideDemo,
|
|
138
|
+
SlideHeaderBadge,
|
|
139
|
+
SlideList,
|
|
140
|
+
SlideListItem,
|
|
141
|
+
SlideNote,
|
|
142
|
+
SlideSpeaker,
|
|
143
|
+
SlideSpeakerGrid,
|
|
144
|
+
SlideSpeakerList,
|
|
145
|
+
SlideSplitLayout,
|
|
146
|
+
SlideStatement,
|
|
147
|
+
SlideStatementList,
|
|
148
|
+
SlideSubtitle,
|
|
149
|
+
SlideTitle
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=primitives.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/primitives.tsx"],"sourcesContent":["import { highlight } from 'sugar-high';\nimport { cn } from './cn';\nimport { SlideDemoContent } from './slide-demo-content';\nimport type { SlideAlign } from './types';\n\nexport function Slide({\n children,\n align = 'center',\n className,\n}: {\n children: React.ReactNode;\n align?: SlideAlign;\n className?: string;\n}) {\n return (\n <div\n className={cn(\n 'nxs-slide relative flex h-dvh w-dvw flex-col justify-center gap-8 px-12 py-20 sm:px-24 md:px-32 lg:px-40',\n align === 'center' && 'items-center text-center',\n align === 'left' && 'items-start text-left',\n className,\n )}\n >\n <div className=\"border-foreground/10 pointer-events-none absolute inset-4 border sm:inset-6\" aria-hidden />\n <div\n className={cn(\n 'relative z-10 flex max-w-4xl flex-col gap-6',\n align === 'center' && 'items-center',\n align === 'left' && 'items-start',\n )}\n >\n {children}\n </div>\n </div>\n );\n}\n\nexport function SlideSplitLayout({\n left,\n right,\n className,\n}: {\n left: React.ReactNode;\n right: React.ReactNode;\n className?: string;\n}) {\n return (\n <div className={cn('nxs-slide relative flex h-dvh w-dvw', className)}>\n <div className=\"border-foreground/10 pointer-events-none absolute inset-4 border sm:inset-6\" aria-hidden />\n <div className=\"relative z-10 flex w-1/2 flex-col justify-center px-12 py-20 sm:px-16 md:px-20 lg:px-24\">\n {left}\n </div>\n <div className=\"bg-foreground/10 absolute top-4 bottom-4 left-1/2 z-10 w-px sm:top-6 sm:bottom-6\" aria-hidden />\n <div className=\"relative z-10 flex w-1/2 flex-col justify-center\">{right}</div>\n </div>\n );\n}\n\nexport function SlideTitle({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <h1\n className={cn('text-foreground text-4xl font-extrabold sm:text-5xl md:text-6xl lg:text-7xl', className)}\n style={{ letterSpacing: '-0.04em' }}\n >\n {children}\n </h1>\n );\n}\n\nexport function SlideSubtitle({ children, className }: { children: React.ReactNode; className?: string }) {\n return <p className={cn('text-muted-foreground text-lg sm:text-xl md:text-2xl', className)}>{children}</p>;\n}\n\nexport function SlideBadge({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <span\n className={cn(\n 'bg-foreground text-background inline-block rounded-full px-4 py-1.5 text-sm font-semibold tracking-wide',\n className,\n )}\n >\n {children}\n </span>\n );\n}\n\nexport function SlideHeaderBadge({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <div className={cn('flex items-center gap-3', className)}>\n <span className=\"text-foreground text-xl font-semibold tracking-tight italic sm:text-2xl\">{children}</span>\n </div>\n );\n}\n\nexport function SlideCode({ children, className, title }: { children: string; className?: string; title?: string }) {\n const html = highlight(children);\n\n return (\n <div className={cn('w-full max-w-2xl', className)}>\n {title && <div className=\"text-muted-foreground mb-2 text-xs font-medium tracking-wider uppercase\">{title}</div>}\n <pre className=\"nxs-code-block border-foreground/10 bg-foreground/[0.03] overflow-x-auto border p-6 text-left font-mono text-[13px] leading-[1.7] sm:text-sm\">\n <code dangerouslySetInnerHTML={{ __html: html }} />\n </pre>\n </div>\n );\n}\n\nexport function SlideList({ children, className }: { children: React.ReactNode; className?: string }) {\n return <ul className={cn('flex flex-col gap-4 text-left', className)}>{children}</ul>;\n}\n\nexport function SlideListItem({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <li className={cn('text-foreground/70 flex items-start gap-3 text-lg sm:text-xl', className)}>\n <span className=\"bg-foreground/40 mt-2 block h-1.5 w-1.5 shrink-0 rounded-full\" aria-hidden />\n <span>{children}</span>\n </li>\n );\n}\n\nexport function SlideNote({ children, className }: { children: React.ReactNode; className?: string }) {\n return <p className={cn('text-muted-foreground/50 mt-4 text-sm', className)}>{children}</p>;\n}\n\nexport function SlideDemo({\n children,\n className,\n label,\n}: {\n children: React.ReactNode;\n className?: string;\n label?: string;\n}) {\n return (\n <div data-slide-interactive className={cn('w-full max-w-2xl', className)}>\n {label && <div className=\"text-muted-foreground mb-2 text-xs font-medium tracking-wider uppercase\">{label}</div>}\n <div className=\"border-foreground/10 bg-foreground/[0.03] border p-6\">\n <SlideDemoContent>{children}</SlideDemoContent>\n </div>\n </div>\n );\n}\n\nexport function SlideStatementList({ children, className }: { children: React.ReactNode; className?: string }) {\n return <div className={cn('flex w-full flex-col', className)}>{children}</div>;\n}\n\nexport function SlideStatement({\n title,\n description,\n className,\n}: {\n title: string;\n description?: string;\n className?: string;\n}) {\n return (\n <div className={cn('border-foreground/10 border-t px-8 py-8 last:border-b sm:px-12 md:px-16', className)}>\n <h3 className=\"text-foreground text-lg font-bold sm:text-xl md:text-2xl\">{title}</h3>\n {description && <p className=\"text-muted-foreground mt-1 text-sm sm:text-base\">{description}</p>}\n </div>\n );\n}\n\nexport function SlideSpeaker({ name, title, className }: { name: string; title: string; className?: string }) {\n return (\n <div className={cn('flex items-center gap-4', className)}>\n <div className=\"bg-muted h-12 w-12 shrink-0 rounded-full\" aria-hidden />\n <div>\n <p className=\"text-foreground/90 text-sm font-medium tracking-widest uppercase\">{name}</p>\n <p className=\"text-muted-foreground text-sm tracking-wider uppercase\">{title}</p>\n </div>\n </div>\n );\n}\n\nexport function SlideSpeakerGrid({ children, className }: { children: React.ReactNode; className?: string }) {\n return <div className={cn('grid grid-cols-1 gap-6 sm:grid-cols-2', className)}>{children}</div>;\n}\n\nexport function SlideSpeakerList({ children, className }: { children: React.ReactNode; className?: string }) {\n return <div className={cn('flex flex-col gap-6', className)}>{children}</div>;\n}\n"],"mappings":"AAeI,SAQE,KARF;AAfJ,SAAS,iBAAiB;AAC1B,SAAS,UAAU;AACnB,SAAS,wBAAwB;AAG1B,SAAS,MAAM;AAAA,EACpB;AAAA,EACA,QAAQ;AAAA,EACR;AACF,GAIG;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,UAAU,YAAY;AAAA,QACtB,UAAU,UAAU;AAAA,QACpB;AAAA,MACF;AAAA,MAEA;AAAA,4BAAC,SAAI,WAAU,+EAA8E,eAAW,MAAC;AAAA,QACzG;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,UAAU,YAAY;AAAA,cACtB,UAAU,UAAU;AAAA,YACtB;AAAA,YAEC;AAAA;AAAA,QACH;AAAA;AAAA;AAAA,EACF;AAEJ;AAEO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACE,qBAAC,SAAI,WAAW,GAAG,uCAAuC,SAAS,GACjE;AAAA,wBAAC,SAAI,WAAU,+EAA8E,eAAW,MAAC;AAAA,IACzG,oBAAC,SAAI,WAAU,2FACZ,gBACH;AAAA,IACA,oBAAC,SAAI,WAAU,oFAAmF,eAAW,MAAC;AAAA,IAC9G,oBAAC,SAAI,WAAU,oDAAoD,iBAAM;AAAA,KAC3E;AAEJ;AAEO,SAAS,WAAW,EAAE,UAAU,UAAU,GAAsD;AACrG,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,+EAA+E,SAAS;AAAA,MACtG,OAAO,EAAE,eAAe,UAAU;AAAA,MAEjC;AAAA;AAAA,EACH;AAEJ;AAEO,SAAS,cAAc,EAAE,UAAU,UAAU,GAAsD;AACxG,SAAO,oBAAC,OAAE,WAAW,GAAG,wDAAwD,SAAS,GAAI,UAAS;AACxG;AAEO,SAAS,WAAW,EAAE,UAAU,UAAU,GAAsD;AACrG,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAEO,SAAS,iBAAiB,EAAE,UAAU,UAAU,GAAsD;AAC3G,SACE,oBAAC,SAAI,WAAW,GAAG,2BAA2B,SAAS,GACrD,8BAAC,UAAK,WAAU,2EAA2E,UAAS,GACtG;AAEJ;AAEO,SAAS,UAAU,EAAE,UAAU,WAAW,MAAM,GAA6D;AAClH,QAAM,OAAO,UAAU,QAAQ;AAE/B,SACE,qBAAC,SAAI,WAAW,GAAG,oBAAoB,SAAS,GAC7C;AAAA,aAAS,oBAAC,SAAI,WAAU,2EAA2E,iBAAM;AAAA,IAC1G,oBAAC,SAAI,WAAU,gJACb,8BAAC,UAAK,yBAAyB,EAAE,QAAQ,KAAK,GAAG,GACnD;AAAA,KACF;AAEJ;AAEO,SAAS,UAAU,EAAE,UAAU,UAAU,GAAsD;AACpG,SAAO,oBAAC,QAAG,WAAW,GAAG,iCAAiC,SAAS,GAAI,UAAS;AAClF;AAEO,SAAS,cAAc,EAAE,UAAU,UAAU,GAAsD;AACxG,SACE,qBAAC,QAAG,WAAW,GAAG,gEAAgE,SAAS,GACzF;AAAA,wBAAC,UAAK,WAAU,iEAAgE,eAAW,MAAC;AAAA,IAC5F,oBAAC,UAAM,UAAS;AAAA,KAClB;AAEJ;AAEO,SAAS,UAAU,EAAE,UAAU,UAAU,GAAsD;AACpG,SAAO,oBAAC,OAAE,WAAW,GAAG,yCAAyC,SAAS,GAAI,UAAS;AACzF;AAEO,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACE,qBAAC,SAAI,0BAAsB,MAAC,WAAW,GAAG,oBAAoB,SAAS,GACpE;AAAA,aAAS,oBAAC,SAAI,WAAU,2EAA2E,iBAAM;AAAA,IAC1G,oBAAC,SAAI,WAAU,wDACb,8BAAC,oBAAkB,UAAS,GAC9B;AAAA,KACF;AAEJ;AAEO,SAAS,mBAAmB,EAAE,UAAU,UAAU,GAAsD;AAC7G,SAAO,oBAAC,SAAI,WAAW,GAAG,wBAAwB,SAAS,GAAI,UAAS;AAC1E;AAEO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACE,qBAAC,SAAI,WAAW,GAAG,2EAA2E,SAAS,GACrG;AAAA,wBAAC,QAAG,WAAU,4DAA4D,iBAAM;AAAA,IAC/E,eAAe,oBAAC,OAAE,WAAU,mDAAmD,uBAAY;AAAA,KAC9F;AAEJ;AAEO,SAAS,aAAa,EAAE,MAAM,OAAO,UAAU,GAAwD;AAC5G,SACE,qBAAC,SAAI,WAAW,GAAG,2BAA2B,SAAS,GACrD;AAAA,wBAAC,SAAI,WAAU,4CAA2C,eAAW,MAAC;AAAA,IACtE,qBAAC,SACC;AAAA,0BAAC,OAAE,WAAU,oEAAoE,gBAAK;AAAA,MACtF,oBAAC,OAAE,WAAU,0DAA0D,iBAAM;AAAA,OAC/E;AAAA,KACF;AAEJ;AAEO,SAAS,iBAAiB,EAAE,UAAU,UAAU,GAAsD;AAC3G,SAAO,oBAAC,SAAI,WAAW,GAAG,yCAAyC,SAAS,GAAI,UAAS;AAC3F;AAEO,SAAS,iBAAiB,EAAE,UAAU,UAAU,GAAsD;AAC3G,SAAO,oBAAC,SAAI,WAAW,GAAG,uBAAuB,SAAS,GAAI,UAAS;AACzE;","names":[]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { SlideDeckConfig } from './types.js';
|
|
3
|
+
|
|
4
|
+
declare function SlideDeck({ children, slides, basePath, showProgress, showCounter, className, }: SlideDeckConfig & {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
}): react_jsx_runtime.JSX.Element;
|
|
7
|
+
|
|
8
|
+
export { SlideDeck };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { usePathname, useRouter } from "next/navigation";
|
|
4
|
+
import { addTransitionType, useCallback, useEffect, useTransition, ViewTransition } from "react";
|
|
5
|
+
import { cn } from "./cn";
|
|
6
|
+
function SlideDeck({
|
|
7
|
+
children,
|
|
8
|
+
slides,
|
|
9
|
+
basePath = "/slides",
|
|
10
|
+
showProgress = true,
|
|
11
|
+
showCounter = true,
|
|
12
|
+
className
|
|
13
|
+
}) {
|
|
14
|
+
const router = useRouter();
|
|
15
|
+
const pathname = usePathname();
|
|
16
|
+
const [isPending, startTransition] = useTransition();
|
|
17
|
+
const total = slides.length;
|
|
18
|
+
const slideRoutePattern = new RegExp(`^${basePath}/(\\d+)$`);
|
|
19
|
+
const isSlideRoute = slideRoutePattern.test(pathname);
|
|
20
|
+
const current = (() => {
|
|
21
|
+
const match = pathname.match(slideRoutePattern);
|
|
22
|
+
return match ? Number(match[1]) - 1 : 0;
|
|
23
|
+
})();
|
|
24
|
+
const goTo = useCallback(
|
|
25
|
+
(index) => {
|
|
26
|
+
const clamped = Math.max(0, Math.min(index, total - 1));
|
|
27
|
+
if (clamped === current) return;
|
|
28
|
+
startTransition(() => {
|
|
29
|
+
addTransitionType(clamped > current ? "slide-forward" : "slide-back");
|
|
30
|
+
router.push(`${basePath}/${clamped + 1}`);
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
[basePath, current, router, startTransition, total]
|
|
34
|
+
);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (!isSlideRoute) return;
|
|
37
|
+
if (current > 0) router.prefetch(`${basePath}/${current}`);
|
|
38
|
+
if (current < total - 1) router.prefetch(`${basePath}/${current + 2}`);
|
|
39
|
+
}, [basePath, current, isSlideRoute, router, total]);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!isSlideRoute) return;
|
|
42
|
+
function onKeyDown(e) {
|
|
43
|
+
const target = e.target;
|
|
44
|
+
if (target.closest("[data-slide-interactive]") || target.matches('input, textarea, select, [contenteditable="true"]')) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (e.key === "ArrowRight" || e.key === " ") {
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
goTo(current + 1);
|
|
50
|
+
} else if (e.key === "ArrowLeft") {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
goTo(current - 1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
window.addEventListener("keydown", onKeyDown);
|
|
56
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
57
|
+
}, [current, goTo, isSlideRoute]);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const prev = document.body.style.overflow;
|
|
60
|
+
document.body.style.overflow = "hidden";
|
|
61
|
+
return () => {
|
|
62
|
+
document.body.style.overflow = prev;
|
|
63
|
+
};
|
|
64
|
+
}, []);
|
|
65
|
+
return /* @__PURE__ */ jsx(ViewTransition, { exit: "deck-unveil", children: /* @__PURE__ */ jsxs(
|
|
66
|
+
"div",
|
|
67
|
+
{
|
|
68
|
+
id: "slide-deck",
|
|
69
|
+
className: cn(
|
|
70
|
+
"bg-background text-foreground fixed inset-0 z-50 overflow-hidden font-sans select-none",
|
|
71
|
+
className
|
|
72
|
+
),
|
|
73
|
+
"data-pending": isPending ? "" : void 0,
|
|
74
|
+
children: [
|
|
75
|
+
/* @__PURE__ */ jsx(
|
|
76
|
+
ViewTransition,
|
|
77
|
+
{
|
|
78
|
+
enter: {
|
|
79
|
+
default: "slide-from-right",
|
|
80
|
+
"slide-back": "slide-from-left",
|
|
81
|
+
"slide-forward": "slide-from-right"
|
|
82
|
+
},
|
|
83
|
+
exit: {
|
|
84
|
+
default: "slide-to-left",
|
|
85
|
+
"slide-back": "slide-to-right",
|
|
86
|
+
"slide-forward": "slide-to-left"
|
|
87
|
+
},
|
|
88
|
+
children: /* @__PURE__ */ jsx("div", { children })
|
|
89
|
+
},
|
|
90
|
+
pathname
|
|
91
|
+
),
|
|
92
|
+
isSlideRoute && showProgress && /* @__PURE__ */ jsx(
|
|
93
|
+
"div",
|
|
94
|
+
{
|
|
95
|
+
className: "fixed bottom-8 left-1/2 z-50 flex -translate-x-1/2 items-center gap-1.5",
|
|
96
|
+
"aria-label": "Slide progress",
|
|
97
|
+
children: Array.from({ length: total }).map((_, i) => /* @__PURE__ */ jsx(
|
|
98
|
+
"div",
|
|
99
|
+
{
|
|
100
|
+
className: cn(
|
|
101
|
+
"h-1 transition-all duration-300",
|
|
102
|
+
i === current ? "bg-foreground w-6" : "bg-foreground/20 w-1"
|
|
103
|
+
)
|
|
104
|
+
},
|
|
105
|
+
i
|
|
106
|
+
))
|
|
107
|
+
}
|
|
108
|
+
),
|
|
109
|
+
isSlideRoute && showCounter && /* @__PURE__ */ jsxs("div", { className: "text-foreground/30 fixed right-8 bottom-8 z-50 font-mono text-xs tracking-wider", children: [
|
|
110
|
+
current + 1,
|
|
111
|
+
" / ",
|
|
112
|
+
total
|
|
113
|
+
] })
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
) });
|
|
117
|
+
}
|
|
118
|
+
export {
|
|
119
|
+
SlideDeck
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=slide-deck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/slide-deck.tsx"],"sourcesContent":["'use client';\n\nimport { usePathname, useRouter } from 'next/navigation';\nimport { addTransitionType, useCallback, useEffect, useTransition, ViewTransition } from 'react';\nimport { cn } from './cn';\nimport type { SlideDeckConfig } from './types';\n\nexport function SlideDeck({\n children,\n slides,\n basePath = '/slides',\n showProgress = true,\n showCounter = true,\n className,\n}: SlideDeckConfig & { children: React.ReactNode }) {\n const router = useRouter();\n const pathname = usePathname();\n const [isPending, startTransition] = useTransition();\n\n const total = slides.length;\n const slideRoutePattern = new RegExp(`^${basePath}/(\\\\d+)$`);\n const isSlideRoute = slideRoutePattern.test(pathname);\n const current = (() => {\n const match = pathname.match(slideRoutePattern);\n return match ? Number(match[1]) - 1 : 0;\n })();\n\n const goTo = useCallback(\n (index: number) => {\n const clamped = Math.max(0, Math.min(index, total - 1));\n if (clamped === current) return;\n startTransition(() => {\n addTransitionType(clamped > current ? 'slide-forward' : 'slide-back');\n router.push(`${basePath}/${clamped + 1}`);\n });\n },\n [basePath, current, router, startTransition, total],\n );\n\n useEffect(() => {\n if (!isSlideRoute) return;\n if (current > 0) router.prefetch(`${basePath}/${current}`);\n if (current < total - 1) router.prefetch(`${basePath}/${current + 2}`);\n }, [basePath, current, isSlideRoute, router, total]);\n\n useEffect(() => {\n if (!isSlideRoute) return;\n function onKeyDown(e: KeyboardEvent) {\n const target = e.target as HTMLElement;\n if (\n target.closest('[data-slide-interactive]') ||\n target.matches('input, textarea, select, [contenteditable=\"true\"]')\n ) {\n return;\n }\n if (e.key === 'ArrowRight' || e.key === ' ') {\n e.preventDefault();\n goTo(current + 1);\n } else if (e.key === 'ArrowLeft') {\n e.preventDefault();\n goTo(current - 1);\n }\n }\n window.addEventListener('keydown', onKeyDown);\n return () => window.removeEventListener('keydown', onKeyDown);\n }, [current, goTo, isSlideRoute]);\n\n useEffect(() => {\n const prev = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n return () => {\n document.body.style.overflow = prev;\n };\n }, []);\n\n return (\n <ViewTransition exit=\"deck-unveil\">\n <div\n id=\"slide-deck\"\n className={cn(\n 'bg-background text-foreground fixed inset-0 z-50 overflow-hidden font-sans select-none',\n className,\n )}\n data-pending={isPending ? '' : undefined}\n >\n <ViewTransition\n key={pathname}\n enter={{\n default: 'slide-from-right',\n 'slide-back': 'slide-from-left',\n 'slide-forward': 'slide-from-right',\n }}\n exit={{\n default: 'slide-to-left',\n 'slide-back': 'slide-to-right',\n 'slide-forward': 'slide-to-left',\n }}\n >\n <div>{children}</div>\n </ViewTransition>\n\n {isSlideRoute && showProgress && (\n <div\n className=\"fixed bottom-8 left-1/2 z-50 flex -translate-x-1/2 items-center gap-1.5\"\n aria-label=\"Slide progress\"\n >\n {Array.from({ length: total }).map((_, i) => (\n <div\n key={i}\n className={cn(\n 'h-1 transition-all duration-300',\n i === current ? 'bg-foreground w-6' : 'bg-foreground/20 w-1',\n )}\n />\n ))}\n </div>\n )}\n\n {isSlideRoute && showCounter && (\n <div className=\"text-foreground/30 fixed right-8 bottom-8 z-50 font-mono text-xs tracking-wider\">\n {current + 1} / {total}\n </div>\n )}\n </div>\n </ViewTransition>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
function SlideDemoContent({ children }) {
|
|
5
|
+
const ref = useRef(null);
|
|
6
|
+
const [minHeight, setMinHeight] = useState(void 0);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const el = ref.current;
|
|
9
|
+
if (!el) return;
|
|
10
|
+
const observer = new ResizeObserver((entries) => {
|
|
11
|
+
for (const entry of entries) {
|
|
12
|
+
const height = entry.borderBoxSize[0].blockSize;
|
|
13
|
+
setMinHeight((prev) => prev === void 0 ? height : Math.max(prev, height));
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
observer.observe(el);
|
|
17
|
+
return () => observer.disconnect();
|
|
18
|
+
}, []);
|
|
19
|
+
return /* @__PURE__ */ jsx("div", { ref, style: minHeight !== void 0 ? { minHeight } : void 0, children });
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
SlideDemoContent
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=slide-demo-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/slide-demo-content.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nexport function SlideDemoContent({ children }: { children: React.ReactNode }) {\n const ref = useRef<HTMLDivElement>(null);\n const [minHeight, setMinHeight] = useState<number | undefined>(undefined);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n const observer = new ResizeObserver(entries => {\n for (const entry of entries) {\n const height = entry.borderBoxSize[0].blockSize;\n setMinHeight(prev => (prev === undefined ? height : Math.max(prev, height)));\n }\n });\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n\n return (\n <div ref={ref} style={minHeight !== undefined ? { minHeight } : undefined}>\n {children}\n </div>\n );\n}\n"],"mappings":";AAuBI;AArBJ,SAAS,WAAW,QAAQ,gBAAgB;AAErC,SAAS,iBAAiB,EAAE,SAAS,GAAkC;AAC5E,QAAM,MAAM,OAAuB,IAAI;AACvC,QAAM,CAAC,WAAW,YAAY,IAAI,SAA6B,MAAS;AAExE,YAAU,MAAM;AACd,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AAET,UAAM,WAAW,IAAI,eAAe,aAAW;AAC7C,iBAAW,SAAS,SAAS;AAC3B,cAAM,SAAS,MAAM,cAAc,CAAC,EAAE;AACtC,qBAAa,UAAS,SAAS,SAAY,SAAS,KAAK,IAAI,MAAM,MAAM,CAAE;AAAA,MAC7E;AAAA,IACF,CAAC;AACD,aAAS,QAAQ,EAAE;AACnB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,CAAC;AAEL,SACE,oBAAC,SAAI,KAAU,OAAO,cAAc,SAAY,EAAE,UAAU,IAAI,QAC7D,UACH;AAEJ;","names":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { SlideLinkVariant } from './types.js';
|
|
3
|
+
|
|
4
|
+
declare function SlideLink({ href, children, className, variant, }: {
|
|
5
|
+
href: string;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
variant?: SlideLinkVariant;
|
|
9
|
+
}): react_jsx_runtime.JSX.Element;
|
|
10
|
+
|
|
11
|
+
export { SlideLink };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import Link from "next/link";
|
|
3
|
+
import { cn } from "./cn";
|
|
4
|
+
function SlideLink({
|
|
5
|
+
href,
|
|
6
|
+
children,
|
|
7
|
+
className,
|
|
8
|
+
variant = "primary"
|
|
9
|
+
}) {
|
|
10
|
+
return /* @__PURE__ */ jsx(
|
|
11
|
+
Link,
|
|
12
|
+
{
|
|
13
|
+
href,
|
|
14
|
+
className: cn(
|
|
15
|
+
"nxs-slide-link inline-flex items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium tracking-wide transition-all",
|
|
16
|
+
variant === "primary" && "bg-primary text-primary-foreground hover:bg-primary/80",
|
|
17
|
+
variant === "ghost" && "border-border border bg-transparent hover:bg-muted",
|
|
18
|
+
"mt-2",
|
|
19
|
+
className
|
|
20
|
+
),
|
|
21
|
+
children
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
SlideLink
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=slide-link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/slide-link.tsx"],"sourcesContent":["import Link from 'next/link';\nimport { cn } from './cn';\nimport type { SlideLinkVariant } from './types';\n\nexport function SlideLink({\n href,\n children,\n className,\n variant = 'primary',\n}: {\n href: string;\n children: React.ReactNode;\n className?: string;\n variant?: SlideLinkVariant;\n}) {\n return (\n <Link\n href={href}\n className={cn(\n 'nxs-slide-link inline-flex items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium tracking-wide transition-all',\n variant === 'primary' && 'bg-primary text-primary-foreground hover:bg-primary/80',\n variant === 'ghost' && 'border-border border bg-transparent hover:bg-muted',\n 'mt-2',\n className,\n )}\n >\n {children}\n </Link>\n );\n}\n"],"mappings":"AAgBI;AAhBJ,OAAO,UAAU;AACjB,SAAS,UAAU;AAGZ,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AACZ,GAKG;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA,YAAY,aAAa;AAAA,QACzB,YAAY,WAAW;AAAA,QACvB;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":[]}
|
package/dist/slides.css
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/* nextjs-slides — Slide transition animations and code theme tokens */
|
|
2
|
+
|
|
3
|
+
/* sugar-high code theme — light */
|
|
4
|
+
:root {
|
|
5
|
+
--sh-class: #7200c4;
|
|
6
|
+
--sh-identifier: #171717;
|
|
7
|
+
--sh-keyword: #b32c62;
|
|
8
|
+
--sh-string: #397c3b;
|
|
9
|
+
--sh-property: #005ee9;
|
|
10
|
+
--sh-entity: #005ee9;
|
|
11
|
+
--sh-jsxliterals: #171717;
|
|
12
|
+
--sh-sign: #666;
|
|
13
|
+
--sh-comment: #666666;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* sugar-high code theme — dark */
|
|
17
|
+
.dark {
|
|
18
|
+
--sh-class: #b675f1;
|
|
19
|
+
--sh-identifier: #ededed;
|
|
20
|
+
--sh-keyword: #f05b8d;
|
|
21
|
+
--sh-string: #58c760;
|
|
22
|
+
--sh-property: #62a6ff;
|
|
23
|
+
--sh-entity: #62a6ff;
|
|
24
|
+
--sh-jsxliterals: #ededed;
|
|
25
|
+
--sh-sign: #a1a1a1;
|
|
26
|
+
--sh-comment: #a1a1a1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Slide transition keyframes */
|
|
30
|
+
@keyframes nxs-fade {
|
|
31
|
+
from {
|
|
32
|
+
filter: blur(3px);
|
|
33
|
+
opacity: 0;
|
|
34
|
+
}
|
|
35
|
+
to {
|
|
36
|
+
filter: blur(0);
|
|
37
|
+
opacity: 1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@keyframes nxs-slide {
|
|
42
|
+
from {
|
|
43
|
+
translate: var(--nxs-slide-offset);
|
|
44
|
+
}
|
|
45
|
+
to {
|
|
46
|
+
translate: 0;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@keyframes nxs-slide-in {
|
|
51
|
+
from {
|
|
52
|
+
opacity: 0;
|
|
53
|
+
transform: translateX(var(--nxs-slide-offset, 60px));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@keyframes nxs-slide-out {
|
|
58
|
+
to {
|
|
59
|
+
opacity: 0;
|
|
60
|
+
transform: translateX(var(--nxs-slide-offset, -60px));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@keyframes nxs-deck-unveil {
|
|
65
|
+
to {
|
|
66
|
+
opacity: 0;
|
|
67
|
+
transform: scale(1.03);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Reset default view transition animations */
|
|
72
|
+
::view-transition-old(*) {
|
|
73
|
+
animation: none;
|
|
74
|
+
}
|
|
75
|
+
::view-transition-new(*) {
|
|
76
|
+
animation: none;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Slide forward */
|
|
80
|
+
::view-transition-new(.slide-from-right) {
|
|
81
|
+
--nxs-slide-offset: 60px;
|
|
82
|
+
animation: nxs-slide-in 200ms ease-out both;
|
|
83
|
+
}
|
|
84
|
+
::view-transition-old(.slide-to-left) {
|
|
85
|
+
--nxs-slide-offset: -60px;
|
|
86
|
+
animation: nxs-slide-out 200ms ease-out both;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Slide backward */
|
|
90
|
+
::view-transition-new(.slide-from-left) {
|
|
91
|
+
--nxs-slide-offset: -60px;
|
|
92
|
+
animation: nxs-slide-in 200ms ease-out both;
|
|
93
|
+
}
|
|
94
|
+
::view-transition-old(.slide-to-right) {
|
|
95
|
+
--nxs-slide-offset: 60px;
|
|
96
|
+
animation: nxs-slide-out 200ms ease-out both;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Deck exit (leaving slide deck) */
|
|
100
|
+
::view-transition-old(.deck-unveil) {
|
|
101
|
+
animation: nxs-deck-unveil 300ms ease-out both;
|
|
102
|
+
}
|
|
103
|
+
::view-transition-new(.deck-unveil) {
|
|
104
|
+
animation: none;
|
|
105
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type SlideAlign = 'center' | 'left';
|
|
2
|
+
type SlideLinkVariant = 'primary' | 'ghost';
|
|
3
|
+
interface SlideDeckConfig {
|
|
4
|
+
slides: React.ReactNode[];
|
|
5
|
+
/** Base path for slide URLs. Defaults to "/slides" */
|
|
6
|
+
basePath?: string;
|
|
7
|
+
/** Show progress dots at the bottom. Defaults to true */
|
|
8
|
+
showProgress?: boolean;
|
|
9
|
+
/** Show slide counter (e.g. "3 / 10"). Defaults to true */
|
|
10
|
+
showCounter?: boolean;
|
|
11
|
+
/** Additional className for the deck container */
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type { SlideAlign, SlideDeckConfig, SlideLinkVariant };
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nextjs-slides",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Composable slide deck primitives for Next.js — powered by React 19 ViewTransitions, Tailwind CSS, and sugar-high syntax highlighting.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./styles.css": "./dist/slides.css"
|
|
13
|
+
},
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"next": ">=15.0.0",
|
|
28
|
+
"react": ">=19.0.0",
|
|
29
|
+
"react-dom": ">=19.0.0",
|
|
30
|
+
"tailwindcss": ">=4.0.0"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"clsx": "^2.1.1",
|
|
34
|
+
"sugar-high": "^0.9.5",
|
|
35
|
+
"tailwind-merge": "^3.5.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/react": "^19",
|
|
39
|
+
"@types/react-dom": "^19",
|
|
40
|
+
"next": "^16.0.0",
|
|
41
|
+
"react": "^19.0.0",
|
|
42
|
+
"react-dom": "^19.0.0",
|
|
43
|
+
"tsup": "^8.4.0",
|
|
44
|
+
"typescript": "^5"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"nextjs",
|
|
48
|
+
"slides",
|
|
49
|
+
"presentation",
|
|
50
|
+
"slide-deck",
|
|
51
|
+
"react",
|
|
52
|
+
"tailwind",
|
|
53
|
+
"view-transitions",
|
|
54
|
+
"sugar-high"
|
|
55
|
+
],
|
|
56
|
+
"repository": {
|
|
57
|
+
"type": "git",
|
|
58
|
+
"url": "https://github.com/aurorascharff/nextjs-slides"
|
|
59
|
+
}
|
|
60
|
+
}
|