{"_id":"@dexq/svelte-scrollspy","name":"@dexq/svelte-scrollspy","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.1":{"name":"@dexq/svelte-scrollspy","description":"Svelte store to tracks the scroll position of elements registered using a provided Svelte action","version":"0.0.1","author":{"name":"Dexter Lui","email":"dexlui@pm.me"},"license":"MIT","keywords":["svelte","scrollspy","intersection observer"],"repository":{"type":"git","url":"git+https://github.com/dexterklui/svelte-scrollspy.git"},"bugs":{"url":"https://github.com/dexterklui/svelte-scrollspy/issues","email":"dexlui@pm.me"},"scripts":{"dev":"vite dev","build":"vite build && npm run package","preview":"vite preview","package":"svelte-kit sync && svelte-package && publint","prepublishOnly":"npm run package","check":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json","check:watch":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch","test":"vitest","lint":"prettier --check . && eslint .","format":"prettier --write ."},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"peerDependencies":{"svelte":"^4.0.0"},"devDependencies":{"@sveltejs/adapter-auto":"^2.0.0","@sveltejs/kit":"^1.27.4","@sveltejs/package":"^2.0.0","@typescript-eslint/eslint-plugin":"^6.0.0","@typescript-eslint/parser":"^6.0.0","eslint":"^8.28.0","eslint-config-prettier":"^9.0.0","eslint-plugin-svelte":"^2.30.0","prettier":"^3.0.0","prettier-plugin-svelte":"^3.0.0","publint":"^0.1.9","svelte":"^4.2.7","svelte-check":"^3.6.0","tslib":"^2.4.1","typescript":"^5.0.0","vite":"^4.4.2","vitest":"^0.34.0"},"svelte":"./dist/index.js","types":"./dist/index.d.ts","type":"module","_id":"@dexq/svelte-scrollspy@0.0.1","gitHead":"0cae3ba3b47d6c30ff91103469059c4d06efcc28","homepage":"https://github.com/dexterklui/svelte-scrollspy#readme","_nodeVersion":"21.2.0","_npmVersion":"10.2.4","dist":{"integrity":"sha512-XPS6Y+Xnn8oEGKCeutk76bGjG6OgbiTVaAbc/firgxVuHjAARVLXS06nutnlM47Kkpmtp3idcvTF+SqscbJ8wA==","shasum":"75774ee1ada98a170346a403af98ab555ada34cf","tarball":"https://registry.npmjs.org/@dexq/svelte-scrollspy/-/svelte-scrollspy-0.0.1.tgz","fileCount":5,"unpackedSize":18372,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDoZJ0yWmHBdP6on5qgg3q/byCT+i2ZPbg3Vtw1ItWCUQIhANw9zgkt9pbbWDnUzU0jMdhec8o65dxS44GbO8hITgx+"}]},"_npmUser":{"name":"dexlui","email":"dexlui+npm@pm.me"},"directories":{},"maintainers":[{"name":"dexlui","email":"dexlui+npm@pm.me"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/svelte-scrollspy_0.0.1_1701008644280_0.2984443574103155"},"_hasShrinkwrap":false}},"time":{"created":"2023-11-26T14:24:04.150Z","0.0.1":"2023-11-26T14:24:04.525Z","modified":"2023-11-26T14:24:04.809Z"},"maintainers":[{"name":"dexlui","email":"dexlui+npm@pm.me"}],"description":"Svelte store to tracks the scroll position of elements registered using a provided Svelte action","homepage":"https://github.com/dexterklui/svelte-scrollspy#readme","keywords":["svelte","scrollspy","intersection observer"],"repository":{"type":"git","url":"git+https://github.com/dexterklui/svelte-scrollspy.git"},"author":{"name":"Dexter Lui","email":"dexlui@pm.me"},"bugs":{"url":"https://github.com/dexterklui/svelte-scrollspy/issues","email":"dexlui@pm.me"},"license":"MIT","readme":"# Svelte ScrollSpy\n\nSvelte ScrollSpy is a [Svelte store](https://svelte.dev/docs/svelte-store) that\ntracks the intersecting state of a set of elements. The store provides a\n[Svelte action](https://svelte.dev/docs/svelte-action) that allows you to easily\nregister any element for tracking.\n\n# Installation\n\n## NPM\n\n```\nnpm i -D svelte-scrollspy\n```\n\n## Yarn\n\n```\nyarn add -D svelte-scrollspy\n```\n\n## bun\n\n```\nbun i -d svelte-scrollspy\n```\n\n# Why Svelte ScrollSpy?\n\nAlthough there are many intersection observer libraries out there, this library\nleverages the power of Svelte actions to provide a simple and intuitive API.\nHere are some of the benefits of using this library in a Svelte project.\n\n- Have a cleaner DOM without needing to add wrapper elements.\n- Easily register any elements, even nested elements, for tracking.\n- No need to worry about cleaning up when elements being tracked are removed\n  from the DOM. Callbacks in Svelte action automatically do that.\n\n# API\n\nThe object stored in the ScrollSpy store has the following properties:\n\n| Property           | Description                                                                                                                                   |\n| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |\n| `amount`           | The number of targets being spied on.                                                                                                         |\n| `targets`          | The ordered set of all targets being spied on, in the order of them being added as a target.                                                  |\n| `activeTargets`    | The ordered set of all intersecting targets, in the order of entering intersection.                                                           |\n| `activeTarget`     | The target that became active most recently and is still active. I.e. the last item in `activeTargets`.                                       |\n| `lastActiveTarget` | The target that became active most recently. It may or may not be active now. If this target is no longer being spied on, this value is null. |\n| `activeId`         | The id of `activeTarget`.                                                                                                                     |\n| `lastActiveId`     | The id of `lastActiveTarget`.                                                                                                                 |\n| `isActive`         | A function that checks if an element is active. Returns `null` if the given element is not a target being spied on.                           |\n\nThe ScrollSpy store also has the following methods besides the `subscribe`\nmethod of a Svelte store.\n\n| Method     | Description                                                                                                                                                                                                                 |\n| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `spy`      | A svelte action to add an element to the target list and start spying on it.                                                                                                                                                |\n| `unspy`    | Stop spying on a target and remove it from the target list. Accepts either the element itself or the id as the argument. This method is automatically called on all registered targets after they are removed from the DOM. |\n| `unspyAll` | Stop spying on all existing targets and remove them from the target list.                                                                                                                                                   |\n\n# Usage\n\n## Basic\n\nTo use ScrollSpy to track the current section in view on the page. First create\na scroll spy store with the imported `createScrollSpy` function. You can pass in\nan IntersectionObserverInit object to configure the IntersectionObserver used\nfor tracking.\n\nIn the example, we set the `rootMargin` to `-50% 0px` so that a section starts\nintersecting when it touches the vertical center of the viewport.\n\n```javascript\nimport createScrollSpy from \"svelte-scrollspy\";\nexport const scrollSpy = createScrollSpy({ rootMargin: \"-50% 0px\" });\n```\n\nThen in any component file you can import the created store and use its `spy`\nmethod as a Svelte action to start spying on that element.\n\n```svelte\n<script>\n  import scrollSpy from \"$lib/stores/scroll-spy\";\n</script>\n\n<section id=\"my-section\" use:scrollSpy.spy><!-- ... --></section>\n<!-- Other sections... -->\n```\n\nElement id is not required for spying. We gave an id to the element here only\nfor scrolling with URL hash. Now, it is easy to make a navbar that highlights\nthe current section.\n\n```svelte\n<script>\n  import scrollSpy from \"$lib/stores/scroll-spy\";\n  import kebabCaseToCapWords from \"$lib/utils.js\";\n</script>\n\n<nav>\n  <ul>\n    {#each $scrollSpy.targets as section (section)}\n      <li class={$scrollSpy.lastActiveTarget === section ? \"active\" : \"\"}>\n        <a href={\"#\" + section.id}>{kebabCaseToCapWords(section.id)}</a>\n      </li>\n    {/each}\n  </ul>\n</nav>\n\n<style>\n  /* Styling... */\n</style>\n```\n\n## Restricting What Can Be Spied On\n\nYou can augment Scroll Spy with additional properties and methods. For example,\nwe can enforce that all elements being spied on must have an ID, and we can\nassign an arbitrary label to each element when they are registered.\n\nTo do this, we create our custom Svelte store by extending the functionality of\nScroll Spy.\n\n```javascript\nimport createScrollSpy from 'svelte-scrollspy';\nimport type { ActionReturn } from 'svelte/action';\n\nexport const sectionSpy = (() => {\n  // use spy store's functionality\n  const spy = createScrollSpy({ rootMargin: '-50% 0px' });\n\n  return {\n    ...spy,\n\n    // Overwrite spy() to add restriction on targets and apply custom attribute\n\n    /**\n     * A svelte action to register an element as a section to spy on. The\n     * element must have an id.\n     *\n     * @param [label] - an arbitrary label for the section. This action assigns\n     *   the label value to the element's `data-section-label` attribute. If no\n     *   label is given, the element's id is transformed into a capitalized\n     *   string and used as the label.\n     */\n    spy(\n      target: string | Element,\n      label?: string,\n    ): ActionReturn<string, { id: string }> {\n      const elem =\n        target instanceof Element ? target : document.getElementById(target);\n\n      if (!elem || !elem.id) return {};\n\n      label ??= elem.id // kebab-case to Capitalized Words\n        .replace(/-./g, (m) => \" \" + m[1].toUpperCase())\n        .replace(/^(.)/, (m) => m.toUpperCase());\n\n      const { destroy } = spy.spy(elem);\n      if (destroy) elem.setAttribute(\"data-section-label\", label);\n      return { destroy };\n    },\n  };\n})();\n```\n\nWe can then use our custom store to assign labels with Svelte action syntax.\n\n```svelte\n<section id=\"my-section\" use:sectionSpy.spy={\"My Label\"}><!-- ... --></section>\n<!-- Other sections... -->\n<footer>You're at the section: {$sectionSpy.activeLabel}</footer>\n```\n\n## Adding Custom Properties to the Store\n\nYou can even add custom properties to the store.\n\n```typescript\nexport const sectionSpy = (() => {\n  const spy = createSpy({ rootMargin: \"-50% 0px\" });\n\n  // extend spy store's properties\n  interface SectionSpy extends Spy {\n    /** The label of the active target (attribute: \"data-section-label\") */\n    activeLabel: string | null;\n\n    /**\n     * The label of the last active section (attribute: \"data-section-label\")\n     */\n    lastActiveLabel: string | null;\n  }\n\n  function getSectionSpy(): SectionSpy {\n    return {\n      ...get(spy),\n      get activeLabel() {\n        return this.activeTarget?.getAttribute(\"data-section-label\") ?? null;\n      },\n      get lastActiveLabel() {\n        return (\n          this.lastActiveTarget?.getAttribute(\"data-section-label\") ?? null\n        );\n      },\n    };\n  }\n\n  // Create our own custom store\n  const { subscribe, set } = writable<SectionSpy>(getSectionSpy());\n  // Update our custom store whenever the spy store updates\n  spy.subscribe(() => set(getSectionSpy()));\n\n  return {\n    ...spy,\n    subscribe,\n\n    // Implement our own spy() method to add restriction on spied targets and\n    // add custom attribute to the targets\n    spy(\n      target: string | Element,\n      label?: string,\n    ): ActionReturn<string, { id: string }> {\n      const elem =\n        target instanceof Element ? target : document.getElementById(target);\n\n      if (!elem || !elem.id) return {};\n\n      label ??= elem.id // kebab-case to Capitalized Words\n        .replace(/-./g, (m) => \" \" + m[1].toUpperCase())\n        .replace(/^(.)/, (m) => m.toUpperCase());\n\n      const { destroy } = spy.spy(elem);\n      if (destroy) elem.setAttribute(\"data-section-label\", label);\n      return { destroy };\n    },\n  };\n})();\n```\n\n# License\n\n[MIT](./LICENSE)\n","readmeFilename":"README.md"}