├── .nvmrc
├── .husky
├── pre-commit
└── commit-msg
├── src
├── main.ts
├── components
│ └── Waypoint
│ │ ├── index.ts
│ │ ├── observer.ts
│ │ └── component.ts
├── env.d.ts
├── __tests__
│ ├── ssr.spec.ts
│ └── example.spec.ts
└── App.vue
├── tsconfig.vitest.json
├── tsconfig.config.json
├── tsconfig.json
├── tsconfig.app.json
├── .gitignore
├── index.html
├── vite.config.ts
├── .eslintrc.cjs
├── vite.config.lib.ts
├── .github
└── workflows
│ ├── release.yml
│ └── pr.yml
├── LICENSE
├── package.json
├── CHANGELOG.md
└── README.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | 16
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx --no -- commitlint --edit $1
5 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from "vue";
2 | import App from "./App.vue";
3 |
4 | createApp(App).mount("#app");
5 |
--------------------------------------------------------------------------------
/src/components/Waypoint/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Waypoint } from "./component";
2 | export { Going, Direction, type WaypointState } from "./observer";
3 |
--------------------------------------------------------------------------------
/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module "*.vue" {
4 | import type { DefineComponent } from "vue";
5 | const component: DefineComponent;
6 | export default component;
7 | }
8 |
--------------------------------------------------------------------------------
/tsconfig.vitest.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.app.json",
3 | "exclude": [],
4 | "compilerOptions": {
5 | "composite": true,
6 | "lib": [],
7 | "types": ["node", "jsdom"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.node.json",
3 | "include": ["vite.config.*", "vitest.config.*"],
4 | "compilerOptions": {
5 | "composite": true,
6 | "types": ["node"]
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.config.json"
6 | },
7 | {
8 | "path": "./tsconfig.app.json"
9 | },
10 | {
11 | "path": "./tsconfig.vitest.json"
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.web.json",
3 | "include": ["src/env.d.ts", "src/**/*", "src/**/*.vue"],
4 | "exclude": ["src/**/__tests__/*"],
5 | "compilerOptions": {
6 | "composite": true,
7 | "baseUrl": ".",
8 | "paths": {
9 | "@/*": ["./src/*"]
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
25 | # Local Netlify folder
26 | .netlify
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { fileURLToPath, URL } from "node:url";
2 | import { defineConfig } from "vite";
3 | import vue from "@vitejs/plugin-vue";
4 |
5 | // https://vitejs.dev/config/
6 | export default defineConfig({
7 | plugins: [vue()],
8 | resolve: {
9 | alias: {
10 | "@": fileURLToPath(new URL("./src", import.meta.url)),
11 | },
12 | },
13 | server: {
14 | host: true,
15 | port: 3000,
16 | },
17 | });
18 |
--------------------------------------------------------------------------------
/src/__tests__/ssr.spec.ts:
--------------------------------------------------------------------------------
1 | // @vitest-environment jsdom
2 |
3 | import { describe, it, expect } from "vitest";
4 | import { renderToString } from "@vue/test-utils";
5 | import { Waypoint } from "@/components/Waypoint";
6 |
7 | describe("Template SSR output", () => {
8 | it("renders the component in SSR contexts without erroring out while printing slot", async () => {
9 | const contents = await renderToString(Waypoint, {
10 | slots: { default: "Test" },
11 | });
12 | expect(contents).toBe('Test
');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | require("@rushstack/eslint-patch/modern-module-resolution");
3 |
4 | module.exports = {
5 | root: true,
6 | extends: [
7 | "plugin:vue/vue3-essential",
8 | "eslint:recommended",
9 | "@vue/eslint-config-typescript/recommended",
10 | "@vue/eslint-config-prettier",
11 | ],
12 | parserOptions: {
13 | ecmaVersion: "latest",
14 | },
15 | rules: {
16 | "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
17 | "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/vite.config.lib.ts:
--------------------------------------------------------------------------------
1 | import { resolve } from "path";
2 | import { defineConfig, mergeConfig } from "vite";
3 | import baseConfig from "./vite.config";
4 |
5 | const libConfig = defineConfig({
6 | build: {
7 | lib: {
8 | entry: resolve(__dirname, "src/components/Waypoint/index.ts"),
9 | name: "VueWaypoint",
10 | fileName: "vue-waypoint",
11 | },
12 | rollupOptions: {
13 | external: ["vue"],
14 | output: {
15 | globals: {
16 | vue: "Vue",
17 | },
18 | },
19 | },
20 | },
21 | });
22 |
23 | export default mergeConfig(baseConfig, libConfig);
24 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*"
7 |
8 | jobs:
9 | cancel:
10 | name: Cancel Previous Runs
11 | runs-on: ubuntu-latest
12 | timeout-minutes: 3
13 | steps:
14 | - uses: styfle/cancel-workflow-action@0.4.0
15 | with:
16 | access_token: ${{ github.token }}
17 |
18 | build:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: actions/checkout@v2
22 | # Setup .npmrc file to publish to npm
23 | - uses: actions/setup-node@v1
24 | with:
25 | node-version: "16.x.x"
26 | registry-url: "https://registry.npmjs.org"
27 | - run: npm ci
28 | - run: npm run build-only-lib
29 | - run: npm publish
30 | env:
31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.github/workflows/pr.yml:
--------------------------------------------------------------------------------
1 | name: Pull Request
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | cancel:
10 | name: Cancel Previous Runs
11 | runs-on: ubuntu-latest
12 | timeout-minutes: 3
13 | steps:
14 | - uses: styfle/cancel-workflow-action@0.4.0
15 | with:
16 | access_token: ${{ github.token }}
17 |
18 | build:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: actions/checkout@v2
22 | # Setup .npmrc file to publish to npm
23 | - uses: actions/setup-node@v1
24 | with:
25 | node-version: "16.x.x"
26 | registry-url: "https://registry.npmjs.org"
27 | - run: npm ci
28 | - run: npm run type-check
29 | - run: npm run lint
30 | - run: npm run test:unit
31 | - run: npm run build-only-lib
32 |
--------------------------------------------------------------------------------
/src/__tests__/example.spec.ts:
--------------------------------------------------------------------------------
1 | import { beforeEach, describe, it, expect, vi } from "vitest";
2 | import { shallowMount } from "@vue/test-utils";
3 | import { Waypoint } from "@/components/Waypoint";
4 |
5 | beforeEach(() => {
6 | const noop = () => null;
7 | const mockIntersectionObserver = vi.fn();
8 | mockIntersectionObserver.mockReturnValue({ observe: noop, unobserve: noop });
9 | window.IntersectionObserver = mockIntersectionObserver;
10 | });
11 |
12 | describe("Template output", () => {
13 | it("renders the component with div tag", () => {
14 | const wrapper = shallowMount(Waypoint);
15 | expect(wrapper.html()).toMatch(``);
16 | });
17 |
18 | it("renders the component with passed tag", () => {
19 | const wrapper = shallowMount(Waypoint, { props: { tag: "span" } });
20 | expect(wrapper.html()).toMatch(``);
21 | });
22 |
23 | it("renders the component with passed tag", () => {
24 | const wrapper = shallowMount(Waypoint, { props: { tag: "p" } });
25 | expect(wrapper.html()).toMatch(``);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Marco 'Gatto' Boffo
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.
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 | going-{{ going.toLowerCase() }}
9 | direction-{{ direction.toLowerCase() }}
10 |
11 |
12 |
13 |
35 |
36 |
63 |
--------------------------------------------------------------------------------
/src/components/Waypoint/observer.ts:
--------------------------------------------------------------------------------
1 | import { ref } from "vue";
2 |
3 | export type WaypointState = {
4 | el: Element | undefined;
5 | going: Going | undefined;
6 | direction: Direction | undefined;
7 | };
8 |
9 | export enum Going {
10 | In = "IN",
11 | Out = "OUT",
12 | }
13 |
14 | export enum Direction {
15 | Up = "UP",
16 | Down = "DOWN",
17 | Left = "LEFT",
18 | Right = "RIGHT",
19 | }
20 |
21 | const toGoing = (isIntersecting: boolean): Going => {
22 | return isIntersecting ? Going.In : Going.Out;
23 | };
24 |
25 | const toDirection = (
26 | rect: DOMRectReadOnly,
27 | oldRect: DOMRectReadOnly
28 | ): Direction | undefined => {
29 | if (rect.top < oldRect.top) return Direction.Up;
30 | if (rect.left > oldRect.left) return Direction.Right;
31 | if (rect.top > oldRect.top) return Direction.Down;
32 | if (rect.left < oldRect.left) return Direction.Left;
33 | };
34 |
35 | type Callback = (state: WaypointState) => void;
36 |
37 | export const createObserver = (options?: IntersectionObserverInit) => {
38 | return (callback: Callback) => {
39 | const boundingClientRect = ref();
40 |
41 | return new window.IntersectionObserver(([entry]) => {
42 | // this should never happen
43 | if (typeof entry === "undefined") {
44 | console.error("[vue-waypoint]", "observed element is undefined");
45 | return;
46 | }
47 |
48 | // set the default bounding client
49 | // this happens only on the first call
50 | boundingClientRect.value ??= entry.boundingClientRect;
51 |
52 | // create a new state and notify
53 | callback({
54 | el: entry.target,
55 | going: toGoing(entry.isIntersecting),
56 | direction: toDirection(
57 | entry.boundingClientRect,
58 | boundingClientRect.value
59 | ),
60 | });
61 |
62 | // save the rect for next matching
63 | boundingClientRect.value = entry.boundingClientRect;
64 | }, options);
65 | };
66 | };
67 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-waypoint",
3 | "version": "4.3.0",
4 | "license": "MIT",
5 | "private": false,
6 | "main": "dist/vue-waypoint.umd.js",
7 | "module": "dist/vue-waypoint.mjs",
8 | "typings": "src/components/Waypoint/index.ts",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/scaccogatto/vue-waypoint"
12 | },
13 | "scripts": {
14 | "dev": "vite",
15 | "build": "run-p type-check build-only",
16 | "test:unit": "vitest --environment jsdom",
17 | "build-only-lib": "vite --config vite.config.lib.ts build",
18 | "build-only": "vite build",
19 | "type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
20 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --ignore-path .gitignore",
21 | "release": "standard-version",
22 | "prepare": "husky install"
23 | },
24 | "devDependencies": {
25 | "@commitlint/cli": "^17.0.3",
26 | "@commitlint/config-conventional": "^17.0.3",
27 | "@rushstack/eslint-patch": "^1.1.4",
28 | "@types/jsdom": "^20.0.0",
29 | "@types/node": "^16.11.47",
30 | "@vitejs/plugin-vue": "^3.0.1",
31 | "@vue/eslint-config-prettier": "^7.0.0",
32 | "@vue/eslint-config-typescript": "^11.0.0",
33 | "@vue/test-utils": "^2.3.0",
34 | "@vue/tsconfig": "^0.1.3",
35 | "eslint": "^8.21.0",
36 | "eslint-plugin-vue": "^9.3.0",
37 | "husky": "^8.0.1",
38 | "jsdom": "^20.0.0",
39 | "lint-staged": "^13.0.3",
40 | "npm-run-all": "^4.1.5",
41 | "prettier": "^2.7.1",
42 | "standard-version": "^9.5.0",
43 | "typescript": "~4.7.4",
44 | "vite": "^3.0.4",
45 | "vitest": "^0.21.0",
46 | "vue": "^3.2.37",
47 | "vue-tsc": "^0.39.5"
48 | },
49 | "peerDependencies": {
50 | "vue": "^3.0.0"
51 | },
52 | "lint-staged": {
53 | "*": "prettier --write --ignore-path .gitignore --ignore-unknown",
54 | "*.{vue,js,jsx,cjs,mjs,ts,tsx,cts,mts}": "eslint --fix --ignore-path .gitignore"
55 | },
56 | "commitlint": {
57 | "extends": [
58 | "@commitlint/config-conventional"
59 | ]
60 | },
61 | "volta": {
62 | "node": "16.20.2"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/Waypoint/component.ts:
--------------------------------------------------------------------------------
1 | import {
2 | computed,
3 | defineComponent,
4 | h,
5 | onBeforeUnmount,
6 | onMounted,
7 | ref,
8 | watch,
9 | type PropType,
10 | } from "vue";
11 | import { createObserver, type WaypointState } from "./observer";
12 |
13 | export default defineComponent({
14 | // eslint-disable-next-line vue/multi-word-component-names
15 | name: "Waypoint",
16 | props: {
17 | active: {
18 | type: Boolean,
19 | default: () => true,
20 | },
21 | options: {
22 | type: Object as PropType,
23 | default: () => ({}),
24 | },
25 | tag: {
26 | type: String,
27 | default: () => "div",
28 | },
29 | disableCssHelpers: {
30 | type: Boolean,
31 | default: () => false,
32 | },
33 | },
34 | setup(props, context) {
35 | // check for mounted status
36 | const mounted = ref(false);
37 |
38 | // element DOM reference
39 | const element = ref(null);
40 |
41 | // activatable conditions
42 | const activatable = computed(
43 | () => mounted.value && props.active && element.value !== null
44 | );
45 |
46 | const waypointState = ref();
47 | const updateWaypointState = (newState: WaypointState) =>
48 | (waypointState.value = newState);
49 |
50 | const observer = ref();
51 | watch(activatable, () => {
52 | // cannot observer or unobserve if the element is null
53 | if (element.value === null) return;
54 |
55 | if (activatable.value && observer.value)
56 | return observer.value.observe(element.value);
57 | else return observer.value?.unobserve(element.value);
58 | });
59 |
60 | watch(waypointState, () => {
61 | if (typeof waypointState.value === "undefined") return;
62 | context.emit("change", waypointState.value);
63 | });
64 |
65 | // bind and unbind IntersectionObserver as needed
66 | onMounted(() => {
67 | mounted.value = true;
68 | observer.value = createObserver(props.options)(updateWaypointState);
69 | });
70 |
71 | onBeforeUnmount(() => (mounted.value = false));
72 |
73 | const cssHelpers = computed(() => {
74 | const { going, direction: dir } = waypointState.value ?? {};
75 | const goingClass = going && `going-${going.toLowerCase()}`;
76 | const directionClass = dir && `direction-${dir.toLowerCase()}`;
77 | return ["waypoint", goingClass, directionClass];
78 | });
79 |
80 | return () => {
81 | const rawProps = props.disableCssHelpers
82 | ? { ref: element }
83 | : { ref: element, class: cssHelpers.value };
84 |
85 | return h(
86 | props.tag,
87 | rawProps,
88 | context.slots.default?.(waypointState.value ?? {})
89 | );
90 | };
91 | },
92 | });
93 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ## [4.3.0](https://github.com/scaccogatto/vue-waypoint/compare/v4.2.5...v4.3.0) (2023-08-28)
6 |
7 | ### Features
8 |
9 | - added support for SSR environments ([#77](https://github.com/scaccogatto/vue-waypoint/issues/77)) ([1ae537b](https://github.com/scaccogatto/vue-waypoint/commit/1ae537bbfa86d6096b6c45e47d65a704ba833f4a))
10 |
11 | ### [4.2.5](https://github.com/scaccogatto/vue-waypoint/compare/v4.2.4...v4.2.5) (2022-09-12)
12 |
13 | ### Bug Fixes
14 |
15 | - typing export ([612fd4c](https://github.com/scaccogatto/vue-waypoint/commit/612fd4c8e4c6efdbdc42a06a3c1c5a47ad7a0b49))
16 |
17 | ### [4.2.4](https://github.com/scaccogatto/vue-waypoint/compare/v4.2.3...v4.2.4) (2022-08-15)
18 |
19 | ### [4.2.3](https://github.com/scaccogatto/vue-waypoint/compare/v4.2.2...v4.2.3) (2022-08-13)
20 |
21 | ### Bug Fixes
22 |
23 | - node version on build ([f5944df](https://github.com/scaccogatto/vue-waypoint/commit/f5944df31e90ac6ea19a6474aa183ffc7f834814))
24 |
25 | ### [4.2.2](https://github.com/scaccogatto/vue-waypoint/compare/v4.2.1...v4.2.2) (2022-08-13)
26 |
27 | ### Bug Fixes
28 |
29 | - intermediate states cleanup ([2766be8](https://github.com/scaccogatto/vue-waypoint/commit/2766be8d583ac546e81576e11add97879e4db0c1))
30 |
31 | ### [4.2.1](https://github.com/scaccogatto/vue-waypoint/compare/v4.2.0...v4.2.1) (2021-09-07)
32 |
33 | ## [4.2.0](https://github.com/scaccogatto/vue-waypoint/compare/v4.1.1...v4.2.0) (2021-08-17)
34 |
35 | ### Features
36 |
37 | - disableCssHelpers ([3ff1e45](https://github.com/scaccogatto/vue-waypoint/commit/3ff1e458a3d4e519c031a0ce72e21454bff51673))
38 |
39 | ### [4.1.1](https://github.com/scaccogatto/vue-waypoint/compare/v4.1.0...v4.1.1) (2021-08-10)
40 |
41 | ## [4.1.0](https://github.com/scaccogatto/vue-waypoint/compare/v4.0.0...v4.1.0) (2021-07-09)
42 |
43 | ### Features
44 |
45 | - adds el as example ([09cdc71](https://github.com/scaccogatto/vue-waypoint/commit/09cdc716873ac0711a2078f248aa2749d31e2629))
46 |
47 | ## [4.0.0](https://github.com/scaccogatto/vue-waypoint/compare/v3.5.0...v4.0.0) (2021-06-21)
48 |
49 | ### ⚠ BREAKING CHANGES
50 |
51 | - vue2 support is over
52 |
53 | - feat: waypoint bind/unbind
54 |
55 | - feat: tag selection
56 |
57 | - feat: export ts types
58 |
59 | - fix: lib target
60 |
61 | - chore: remove roadmap since it is done
62 |
63 | - build: standard version
64 |
65 | - docs: generally improved and vue2 refs
66 |
67 | - build: release on tag push
68 |
69 | - chore: license and private to false
70 |
71 | - fix: aligned version
72 |
73 | - docs: dev steps
74 |
75 | - docs: move CSS helpers on top
76 |
77 | CSS helpers will probably be the most used feature, so we move it to the top
78 |
79 | - docs: CSS features list
80 |
81 | - fix: remove impossible path
82 |
83 | - feat: final decorations
84 |
85 | ### Features
86 |
87 | - vue3 support ([#46](https://github.com/scaccogatto/vue-waypoint/issues/46)) ([2fe79ee](https://github.com/scaccogatto/vue-waypoint/commit/2fe79ee0e1c30bc314b5c66fc3eadbdbca536d4f))
88 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VueWaypoint
2 |
3 | > trigger functions and events based on the element position on the screen
4 |
5 | 
6 |
7 | ## Demo
8 |
9 | [Simple demo page](https://vue-waypoint.netlify.app/)
10 |
11 | Open your browser console and see what's going on while scrolling up and down
12 |
13 | ## Features
14 |
15 | - [x] Vue 3
16 | - [x] No dependencies
17 | - [x] Flexible
18 | - [x] Typescript
19 | - [x] Battle tested
20 | - [x] Customizable
21 | - [x] Solid project (5+ years)
22 | - [x] Supports slots
23 |
24 | ## Getting started
25 |
26 | ### npm
27 |
28 | ```bash
29 | npm i vue-waypoint
30 | ```
31 |
32 | ### Vue component
33 |
34 | ```html
35 |
36 |
37 |
38 |
39 |
40 | ```
41 |
42 | ```html
43 |
71 | ```
72 |
73 | ## Props
74 |
75 | ### `active`
76 |
77 | - [x] Can use a reactive variable
78 | - [x] Can set `true`/`false` dynamically
79 |
80 | Usage:
81 |
82 | - Enable the waypoint: ``
83 | - Disable the waypoint: ``
84 |
85 | ### `options`
86 |
87 | - [x] Useful for inner div detection
88 | - [x] Trigger `change` event a portion of the element is completely on screen
89 | - [x] Is an [official IntersectionObserverInit implementation](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver)
90 |
91 | Usage:
92 |
93 | - Set a custom `IntersectionObserver` options: ``
94 | - Read what you can do with `options`: [IntersectionObserverInit docs](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver)
95 |
96 | Options example:
97 |
98 | ```js
99 | const options: IntersectionObserverInit = {
100 | root: document,
101 | rootMargin: "0px 0px 0px 0px",
102 | threshold: [0.25, 0.75],
103 | };
104 | ```
105 |
106 | ### `tag`
107 |
108 | - [x] Set your preferred tag for the element
109 | - [x] Defaults to `div`
110 |
111 | - Waypoint as div: ` --> renders --> `
112 | - Waypoint as span: ` --> renders --> `
113 | - Waypoint as p: ` --> renders --> `
114 |
115 | ### `disableCssHelpers`
116 |
117 | - [x] Disable automatic CSS classes on the Waypoint component
118 | - [x] Defaults to `false`
119 |
120 | Usage:
121 |
122 | - Enable helpers (default): ``
123 | - Disable helpers: ``
124 |
125 | DOM result:
126 |
127 | - With CSS helpers: ` --> renders --> `
128 | - Without CSS helpers: ` --> renders --> `
129 |
130 | ## CSS helpers
131 |
132 | - [x] Zero configuration needed
133 | - [x] Useful for simple CSS animations
134 |
135 | The component comes with three classes:
136 |
137 | - `waypoint`: set when the waypoint is ready
138 | - `going-in`, `going-out`: dynamically changed when the waypoint comes in and out
139 | - `direction-up`, `direction-down`, `direction-left`, `direction-right`: dynamically changed when the direction changes
140 |
141 | Examples:
142 |
143 | - `` - the element is visible and came from bottom and is going top (natural scroll)
144 | - `` - the element is visible and came from top and is going up (reverse natural scroll)
145 | - `` - the element is not visible and came from bottom and is going top
146 | - `` - the element is not visible and came from top and is going up
147 |
148 | ## Events
149 |
150 | ### `change`
151 |
152 | Emitted every time the waypoint detects a change.
153 |
154 | ```html
155 |
156 |
157 |
158 | ```
159 |
160 | ```js
161 | function onChange(waypointState) {
162 | /* ... */
163 | }
164 | ```
165 |
166 | ```js
167 | interface WaypointState {
168 | el: Element;
169 | going: "IN" | "OUT";
170 | direction: "UP" | "DOWN" | "LEFT" | "RIGHT";
171 | }
172 | ```
173 |
174 | ## Development
175 |
176 | 1. Fork the repository
177 | 2. Run the project (`npm i && npm run dev`)
178 | 3. Follow [Conventional Commits spec](https://www.conventionalcommits.org/en/v1.0.0/) for your commits
179 | 4. Open a pull request
180 |
181 | ## LEGACY: Vue2 and Nuxt version
182 |
183 | [vue-waypoint for Vue2 repository](https://github.com/scaccogatto/vue-waypoint/tree/vue2)
184 |
--------------------------------------------------------------------------------