@octanejs/embla-carousel 0.0.1
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/LICENSE +21 -0
- package/README.md +38 -0
- package/UPSTREAM.md +58 -0
- package/package.json +53 -0
- package/src/components/useEmblaCarousel.ts +66 -0
- package/src/index.ts +5 -0
- package/status.json +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) David Jerleke.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @octanejs/embla-carousel
|
|
2
|
+
|
|
3
|
+
Octane binding for `embla-carousel-react@8.6.0`. It keeps the familiar default
|
|
4
|
+
`useEmblaCarousel` API while reusing Embla's framework-neutral runtime.
|
|
5
|
+
|
|
6
|
+
```tsrx
|
|
7
|
+
import useEmblaCarousel from '@octanejs/embla-carousel';
|
|
8
|
+
|
|
9
|
+
export function Carousel() @{
|
|
10
|
+
const [viewportRef, api] = useEmblaCarousel({ loop: true });
|
|
11
|
+
|
|
12
|
+
<section>
|
|
13
|
+
<div ref={viewportRef}>
|
|
14
|
+
<div>
|
|
15
|
+
<div>First</div>
|
|
16
|
+
<div>Second</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
<button onClick={() => api?.scrollPrev()}>Previous</button>
|
|
20
|
+
<button onClick={() => api?.scrollNext()}>Next</button>
|
|
21
|
+
</section>
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Replace the React import:
|
|
26
|
+
|
|
27
|
+
```diff
|
|
28
|
+
- import useEmblaCarousel from 'embla-carousel-react';
|
|
29
|
+
+ import useEmblaCarousel from '@octanejs/embla-carousel';
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The call signature, tuple result, exported types, and `globalOptions` property
|
|
33
|
+
match upstream. The published package has no React runtime or type dependency.
|
|
34
|
+
|
|
35
|
+
Lifecycle and option updates run against the pinned React adapter as a live
|
|
36
|
+
differential oracle. Real layout and pointer physics are delegated unchanged to
|
|
37
|
+
`embla-carousel`. The required Chromium lane verifies nonzero layout, scrolling,
|
|
38
|
+
selection updates, and destroy cleanup against the real core.
|
package/UPSTREAM.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Upstream Embla Carousel React audit
|
|
2
|
+
|
|
3
|
+
This port targets the immutable `embla-carousel-react@8.6.0` release.
|
|
4
|
+
|
|
5
|
+
- Repository: `https://github.com/davidjerleke/embla-carousel`
|
|
6
|
+
- Tag commit: `0fe65834136f1aa35e4c1a4a477e5ccb4bb5ee54`
|
|
7
|
+
- npm integrity: `sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==`
|
|
8
|
+
- Runtime/core oracle: `embla-carousel@8.6.0`
|
|
9
|
+
- Reactive utility oracle: `embla-carousel-reactive-utils@8.6.0`
|
|
10
|
+
- React oracle: `react@19.2.7` / `react-dom@19.2.7` (exact; required differential lane)
|
|
11
|
+
- React types: `@types/react@19.2.17` (exact; declaration consumer matrix)
|
|
12
|
+
- Advertised compatibility: exactly the 8.6.0 React adapter surface
|
|
13
|
+
|
|
14
|
+
The MIT-licensed tag supplied the byte-exact files in `upstream/`. The npm
|
|
15
|
+
artifact supplied the declaration and bundle check; it contains no source or
|
|
16
|
+
tests. Vendored evidence is excluded from the published `files` list.
|
|
17
|
+
|
|
18
|
+
## Export crosswalk
|
|
19
|
+
|
|
20
|
+
| Upstream root export | Octane disposition | Evidence |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| `default` (`useEmblaCarousel`) | Ported with `octane` hooks | `tests/conformance/binding.test.ts` |
|
|
23
|
+
| `UseEmblaCarouselType` | Ported without semantic change | `typetests/public.test-d.ts` |
|
|
24
|
+
| `EmblaViewportRefType` | Ported without semantic change | `typetests/public.test-d.ts` |
|
|
25
|
+
| `./package.json` | Package metadata export | package pack check |
|
|
26
|
+
|
|
27
|
+
## Source and test disposition
|
|
28
|
+
|
|
29
|
+
| Upstream artifact | Disposition |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| `embla-carousel-react/src/index.ts` | Mirrored by `src/index.ts` |
|
|
32
|
+
| `embla-carousel-react/src/components/useEmblaCarousel.ts` | Ported module-by-module; only the framework hook import and formatting differ |
|
|
33
|
+
| `embla-carousel-reactive-utils/src/components/utils.ts` | Reused unchanged from the published framework-neutral dependency |
|
|
34
|
+
| `embla-carousel-reactive-utils/src/index.ts` | Reused unchanged from the published framework-neutral dependency |
|
|
35
|
+
| `embla-carousel-reactive-utils/src/__tests__/utils.test.ts` | Upstream suite registered as the `embla-pristine-utils` parity lane (`embla-carousel-pristine-utils`) |
|
|
36
|
+
| React adapter tests | No test files exist at the pinned tag; `package.json`'s test script reports no tests |
|
|
37
|
+
| Upstream type tests | None exist at the pinned tag; pristine declaration consumption and adapted public types are checked locally |
|
|
38
|
+
|
|
39
|
+
## Differences
|
|
40
|
+
|
|
41
|
+
There are no consumer-visible API divergences. Octane compiles the hook calls
|
|
42
|
+
to stable call-site slots and uses native callback refs; neither changes the
|
|
43
|
+
published tuple contract.
|
|
44
|
+
|
|
45
|
+
Real pointer physics and layout remain owned by the unchanged Embla core. The
|
|
46
|
+
adapter suite uses a controlled constructor boundary because jsdom has no
|
|
47
|
+
layout. An unpaired Octane browser harness under `tests/browser` opts into
|
|
48
|
+
`testExecution.group: heavy-browser` and runs in Chromium via the generic
|
|
49
|
+
heavy-browser discovery lane. It verifies nonzero layout, scrolling, selection
|
|
50
|
+
updates, and destroy cleanup. Browser behavior is not simulated with jsdom
|
|
51
|
+
geometry mocks.
|
|
52
|
+
|
|
53
|
+
Every port-authored runtime test and repo-authored type probe is classified in
|
|
54
|
+
`audit/test-classifications.json`. Unpaired conformance/browser/hydration/SSR
|
|
55
|
+
cases stay Octane-only and outside React-parity ownership. The required
|
|
56
|
+
pristine/adapted type probes are `repo-authored-type-oracle` evidence paired
|
|
57
|
+
with the type-parity lanes; the required differential lane is the React runtime
|
|
58
|
+
oracle and rejects React/ReactDOM version drift from the pins above.
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@octanejs/embla-carousel",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=22.22.2"
|
|
8
|
+
},
|
|
9
|
+
"description": "Embla Carousel bindings for Octane, API-compatible with embla-carousel-react 8.6.0.",
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/octanejs/octane.git",
|
|
16
|
+
"directory": "packages/embla-carousel"
|
|
17
|
+
},
|
|
18
|
+
"main": "src/index.ts",
|
|
19
|
+
"module": "src/index.ts",
|
|
20
|
+
"types": "src/index.ts",
|
|
21
|
+
"files": [
|
|
22
|
+
"src",
|
|
23
|
+
"README.md",
|
|
24
|
+
"UPSTREAM.md",
|
|
25
|
+
"status.json"
|
|
26
|
+
],
|
|
27
|
+
"exports": {
|
|
28
|
+
".": "./src/index.ts",
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"embla-carousel": "8.6.0",
|
|
33
|
+
"embla-carousel-reactive-utils": "8.6.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"octane": "0.1.35"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/react": "19.2.17",
|
|
40
|
+
"embla-carousel-react": "8.6.0",
|
|
41
|
+
"playwright": "^1.61.1",
|
|
42
|
+
"react": "19.2.7",
|
|
43
|
+
"react-dom": "19.2.7",
|
|
44
|
+
"vite": "^8.1.5",
|
|
45
|
+
"vitest": "^4.1.10",
|
|
46
|
+
"@octanejs/testing-library": "0.1.32",
|
|
47
|
+
"octane": "0.1.35"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"test": "node audit/type-parity.mjs && vitest run --root ../.. --project embla-carousel --project embla-carousel-pristine-utils --project embla-carousel-differential --project embla-carousel-hydration --project embla-carousel-ssr --project embla-carousel-audit --project embla-carousel-browser",
|
|
51
|
+
"typecheck": "tsrx-tsc --noEmit -p tsconfig.json && tsc --noEmit -p typetests/pristine.tsconfig.json && tsrx-tsc --noEmit -p typetests/tsconfig.json"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'octane';
|
|
2
|
+
import { areOptionsEqual, arePluginsEqual, canUseDOM } from 'embla-carousel-reactive-utils';
|
|
3
|
+
import EmblaCarousel, {
|
|
4
|
+
type EmblaCarouselType,
|
|
5
|
+
type EmblaOptionsType,
|
|
6
|
+
type EmblaPluginType,
|
|
7
|
+
} from 'embla-carousel';
|
|
8
|
+
|
|
9
|
+
export type EmblaViewportRefType = <ViewportElement extends HTMLElement>(
|
|
10
|
+
instance: ViewportElement | null,
|
|
11
|
+
) => void;
|
|
12
|
+
|
|
13
|
+
export type UseEmblaCarouselType = [EmblaViewportRefType, EmblaCarouselType | undefined];
|
|
14
|
+
|
|
15
|
+
function useEmblaCarousel(
|
|
16
|
+
options?: EmblaOptionsType,
|
|
17
|
+
plugins?: EmblaPluginType[],
|
|
18
|
+
): UseEmblaCarouselType;
|
|
19
|
+
function useEmblaCarousel(
|
|
20
|
+
...args: [options?: EmblaOptionsType, plugins?: EmblaPluginType[], slot?: symbol]
|
|
21
|
+
): UseEmblaCarouselType {
|
|
22
|
+
const hasCompilerSlot = typeof args.at(-1) === 'symbol';
|
|
23
|
+
const options = (hasCompilerSlot && args.length === 1 ? undefined : args[0]) ?? {};
|
|
24
|
+
const plugins = (hasCompilerSlot && args.length === 2 ? undefined : args[1]) ?? [];
|
|
25
|
+
const storedOptions = useRef(options);
|
|
26
|
+
const storedPlugins = useRef(plugins);
|
|
27
|
+
const [emblaApi, setEmblaApi] = useState<EmblaCarouselType>();
|
|
28
|
+
const [viewport, setViewport] = useState<HTMLElement>();
|
|
29
|
+
|
|
30
|
+
const reInit = useCallback(() => {
|
|
31
|
+
if (emblaApi) emblaApi.reInit(storedOptions.current, storedPlugins.current);
|
|
32
|
+
}, [emblaApi]);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (areOptionsEqual(storedOptions.current, options)) return;
|
|
36
|
+
storedOptions.current = options;
|
|
37
|
+
reInit();
|
|
38
|
+
}, [options, reInit]);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (arePluginsEqual(storedPlugins.current, plugins)) return;
|
|
42
|
+
storedPlugins.current = plugins;
|
|
43
|
+
reInit();
|
|
44
|
+
}, [plugins, reInit]);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (canUseDOM() && viewport) {
|
|
48
|
+
EmblaCarousel.globalOptions = useEmblaCarousel.globalOptions;
|
|
49
|
+
const newEmblaApi = EmblaCarousel(viewport, storedOptions.current, storedPlugins.current);
|
|
50
|
+
setEmblaApi(newEmblaApi);
|
|
51
|
+
return () => newEmblaApi.destroy();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
setEmblaApi(undefined);
|
|
55
|
+
}, [viewport, setEmblaApi]);
|
|
56
|
+
|
|
57
|
+
return [setViewport as EmblaViewportRefType, emblaApi];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
declare namespace useEmblaCarousel {
|
|
61
|
+
let globalOptions: EmblaOptionsType | undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
useEmblaCarousel.globalOptions = undefined;
|
|
65
|
+
|
|
66
|
+
export default useEmblaCarousel;
|
package/src/index.ts
ADDED
package/status.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"upstream": {
|
|
3
|
+
"package": "embla-carousel-react",
|
|
4
|
+
"version": "8.6.0",
|
|
5
|
+
"core": "embla-carousel@8.6.0"
|
|
6
|
+
},
|
|
7
|
+
"surface": "Complete package-root adapter: default useEmblaCarousel hook, its viewport-ref and tuple types, and globalOptions; the framework-neutral Embla core and reactive equality utilities are reused unchanged.",
|
|
8
|
+
"divergences": [],
|
|
9
|
+
"ssr": "The hook constructs no carousel without DOM globals; client attachment initializes the core.",
|
|
10
|
+
"verified": "2026-08-02",
|
|
11
|
+
"notes": [
|
|
12
|
+
"The live React differential covers adapter lifecycle. A required Chromium lane verifies real layout, scrolling, selection updates, and destroy cleanup."
|
|
13
|
+
]
|
|
14
|
+
}
|