nextjs-slides 0.1.3 → 0.1.4
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 +4 -2
- package/dist/slide-deck.js +2 -1
- package/dist/slide-deck.js.map +1 -1
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# nextjs-slides
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/nextjs-slides)
|
|
4
|
+
|
|
3
5
|
Composable slide deck primitives for Next.js — powered by React 19 ViewTransitions, Tailwind CSS v4, and sugar-high syntax highlighting.
|
|
4
6
|
|
|
5
7
|
Build full presentations from React components with URL-based routing, keyboard navigation, progress indicators, and smooth slide transitions — all declarative.
|
|
@@ -155,7 +157,7 @@ That's it. Navigate to `/slides` and you have a full slide deck.
|
|
|
155
157
|
|
|
156
158
|
- **`<SlideCode>`** — Syntax-highlighted code block (sugar-high). Props: `title`, `className`. Pass code as `children` string.
|
|
157
159
|
- **`<SlideList>`** / **`<SlideListItem>`** — Bullet list.
|
|
158
|
-
- **`<SlideDemo>`** — Interactive component container. Keyboard
|
|
160
|
+
- **`<SlideDemo>`** — Interactive component container. Keyboard navigation is disabled inside so you can use inputs and buttons. Props: `label`, `className`.
|
|
159
161
|
|
|
160
162
|
### Structured
|
|
161
163
|
|
|
@@ -175,7 +177,7 @@ That's it. Navigate to `/slides` and you have a full slide deck.
|
|
|
175
177
|
| `→` or Space | Next slide |
|
|
176
178
|
| `←` | Previous slide |
|
|
177
179
|
|
|
178
|
-
|
|
180
|
+
Keyboard events are ignored inside `<SlideDemo>`, inputs, and textareas so you can interact without advancing slides.
|
|
179
181
|
|
|
180
182
|
## Custom Base Path
|
|
181
183
|
|
package/dist/slide-deck.js
CHANGED
|
@@ -62,7 +62,7 @@ function SlideDeck({
|
|
|
62
62
|
document.body.style.overflow = prev;
|
|
63
63
|
};
|
|
64
64
|
}, []);
|
|
65
|
-
return /* @__PURE__ */ jsx(ViewTransition, { exit: "deck-unveil", children: /* @__PURE__ */ jsxs(
|
|
65
|
+
return /* @__PURE__ */ jsx(ViewTransition, { default: "none", exit: "deck-unveil", children: /* @__PURE__ */ jsxs(
|
|
66
66
|
"div",
|
|
67
67
|
{
|
|
68
68
|
id: "slide-deck",
|
|
@@ -75,6 +75,7 @@ function SlideDeck({
|
|
|
75
75
|
/* @__PURE__ */ jsx(
|
|
76
76
|
ViewTransition,
|
|
77
77
|
{
|
|
78
|
+
default: "none",
|
|
78
79
|
enter: {
|
|
79
80
|
default: "slide-from-right",
|
|
80
81
|
"slide-back": "slide-from-left",
|
package/dist/slide-deck.js.map
CHANGED
|
@@ -1 +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":";
|
|
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 default=\"none\" 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 default=\"none\"\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":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nextjs-slides",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Composable slide deck primitives for Next.js — powered by React 19 ViewTransitions, Tailwind CSS, and sugar-high syntax highlighting.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsup",
|
|
22
22
|
"dev": "tsup --watch",
|
|
23
|
+
"lint": "eslint .",
|
|
24
|
+
"lint:fix": "eslint . --fix",
|
|
23
25
|
"test": "vitest run",
|
|
24
26
|
"test:watch": "vitest",
|
|
25
27
|
"typecheck": "tsc --noEmit",
|
|
@@ -37,16 +39,19 @@
|
|
|
37
39
|
"tailwind-merge": "^3.5.0"
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
42
|
+
"@eslint/js": "^10.0.1",
|
|
40
43
|
"@testing-library/jest-dom": "^6.6.0",
|
|
41
44
|
"@testing-library/react": "^16.0.0",
|
|
42
45
|
"@types/react": "^19",
|
|
43
46
|
"@types/react-dom": "^19",
|
|
47
|
+
"eslint": "^10.0.2",
|
|
44
48
|
"jsdom": "^25.0.0",
|
|
45
49
|
"next": "^16.0.0",
|
|
46
50
|
"react": "^19.0.0",
|
|
47
51
|
"react-dom": "^19.0.0",
|
|
48
52
|
"tsup": "^8.4.0",
|
|
49
53
|
"typescript": "^5",
|
|
54
|
+
"typescript-eslint": "^8.56.1",
|
|
50
55
|
"vitest": "^2.0.0"
|
|
51
56
|
},
|
|
52
57
|
"keywords": [
|