├── .all-contributorsrc
├── .changeset
├── README.md
└── config.json
├── .github
├── FUNDING.yml
└── workflows
│ ├── cr.yml
│ └── release.yml
├── .gitignore
├── .prettierrc
├── .vscode
└── settings.json
├── LICENSE.txt
├── README.md
├── docs
├── .gitignore
├── .npmrc
├── .prettierrc.cjs
├── .vscode
│ ├── extensions.json
│ ├── launch.json
│ └── settings.json
├── CHANGELOG.md
├── README.md
├── astro.config.ts
├── package.json
├── postcss.config.cjs
├── public
│ ├── banner.png
│ ├── favicon.png
│ ├── favicon.svg
│ ├── home
│ │ ├── metaframeworks-dark.webp
│ │ └── metaframeworks-light.webp
│ ├── logo-2x.png
│ ├── logo.svg
│ └── logos
│ │ ├── react.svg
│ │ ├── solid.svg
│ │ ├── svelte.svg
│ │ ├── vanilla.svg
│ │ └── vue.svg
├── src
│ ├── actions
│ │ ├── portal.ts
│ │ └── typingEffect.ts
│ ├── auto-imports.d.ts
│ ├── components
│ │ ├── Footer.svelte
│ │ ├── MetaThemeColor.svelte
│ │ ├── MobileNav.svelte
│ │ ├── PawCursor.svelte
│ │ ├── ThemeSwitcher.svelte
│ │ ├── ToC.svelte
│ │ ├── docs
│ │ │ └── Nav.svelte
│ │ ├── examples
│ │ │ └── KitchenSinkDemo.svelte
│ │ ├── home
│ │ │ ├── ExploreFrameworks.svelte
│ │ │ ├── HeroExample.svelte
│ │ │ ├── RingSVG.svelte
│ │ │ ├── ScrollDownIndicator.svelte
│ │ │ └── features
│ │ │ │ ├── bundle-sizes
│ │ │ │ └── BundleSizeFeature.svelte
│ │ │ │ ├── feature-box.scss
│ │ │ │ ├── feature-rich
│ │ │ │ └── FeatureRichFeature.svelte
│ │ │ │ ├── multiple-frameworks
│ │ │ │ ├── FrameworkPolygon.svelte
│ │ │ │ ├── FrameworkVertex.svelte
│ │ │ │ └── MultipleFrameworksFeature.svelte
│ │ │ │ └── ssr-friendly
│ │ │ │ └── SSRFriendlyFeature.svelte
│ │ └── options
│ │ │ ├── Options.astro
│ │ │ ├── OptionsCode.astro
│ │ │ ├── OptionsDemoBase.svelte
│ │ │ ├── OptionsExample.astro
│ │ │ └── OptionsExamples.svelte
│ ├── css
│ │ ├── breakpoints.scss
│ │ ├── globals.scss
│ │ ├── include-media.scss
│ │ └── themes.scss
│ ├── data
│ │ └── sizes.json
│ ├── documentation
│ │ ├── exported-types.mdx
│ │ ├── installation.mdx
│ │ └── options
│ │ │ ├── applyUserSelectHack
│ │ │ ├── +option.mdx
│ │ │ ├── NoUserSelect.example.svelte
│ │ │ ├── UserSelect.example.svelte
│ │ │ └── user-select.mixin.scss
│ │ │ ├── axis
│ │ │ ├── +option.mdx
│ │ │ ├── BothAxis.example.svelte
│ │ │ ├── NoneAxis.example.svelte
│ │ │ ├── XAxis.example.svelte
│ │ │ └── YAxis.example.svelte
│ │ │ ├── bounds
│ │ │ ├── +option.mdx
│ │ │ ├── BodyBounds.example.svelte
│ │ │ ├── CoordinateBounds.example.svelte
│ │ │ └── ParentBounds.example.svelte
│ │ │ ├── cancel
│ │ │ ├── +option.mdx
│ │ │ ├── MultipleCancelElement.example.svelte
│ │ │ ├── MultipleCancelSelector.example.svelte
│ │ │ ├── SingleCancelElement.example.svelte
│ │ │ ├── SingleCancelSelector.example.svelte
│ │ │ └── cancel-base.mixin.scss
│ │ │ ├── defaultClass
│ │ │ └── +option.mdx
│ │ │ ├── defaultClassDragged
│ │ │ └── +option.mdx
│ │ │ ├── defaultClassDragging
│ │ │ └── +option.mdx
│ │ │ ├── defaultPosition
│ │ │ ├── +option.mdx
│ │ │ └── DefaultPosition.example.svelte
│ │ │ ├── disabled
│ │ │ ├── +option.mdx
│ │ │ └── Disabled.example.svelte
│ │ │ ├── gpuAcceleration
│ │ │ ├── +option.mdx
│ │ │ ├── Acceleration.example.svelte
│ │ │ └── NoAcceleration.example.svelte
│ │ │ ├── grid
│ │ │ ├── +option.mdx
│ │ │ ├── InactiveGrid.example.svelte
│ │ │ ├── RectangleGrid.example.svelte
│ │ │ └── SquareGrid.example.svelte
│ │ │ ├── handle
│ │ │ ├── +option.mdx
│ │ │ ├── MultipleHandleElement.example.svelte
│ │ │ ├── MultipleHandleSelector.example.svelte
│ │ │ ├── SingleHandleElement.example.svelte
│ │ │ ├── SingleHandleSelector.example.svelte
│ │ │ └── handle-base.mixin.scss
│ │ │ ├── ignoreMultitouch
│ │ │ ├── +option.mdx
│ │ │ ├── IgnoredMultitouch.example.svelte
│ │ │ └── Multitouch.example.svelte
│ │ │ ├── legacyTranslate
│ │ │ ├── +option.mdx
│ │ │ ├── LegacyTranslateGPU.example.svelte
│ │ │ ├── LegacyTranslateNoGPU.example.svelte
│ │ │ ├── TranslateGPU.example.svelte
│ │ │ └── TranslateNoGPU.example.svelte
│ │ │ ├── onDrag
│ │ │ └── +option.mdx
│ │ │ ├── onDragEnd
│ │ │ └── +option.mdx
│ │ │ ├── onDragStart
│ │ │ └── +option.mdx
│ │ │ ├── position
│ │ │ ├── +option.mdx
│ │ │ ├── DisabledPosition.example.svelte
│ │ │ └── Position.example.svelte
│ │ │ ├── recomputeBounds
│ │ │ └── +option.mdx
│ │ │ ├── threshold
│ │ │ ├── +option.mdx
│ │ │ ├── Delay.example.svelte
│ │ │ └── Distance.example.svelte
│ │ │ └── transform
│ │ │ ├── +option.mdx
│ │ │ ├── ManualTransform.example.svelte
│ │ │ └── ReturnTransform.example.svelte
│ ├── env.d.ts
│ ├── helpers
│ │ ├── constants.ts
│ │ ├── framework-icons.ts
│ │ └── utils.ts
│ ├── layouts
│ │ ├── DocsLayout.astro
│ │ ├── Layout.astro
│ │ ├── MainDocsLayout.astro
│ │ └── ThemeWatcher.svelte
│ ├── pages
│ │ ├── docs
│ │ │ ├── index.astro
│ │ │ ├── migrating
│ │ │ │ └── svelte-drag.mdx
│ │ │ ├── react.mdx
│ │ │ ├── solid.mdx
│ │ │ ├── svelte.mdx
│ │ │ ├── vanilla.mdx
│ │ │ └── vue.mdx
│ │ └── index.astro
│ ├── state
│ │ ├── auto-destroy-effect-root.svelte.ts
│ │ ├── persisted.svelte.ts
│ │ └── user-preferences.svelte.ts
│ └── worklet
│ │ └── squircle.js
├── svelte.config.js
└── tsconfig.json
├── package.json
├── packages
├── config
│ └── index.ts
├── core
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ ├── tsconfig.json
│ └── tsup.config.ts
├── react
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── demo
│ │ ├── .gitignore
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ ├── tsconfig.json
│ └── tsup.config.ts
├── solid
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── demo
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── pnpm-lock.yaml
│ │ ├── src
│ │ │ ├── App.module.css
│ │ │ ├── App.tsx
│ │ │ ├── global.d.ts
│ │ │ └── index.tsx
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ ├── tsconfig.json
│ └── tsup.config.ts
├── svelte
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── demo
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── app.d.ts
│ │ │ ├── app.html
│ │ │ └── routes
│ │ │ │ ├── 2
│ │ │ │ └── +page.svelte
│ │ │ │ └── +page.svelte
│ │ ├── svelte.config.js
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ ├── tests
│ │ ├── CancelDraggable.spec.ts
│ │ ├── Draggable.spec.ts
│ │ ├── HandleDraggable.spec.ts
│ │ ├── components
│ │ │ ├── CancelDraggable.svelte
│ │ │ ├── Draggable.svelte
│ │ │ └── HandleDraggable.svelte
│ │ └── testHelpers.ts
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ └── vitest.config.ts
├── vanilla
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── demo-umd
│ │ ├── .gitignore
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── main.js
│ │ │ └── style.css
│ │ └── tsconfig.json
│ ├── demo
│ │ ├── .gitignore
│ │ ├── favicon.svg
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── main.ts
│ │ │ ├── style.css
│ │ │ └── vite-env.d.ts
│ │ └── tsconfig.json
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ ├── tsconfig.json
│ └── tsup.config.ts
└── vue
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── demo
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── src
│ │ ├── App.vue
│ │ ├── components
│ │ │ └── HelloWorld.vue
│ │ ├── env.d.ts
│ │ └── main.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
│ ├── package.json
│ ├── src
│ └── index.ts
│ ├── tsconfig.json
│ └── tsup.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── scripts
└── gather-sizes.ts
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "commitConvention": "angular",
8 | "contributors": [
9 | {
10 | "login": "PuruVJ",
11 | "name": "Puru Vijay",
12 | "avatar_url": "https://avatars.githubusercontent.com/u/47742487?v=4",
13 | "profile": "https://puruvj.dev",
14 | "contributions": [
15 | "infra",
16 | "code",
17 | "maintenance",
18 | "content",
19 | "doc",
20 | "financial",
21 | "research"
22 | ]
23 | },
24 | {
25 | "login": "bluwy",
26 | "name": "Bjorn Lu",
27 | "avatar_url": "https://avatars.githubusercontent.com/u/34116392?v=4",
28 | "profile": "https://bjornlu.com/",
29 | "contributions": [
30 | "code",
31 | "ideas"
32 | ]
33 | },
34 | {
35 | "login": "matrushka",
36 | "name": "Baris Gumustas",
37 | "avatar_url": "https://avatars.githubusercontent.com/u/53268?v=4",
38 | "profile": "https://github.com/matrushka",
39 | "contributions": [
40 | "code"
41 | ]
42 | },
43 | {
44 | "login": "sidharth-anand",
45 | "name": "Sidharth Anand",
46 | "avatar_url": "https://avatars.githubusercontent.com/u/55060749?v=4",
47 | "profile": "https://github.com/sidharth-anand",
48 | "contributions": [
49 | "code"
50 | ]
51 | },
52 | {
53 | "login": "Tropix126",
54 | "name": "Tropical",
55 | "avatar_url": "https://avatars.githubusercontent.com/u/42101043?v=4",
56 | "profile": "https://github.com/Tropix126",
57 | "contributions": [
58 | "doc"
59 | ]
60 | },
61 | {
62 | "login": "AphLute",
63 | "name": "AphLute",
64 | "avatar_url": "https://avatars.githubusercontent.com/u/80430144?v=4",
65 | "profile": "https://earth.suncapped.com/",
66 | "contributions": [
67 | "code"
68 | ]
69 | },
70 | {
71 | "login": "tascodes",
72 | "name": "Tas",
73 | "avatar_url": "https://avatars.githubusercontent.com/u/32209335?v=4",
74 | "profile": "https://github.com/tascodes",
75 | "contributions": [
76 | "infra",
77 | "code",
78 | "test"
79 | ]
80 | }
81 | ],
82 | "contributorsPerLine": 7,
83 | "skipCi": true,
84 | "repoType": "github",
85 | "repoHost": "https://github.com",
86 | "projectName": "neodrag",
87 | "projectOwner": "PuruVJ"
88 | }
89 |
--------------------------------------------------------------------------------
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json",
3 | "changelog": ["@changesets/changelog-github", { "repo": "PuruVJ/neodrag" }],
4 | "commit": false,
5 | "linked": [],
6 | "access": "public",
7 | "baseBranch": "main",
8 | "updateInternalDependencies": "patch",
9 | "ignore": []
10 | }
11 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 | github: PuruVJ
3 |
--------------------------------------------------------------------------------
/.github/workflows/cr.yml:
--------------------------------------------------------------------------------
1 | name: Continuous Releases
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | test:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - uses: actions/checkout@v4
11 | - uses: actions/setup-node@v4
12 | with:
13 | node-version: 20
14 |
15 | - uses: pnpm/action-setup@v4
16 | with:
17 | version: 9
18 | run_install: true
19 |
20 | - name: Compile
21 | run: pnpm compile
22 |
23 | - name: Release
24 | run: pnpm dlx pkg-pr-new publish './packages/*'
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Changesets
2 | on:
3 | push:
4 | branches:
5 | - main
6 | - '2.0'
7 | env:
8 | CI: true
9 | PNPM_CACHE_FOLDER: .pnpm-store
10 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
11 | jobs:
12 | version:
13 | # TODO: Change this later if repo changes
14 | if: github.repository == 'PuruVJ/neodrag'
15 | timeout-minutes: 15
16 | runs-on: ubuntu-latest
17 | steps:
18 | - name: Checkout Repo
19 | uses: actions/checkout@v3
20 | with:
21 | # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
22 | fetch-depth: 0
23 | - name: Setup Node.js
24 | uses: actions/setup-node@v4
25 | with:
26 | node-version: 22.x
27 |
28 | - uses: pnpm/action-setup@v4
29 | name: Install pnpm
30 | id: pnpm-install
31 | with:
32 | version: 9
33 | run_install: true
34 |
35 | - name: Get pnpm store directory
36 | id: pnpm-cache
37 | shell: bash
38 | run: |
39 | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
40 |
41 | - uses: actions/cache@v4
42 | name: Setup pnpm cache
43 | with:
44 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
45 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
46 | restore-keys: |
47 | ${{ runner.os }}-pnpm-store-
48 |
49 | - name: Build all packages
50 | run: pnpm -r compile
51 |
52 | - name: Create Release Pull Request or Publish to npm
53 | uses: changesets/action@v1
54 | with:
55 | version: pnpm ci:version
56 | commit: 'chore: update versions'
57 | title: 'chore: update versions'
58 | publish: pnpm ci:release
59 | env:
60 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
61 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
62 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 | .DS_Store
4 | .env
5 | .pnpm-store/
6 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "printWidth": 100,
4 | "useTabs": true
5 | }
6 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules/typescript/lib"
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021-2022 Puru Vijay
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.
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # build output
2 | dist/
3 | .output/
4 |
5 | # dependencies
6 | node_modules/
7 |
8 | # logs
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | pnpm-debug.log*
13 |
14 |
15 | # environment variables
16 | .env
17 | .env.production
18 |
19 | # macOS-specific files
20 | .DS_Store
21 |
22 | .astro
--------------------------------------------------------------------------------
/docs/.npmrc:
--------------------------------------------------------------------------------
1 | # Expose Astro dependencies for `pnpm` users
2 |
--------------------------------------------------------------------------------
/docs/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | useTabs: true,
3 | semi: true,
4 | printWidth: 100,
5 | singleQuote: true,
6 |
7 | plugins: [require.resolve('prettier-plugin-astro'), require.resolve('prettier-plugin-svelte')],
8 | overrides: [
9 | {
10 | files: '*.astro',
11 | options: {
12 | parser: 'astro',
13 | },
14 | },
15 | {
16 | files: ['*.mdx', '*.md'],
17 | options: {
18 | useTabs: false,
19 | },
20 | },
21 | ],
22 | };
23 |
--------------------------------------------------------------------------------
/docs/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["astro-build.astro-vscode"],
3 | "unwantedRecommendations": []
4 | }
5 |
--------------------------------------------------------------------------------
/docs/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "command": "./node_modules/.bin/astro dev",
6 | "name": "Development server",
7 | "request": "launch",
8 | "type": "node-terminal"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/docs/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cssvar.files": [
3 | "./node_modules/open-props/open-props.min.css",
4 | // if you have an alternative path to where your styles are located
5 | // you can add it in this array of files
6 | "../src/css/globals.scss",
7 | "../src/css/themes.scss"
8 | ],
9 |
10 | // Do not ignore node_modules css files, which is ignored by default
11 | "cssvar.ignore": [],
12 |
13 | // add support for autocomplete in JS or JS like files
14 | "cssvar.extensions": ["css", "jsx", "tsx", "astro", "svelte", "vue"]
15 | }
16 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to [Astro](https://astro.build)
2 |
3 | [](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics)
4 |
5 | > 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
6 |
7 | 
8 |
9 | ## 🚀 Project Structure
10 |
11 | Inside of your Astro project, you'll see the following folders and files:
12 |
13 | ```
14 | /
15 | ├── public/
16 | │ └── favicon.svg
17 | ├── src/
18 | │ ├── components/
19 | │ │ └── Card.astro
20 | │ ├── layouts/
21 | │ │ └── Layout.astro
22 | │ └── pages/
23 | │ └── index.astro
24 | └── package.json
25 | ```
26 |
27 | Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
28 |
29 | There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
30 |
31 | Any static assets, like images, can be placed in the `public/` directory.
32 |
33 | ## 🧞 Commands
34 |
35 | All commands are run from the root of the project, from a terminal:
36 |
37 | | Command | Action |
38 | | :--------------------- | :------------------------------------------------- |
39 | | `npm install` | Installs dependencies |
40 | | `npm run dev` | Starts local dev server at `localhost:3000` |
41 | | `npm run build` | Build your production site to `./dist/` |
42 | | `npm run preview` | Preview your build locally, before deploying |
43 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro preview` |
44 | | `npm run astro --help` | Get help using the Astro CLI |
45 |
46 | ## 👀 Want to learn more?
47 |
48 | Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
49 |
--------------------------------------------------------------------------------
/docs/astro.config.ts:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import { rehypeHeadingIds } from '@astrojs/markdown-remark';
3 | import mdx from '@astrojs/mdx';
4 | import sitemap from '@astrojs/sitemap';
5 | import svelte from '@astrojs/svelte';
6 | import { defineConfig } from 'astro/config';
7 | import { h } from 'hastscript';
8 | import rehypeAutolinkHeadings, { type Options } from 'rehype-autolink-headings';
9 | import container from 'remark-custom-container/dist/esm/index.js';
10 | import UnpluginIcons from 'unplugin-icons/vite';
11 |
12 | const AnchorLinkIcon = h(
13 | 'svg',
14 | {
15 | width: '0.75em',
16 | height: '0.75em',
17 | version: 1.1,
18 | viewBox: '0 0 16 16',
19 | xlmns: 'http://www.w3.org/2000/svg',
20 | },
21 | h('path', {
22 | fillRule: 'evenodd',
23 | fill: 'currentcolor',
24 | d: 'M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z',
25 | }),
26 | );
27 |
28 | // https://astro.build/config
29 | export default defineConfig({
30 | site: 'https://neodrag.dev',
31 | integrations: [svelte(), mdx(), sitemap()],
32 |
33 | markdown: {
34 | extendDefaultPlugins: true,
35 | shikiConfig: {
36 | themes: {
37 | light: 'github-light',
38 | dark: 'github-dark',
39 | },
40 | },
41 | // @ts-ignore
42 | remarkPlugins: [container],
43 | rehypePlugins: [
44 | rehypeHeadingIds,
45 | [
46 | rehypeAutolinkHeadings,
47 | {
48 | test: (heading) => /^h[1-5]$/i.test(heading.tagName),
49 | behavior: 'append',
50 | properties: {
51 | ariaHidden: 'true',
52 | tabindex: -1,
53 | class: 'unstyled heading-anchor',
54 | },
55 | content: (heading) => [
56 | h(
57 | `span`,
58 | {
59 | ariaHidden: 'true',
60 | },
61 | AnchorLinkIcon,
62 | ),
63 | ],
64 | } as Options,
65 | ],
66 | ],
67 | },
68 |
69 | vite: {
70 | // @ts-ignore
71 | plugins: [UnpluginIcons({ autoInstall: true, compiler: 'svelte' })],
72 | },
73 | });
74 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "type": "module",
4 | "version": "0.0.14",
5 | "private": true,
6 | "scripts": {
7 | "dev": "astro dev",
8 | "build": "astro build",
9 | "preview": "astro preview",
10 | "astro": "astro"
11 | },
12 | "dependencies": {
13 | "@astrojs/markdown-remark": "^6.0.0",
14 | "@fontsource/jetbrains-mono": "^5.0.18",
15 | "@fontsource/plus-jakarta-sans": "^5.0.18",
16 | "@neodrag/svelte": "workspace:*",
17 | "astro-seo": "^0.8.0",
18 | "open-props": "^1.7.8",
19 | "popmotion": "^11.0.5",
20 | "runed": "^0.18.0",
21 | "slugify": "^1.6.6",
22 | "svelte-body": "^2.0.0",
23 | "svelte-copy": "^2.0.0",
24 | "svelte-inview": "^4.0.1",
25 | "throttle-debounce": "^5.0.0"
26 | },
27 | "devDependencies": {
28 | "@astrojs/mdx": "^4.0.1",
29 | "@astrojs/sitemap": "^3.2.1",
30 | "@astrojs/svelte": "^7.0.1",
31 | "@iconify/json": "^2.2.159",
32 | "@types/throttle-debounce": "^5.0.2",
33 | "astro": "^5.0.3",
34 | "astrojs-service-worker": "^2.0.0",
35 | "autoprefixer": "^10.4.16",
36 | "hast-util-to-string": "^3.0.0",
37 | "hastscript": "^9.0.0",
38 | "postcss": "^8.4.32",
39 | "postcss-jit-props": "^1.0.14",
40 | "prettier": "^3.1.1",
41 | "prettier-plugin-astro": "^0.14.1",
42 | "prettier-plugin-svelte": "^3.3.2",
43 | "rehype-autolink-headings": "^7.1.0",
44 | "remark-custom-container": "^1.2.0",
45 | "sass": "^1.82.0",
46 | "svelte": "^5.0.0",
47 | "typescript": "^5.5.0",
48 | "unplugin-icons": "^0.21.0",
49 | "vite": "^6.0.3"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/docs/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | const autoprefixer = require('autoprefixer');
2 | const postcssJitProps = require('postcss-jit-props');
3 | const OpenProps = require('open-props');
4 |
5 | module.exports = {
6 | // only vars used are in build output
7 | plugins: [postcssJitProps(OpenProps), autoprefixer()],
8 | };
9 |
--------------------------------------------------------------------------------
/docs/public/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PuruVJ/neodrag/61e34331987a48a49accbe62370867248c35fc07/docs/public/banner.png
--------------------------------------------------------------------------------
/docs/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PuruVJ/neodrag/61e34331987a48a49accbe62370867248c35fc07/docs/public/favicon.png
--------------------------------------------------------------------------------
/docs/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/docs/public/home/metaframeworks-dark.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PuruVJ/neodrag/61e34331987a48a49accbe62370867248c35fc07/docs/public/home/metaframeworks-dark.webp
--------------------------------------------------------------------------------
/docs/public/home/metaframeworks-light.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PuruVJ/neodrag/61e34331987a48a49accbe62370867248c35fc07/docs/public/home/metaframeworks-light.webp
--------------------------------------------------------------------------------
/docs/public/logo-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PuruVJ/neodrag/61e34331987a48a49accbe62370867248c35fc07/docs/public/logo-2x.png
--------------------------------------------------------------------------------
/docs/public/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/public/logos/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/public/logos/solid.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/public/logos/svelte.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/public/logos/vanilla.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/public/logos/vue.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/src/actions/portal.ts:
--------------------------------------------------------------------------------
1 | import { tick } from 'svelte';
2 |
3 | /**
4 | * Usage:
or
5 | *
6 | * @param {HTMLElement} el
7 | * @param {HTMLElement|string} target DOM Element or CSS Selector
8 | */
9 | export function portal(el: HTMLElement, target: HTMLElement | string = 'body') {
10 | let targetEl: HTMLElement;
11 |
12 | async function update(newTarget: HTMLElement | string) {
13 | target = newTarget;
14 |
15 | if (typeof target === 'string') {
16 | targetEl = document.querySelector(target)!;
17 |
18 | if (targetEl === null) {
19 | await tick();
20 | targetEl = document.querySelector(target)!;
21 | }
22 |
23 | if (targetEl === null) {
24 | throw new Error(`No element found matching css selector: "${target}"`);
25 | }
26 | } else if (target instanceof HTMLElement) {
27 | targetEl = target;
28 | } else {
29 | throw new TypeError(
30 | `Unknown portal target type: ${
31 | target === null ? 'null' : typeof target
32 | }. Allowed types: string (CSS selector) or HTMLElement.`,
33 | );
34 | }
35 | targetEl.appendChild(el);
36 | el.hidden = false;
37 | }
38 |
39 | function destroy() {
40 | if (el.parentNode) {
41 | el.parentNode.removeChild(el);
42 | }
43 | }
44 |
45 | update(target);
46 |
47 | return {
48 | update,
49 | destroy,
50 | };
51 | }
52 |
--------------------------------------------------------------------------------
/docs/src/actions/typingEffect.ts:
--------------------------------------------------------------------------------
1 | export function typingEffect(element: HTMLElement, speed: number) {
2 | const text = element.innerHTML;
3 |
4 | element.innerHTML = '';
5 |
6 | let i = 0;
7 | const timer = setInterval(() => {
8 | if (i < text.length) element.append(text.charAt(i++));
9 | }, speed);
10 |
11 | return {
12 | destroy: () => clearInterval(timer),
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/docs/src/auto-imports.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /* prettier-ignore */
3 | // @ts-nocheck
4 | // noinspection JSUnusedGlobalSymbols
5 | // Generated by unplugin-auto-import
6 | export {}
7 | declare global {
8 | const Code: (typeof import('$components/options/OptionsCode.astro'))['default'];
9 | const Example: (typeof import('$components/options/OptionsExample.astro'))['default'];
10 | const Examples: (typeof import('$components/options/OptionsExamples.svelte'))['default'];
11 | }
12 |
--------------------------------------------------------------------------------
/docs/src/components/Footer.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
34 |
35 |
53 |
--------------------------------------------------------------------------------
/docs/src/components/MetaThemeColor.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/docs/src/components/MobileNav.svelte:
--------------------------------------------------------------------------------
1 |
17 |
18 |
(shadow = false)}
23 | oninview_leave={() => (shadow = true)}
24 | >
25 |
26 |
38 |
39 | {#if is_nav_open}
40 |
41 |
46 | (is_nav_open = false)}>
47 |
48 |
49 |
50 |
51 | {/if}
52 |
53 |
132 |
--------------------------------------------------------------------------------
/docs/src/components/ThemeSwitcher.svelte:
--------------------------------------------------------------------------------
1 |
33 |
34 |
{
37 | const { clientX } = e;
38 |
39 | // Get difference
40 | position_x.target =
41 | clientX -
42 | theme_switcher_container!.getBoundingClientRect().left -
43 | draggable_el!.getBoundingClientRect().width / 2;
44 |
45 | if (position_x.current / container_width >= 0.5) {
46 | position_x.target = container_width - draggable_el!.getBoundingClientRect().width;
47 | } else {
48 | position_x.target = 0;
49 | }
50 |
51 | change_theme();
52 | }}
53 | bind:clientWidth={container_width}
54 | bind:this={theme_switcher_container}
55 | >
56 |
{
61 | position_x.target = 0;
62 | change_theme();
63 | }}
64 | >
65 |
66 |
67 |
68 |
{
77 | position_x.set(offsetX, { duration: 0 });
78 | change_theme();
79 | },
80 | onDragEnd: ({ offsetX, rootNode }) => {
81 | if (offsetX / container_width > 0.3) {
82 | position_x.target = container_width - rootNode.getBoundingClientRect().width;
83 | } else {
84 | position_x.target = 0;
85 | }
86 |
87 | change_theme();
88 | },
89 | }}
90 | >
91 |
92 |
{
97 | position_x.target = container_width - draggable_el!.getBoundingClientRect().width;
98 | change_theme();
99 | }}
100 | >
101 |
102 |
103 |
104 |
105 |
158 |
--------------------------------------------------------------------------------
/docs/src/components/home/ExploreFrameworks.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
Pick your framework
7 |
8 |
9 | {#each FRAMEWORKS as { name }}
10 | {@const Icon = FRAMEWORK_ICONS[name]}
11 |
12 |
13 |
14 |
15 | {/each}
16 |
17 |
18 |
42 |
--------------------------------------------------------------------------------
/docs/src/components/home/RingSVG.svelte:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/docs/src/components/home/ScrollDownIndicator.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
73 |
--------------------------------------------------------------------------------
/docs/src/components/home/features/bundle-sizes/BundleSizeFeature.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
Small bundle size
15 |
16 | Neodrag will never be heavy on your app. It's designed to be as small as possible, so you can
17 | use it without worrying about your bundle size.
18 |
19 |
20 |
21 |
22 |
23 | Ranges from {SIZES.svelte.size}KB
for
24 | Svelte
25 | to
26 | {SIZES.react.size}KB
for React .
27 |
28 |
29 |
30 | *Sizes in brotli after terser minification
31 |
32 |
33 |
34 |
35 |
36 |
37 | {#each sorted_frameworks.map(([framework, { size }]) => [framework, size, FRAMEWORK_ICONS[framework]] as const) as [framework, size, Icon]}
38 |
39 |
40 |
41 |
42 |
47 | {size} KB
48 |
49 |
50 | {/each}
51 |
52 |
53 |
117 |
--------------------------------------------------------------------------------
/docs/src/components/home/features/feature-box.scss:
--------------------------------------------------------------------------------
1 | @mixin paragraph {
2 | font-size: clamp(1rem, 2vw, 1.3rem);
3 |
4 | // max-width: clamp(20ch, 80vw, 100ch);
5 | }
6 |
--------------------------------------------------------------------------------
/docs/src/components/home/features/feature-rich/FeatureRichFeature.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
Feature rich
7 |
Play with the kitchen sink demo below
8 |
9 |
10 |
11 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/docs/src/components/home/features/multiple-frameworks/MultipleFrameworksFeature.svelte:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 |
Multi-framework
25 |
26 | One tool, endless possibilities: integrate with Svelte, Vue, React, Solid, and more.
28 | Core logic is implemented only once, so you can use Neodrag in different frameworks, and get
30 | the same predictible behavior
32 |
33 |
34 |
35 |
36 |
37 | npm install @neodrag/
38 |
39 | {#key selected_framework}
40 | {@html selected_framework}
46 | {/key}
47 |
48 |
49 | (copied = true),
56 | }}
57 | >
58 |
59 |
60 | {#if copied}
61 |
62 |
63 |
64 | {:else}
65 |
66 |
67 |
68 | {/if}
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | (selected_framework = framework)} />
77 |
78 |
79 |
158 |
--------------------------------------------------------------------------------
/docs/src/components/home/features/ssr-friendly/SSRFriendlyFeature.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
SSR-friendly
7 |
8 | Neodrag is Server Side Renderable. Will play well with meta-frameworks like Sveltekit, NextJS,
9 | Nuxt, Vitepress, SolidStart and more
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
33 |
--------------------------------------------------------------------------------
/docs/src/components/options/Options.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import type { MDXInstance } from 'astro';
3 | import slugify from 'slugify';
4 | import PhLinkThin from '~icons/ph/link-thin';
5 |
6 | type OptionsFrontMatter = {
7 | title: string;
8 | type: string;
9 | defaultValue: string;
10 | shortDescription: string;
11 | deprecated?: boolean;
12 | deprecatedText?: string;
13 | };
14 |
15 | const ORDER = [
16 | 'axis',
17 | 'bounds',
18 | 'recomputeBounds',
19 | 'grid',
20 | 'threshold',
21 | 'defaultPosition',
22 | 'position',
23 | 'gpuAcceleration',
24 | 'legacyTranslate',
25 | 'transform',
26 | 'applyUserSelectHack',
27 | 'ignoreMultitouch',
28 | 'disabled',
29 | 'handle',
30 | 'cancel',
31 | 'defaultClass',
32 | 'defaultClassDragging',
33 | 'defaultClassDragged',
34 | 'onDragStart',
35 | 'onDrag',
36 | 'onDragEnd',
37 | ];
38 |
39 | const optionsMD = Object.values(
40 | import.meta.glob
>('../../documentation/options/*/+option.mdx', {
41 | eager: true,
42 | }),
43 | );
44 |
45 | function validate() {
46 | if (optionsMD.length > ORDER.length) {
47 | const excludedOptions = optionsMD
48 | .map((o) => o.frontmatter.title)
49 | .filter((o) => !ORDER.includes(o));
50 |
51 | throw new Error(`Add \`${excludedOptions.join(', ')}\` properties to ORDER array`);
52 | }
53 |
54 | for (const option of optionsMD) {
55 | // @ts-ignore
56 | if (!option.shortDescription) {
57 | throw new Error(`Add \`shortDescription\` to ${option.frontmatter.title}`);
58 | }
59 | }
60 | }
61 |
62 | validate();
63 |
64 | const orderedOptionsMD = ORDER.map(
65 | (property) => optionsMD.find((option) => option.frontmatter.title === property)!,
66 | ) as typeof optionsMD & { shortDescription: string }[];
67 | ---
68 |
69 |
70 | {
71 | orderedOptionsMD.map(
72 | ({ Content, frontmatter: { defaultValue, title, type, deprecated, deprecatedText } }) => (
73 | <>
74 |
75 | {title}
76 |
77 |
83 |
84 |
85 |
86 |
87 | {deprecated === true ? `⚠️ Deprecated: ${deprecatedText}` : ''}
88 |
89 | Type:
90 |
91 | {type}
92 |
93 |
94 | Default Value: {defaultValue}
95 |
96 |
97 |
98 | >
99 | ),
100 | )
101 | }
102 |
103 |
104 |
116 |
--------------------------------------------------------------------------------
/docs/src/components/options/OptionsCode.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import type { Framework } from '$helpers/constants';
3 |
4 | const framework = Astro.url.pathname.split('/').filter(Boolean).at(-1) as Framework;
5 | ---
6 |
7 | {framework === 'react' && }
8 | {framework === 'svelte' && }
9 | {framework === 'vue' && }
10 | {framework === 'vanilla' && }
11 | {framework === 'solid' && }
12 |
--------------------------------------------------------------------------------
/docs/src/components/options/OptionsExample.astro:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
59 |
--------------------------------------------------------------------------------
/docs/src/css/breakpoints.scss:
--------------------------------------------------------------------------------
1 | @import './include-media';
2 |
--------------------------------------------------------------------------------
/docs/src/css/globals.scss:
--------------------------------------------------------------------------------
1 | /* optional imports that use the props */
2 | @import url('open-props/normalize.min.css');
3 |
4 | @import './themes';
5 | @import './breakpoints';
6 | @import './include-media';
7 |
8 | * {
9 | color: var(--app-color-dark);
10 | }
11 |
12 | *:focus-visible {
13 | box-shadow: 0 0 0 4px hsla(var(--secondary-color-hsl, --app-color-primary-hsl), 0.25);
14 | }
15 |
16 | html {
17 | width: 100%;
18 | }
19 |
20 | html,
21 | body {
22 | height: 100%;
23 | width: 100%;
24 |
25 | background-color: var(--app-color-shell);
26 |
27 | scroll-behavior: smooth;
28 | overflow-x: hidden;
29 |
30 | font-family: var(--app-font-main);
31 | }
32 |
33 | body {
34 | height: auto !important;
35 |
36 | accent-color: var(--app-color-primary);
37 | }
38 |
39 | :is(h1, h2, h3, h4, h5, .h1, .h2, .h3, .h4, .h5) {
40 | width: max-content;
41 |
42 | color: var(--app-color-dark);
43 | font-family: var(--app-font-heading);
44 | word-break: break-all;
45 |
46 | margin: 0.75em 0 0.25em;
47 |
48 | max-width: 100%;
49 | }
50 |
51 | h1,
52 | .h1 {
53 | margin-top: 0;
54 | font-size: clamp(2.35rem, 10vw, 3.998rem);
55 | }
56 |
57 | h2,
58 | .h2 {
59 | font-size: clamp(1.77rem, 5vw, 2.827rem);
60 | }
61 |
62 | h3,
63 | .h3 {
64 | font-size: clamp(1.33rem, 2.5vw, 1.999rem);
65 | }
66 |
67 | h4,
68 | .h4 {
69 | font-size: clamp(1rem, 1.25vw, 1.414rem);
70 | }
71 |
72 | code {
73 | color: var(--app-color-dark);
74 | background-color: var(--app-color-dark-contrast);
75 |
76 | font-family: var(--app-font-mono);
77 | }
78 |
79 | pre code {
80 | color: initial;
81 | background-color: initial;
82 | }
83 |
84 | kbd {
85 | font-family: var(--app-font-mono);
86 | font-weight: 400;
87 | }
88 |
89 | :is(a[href], a[href]:visited):where(:not(.unstyled)) {
90 | --distance: calc(50% - 0.375rem);
91 | --opacity: 0.35;
92 | --duration: 150ms;
93 | --easing: ease-in-out;
94 |
95 | color: var(--app-color-primary);
96 | font-weight: 600 !important;
97 | text-decoration: none;
98 |
99 | padding: 0 0.25rem;
100 |
101 | background-image: linear-gradient(
102 | transparent 0%,
103 | transparent var(--distance),
104 | hsla(var(--app-color-primary-hsl), var(--opacity)) var(--distance),
105 | hsla(var(--app-color-primary-hsl), var(--opacity)) 100%
106 | );
107 | background-size: 100% 200%;
108 | background-position: 0 0;
109 |
110 | transition: var(--duration) var(--easing);
111 | transition-property: color, background-position, background-image, border-radius;
112 |
113 | &:hover,
114 | &:focus-visible {
115 | background-position: 0 100%;
116 |
117 | color: var(--app-color-primary-contrast);
118 | font-weight: 600 !important;
119 |
120 | border-radius: 0.25rem;
121 |
122 | --opacity: 1;
123 | }
124 | }
125 |
126 | .sr-only {
127 | position: absolute;
128 | width: 1px;
129 | height: 1px;
130 | padding: 0;
131 | margin: -1px;
132 | overflow: hidden;
133 | clip: rect(0, 0, 0, 0);
134 | white-space: nowrap;
135 | border-width: 0;
136 | }
137 |
138 | // Set the text-FRAMEWORK
139 | // Map of frameworks
140 | $frameworks: svelte, react, vue, solid, vanilla;
141 |
142 | @each $framework in $frameworks {
143 | // Set the text-FRAMEWORK
144 | .text-#{$framework} {
145 | color: var(--app-color-brand-#{$framework});
146 | fill: var(--app-color-brand-#{$framework});
147 | }
148 |
149 | .bg-#{$framework} {
150 | background-color: var(--app-color-brand-#{$framework});
151 | }
152 | }
153 |
154 | ::selection {
155 | background-color: hsla(var(--secondary-color-hsl, var(--app-color-primary-hsl)), 0.3);
156 | // color: var(--app-color-primary-contrast);
157 | // -webkit-text-fill-color: var(--app-color-primary-contrast);
158 | }
159 |
160 | p {
161 | word-wrap: normal;
162 | }
163 |
164 | button {
165 | background-color: transparent;
166 | }
167 |
168 | .font-mono {
169 | font-family: var(--app-font-mono);
170 | }
171 |
--------------------------------------------------------------------------------
/docs/src/data/sizes.json:
--------------------------------------------------------------------------------
1 | {
2 | "react": {
3 | "size": 2.18,
4 | "version": "2.2.0"
5 | },
6 | "solid": {
7 | "size": 1.98,
8 | "version": "2.2.0"
9 | },
10 | "svelte": {
11 | "size": 1.94,
12 | "version": "2.2.0"
13 | },
14 | "vanilla": {
15 | "size": 2,
16 | "version": "2.2.0"
17 | },
18 | "vue": {
19 | "size": 1.98,
20 | "version": "2.2.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docs/src/documentation/exported-types.mdx:
--------------------------------------------------------------------------------
1 | import { Code } from 'astro/components';
2 |
3 | ### Types Exported from package
4 |
5 | This package exports these types you can use:
6 |
7 |
17 |
18 | `DragOptions` is the documented list of all options provided by the component.
19 |
20 | `DragAxis` is the type of `axis` option, and is equal to `'both' | 'x' | 'y' | 'none'`.
21 |
22 | `DragBounds` is `'parent' | string | Partial`, the complete type of `bounds` option.
23 |
24 | `DragBoundsCoords` is when you're specifying the `bounds` field using an object, this is the type needed for that.
25 |
26 | `DragEventData` is the data provided during the [events](#events)
27 |
28 | ```ts
29 | export type DragAxis = 'both' | 'x' | 'y' | 'none';
30 |
31 | export type DragBounds = 'parent' | string | Partial;
32 |
33 | export type DragEventData = {
34 | /** How much element moved from its original position horizontally */
35 | offsetX: number;
36 |
37 | /** How much element moved from its original position vertically */
38 | offsetY: number;
39 |
40 | /** The node on which the draggable is applied */
41 | rootNode: HTMLElement;
42 |
43 | /** The element being dragged */
44 | currentNode: HTMLElement;
45 |
46 | /** The pointer event that triggered the drag */
47 | event: PointerEvent;
48 | };
49 |
50 | export type DragBoundsCoords = {
51 | /** Number of pixels from left of the window */
52 | left: number;
53 |
54 | /** Number of pixels from top of the window */
55 | top: number;
56 |
57 | /** Number of pixels from the right side of window */
58 | right: number;
59 |
60 | /** Number of pixels from the bottom of the window */
61 | bottom: number;
62 | };
63 | ```
64 |
--------------------------------------------------------------------------------
/docs/src/documentation/installation.mdx:
--------------------------------------------------------------------------------
1 | import { Code } from 'astro/components';
2 |
3 | ## Installation
4 |
5 | {/* prettier-ignore */}
6 |
17 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/applyUserSelectHack/+option.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: applyUserSelectHack
3 | type: 'boolean'
4 | defaultValue: 'true'
5 | ---
6 |
7 | import Code from '$components/options/OptionsCode.astro';
8 | import Example from '$components/options/OptionsExample.astro';
9 | import Examples from '$components/options/OptionsExamples.svelte';
10 |
11 | import UserSelectExample from './UserSelect.example.svelte';
12 | import NoUserSelectExample from './NoUserSelect.example.svelte';
13 |
14 | export const shortDescription = 'Applies `user-select: none` on ` ` when dragging';
15 |
16 | Applies `user-select: none` on ` ` element when dragging, to prevent the irritating effect where dragging doesn't happen and the text is selected. Applied when dragging starts and removed when it stops.
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ```svelte
25 |
26 | User Select disabled
27 |
28 | ```
29 |
30 |
31 |
32 | ```vue
33 |
34 |
35 | User Select disabled
36 |
37 |
38 | ```
39 |
40 |
41 |
42 | ```jsx
43 |
44 | User Select disabled
45 |
46 | ```
47 |
48 |
49 |
50 | ```ts
51 | useDraggable(draggableRef, { applyUserSelectHack: true });
52 | ```
53 |
54 |
55 |
56 | ```js
57 | new Draggable(el, { applyUserSelectHack: true });
58 | ```
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | ```svelte
71 |
72 | User Select enabled
73 |
74 | ```
75 |
76 |
77 |
78 | ```vue
79 |
80 |
81 | User Select enabled
82 |
83 |
84 | ```
85 |
86 |
87 |
88 | ```jsx
89 |
90 | User Select enabled
91 |
92 | ```
93 |
94 |
95 |
96 | ```ts
97 | useDraggable(draggableRef, { applyUserSelectHack: false });
98 | ```
99 |
100 |
101 |
102 | ```js
103 | new Draggable(el, { applyUserSelectHack: false });
104 | ```
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/applyUserSelectHack/NoUserSelect.example.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | User Select disabled
9 |
10 | {#snippet caption()}
11 | {#if isDesktop}
12 | Hit
13 | {#if isMac}
14 |
15 | {:else}
16 | ctrl
17 | {/if} + A
18 |
19 |
20 | while dragging - Nothing will be selected
21 | {/if}
22 | {/snippet}
23 |
24 |
25 |
32 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/applyUserSelectHack/UserSelect.example.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | User Select enabled
9 |
10 | {#snippet caption()}
11 | {#if isDesktop}
12 | Hit
13 | {#if isMac}
14 |
15 | {:else}
16 | ctrl
17 | {/if} + A
18 |
19 |
20 | while dragging - Text will be selected
21 | {/if}
22 | {/snippet}
23 |
24 |
25 |
32 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/applyUserSelectHack/user-select.mixin.scss:
--------------------------------------------------------------------------------
1 | @mixin userSelectKBD() {
2 | display: flex;
3 | align-items: center;
4 | gap: 1ch;
5 |
6 | padding: 0.2rem 0.2rem;
7 |
8 | line-height: 1;
9 | }
10 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/axis/BothAxis.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | Both directions
6 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/axis/NoneAxis.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | None axis: Won't drag
6 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/axis/XAxis.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | Horizontal
6 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/axis/YAxis.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | Vertical
6 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/bounds/BodyBounds.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 | Can't go outside <body>
10 |
11 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/bounds/CoordinateBounds.example.svelte:
--------------------------------------------------------------------------------
1 |
83 |
84 |
85 | Limited to:
86 |
87 | top: {supposed_bounds.top}
88 |
89 | left: {supposed_bounds.left}
90 |
91 | bottom: {supposed_bounds.bottom}
92 |
93 | right: {supposed_bounds.right}
94 |
95 |
96 | {#snippet caption()}
97 | Bounded by these coordinates from the window's edges.
98 | {/snippet}
99 |
100 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/bounds/ParentBounds.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 | Can't go outside the parent element
8 |
9 |
10 |
11 |
21 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/cancel/MultipleCancelElement.example.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 | This will drag!
10 |
11 |
12 |
13 | This won't drag
14 | This won't drag
15 |
16 | {#snippet caption()}
17 | Multiple cancel
passed as array of elements.
18 | {/snippet}
19 |
20 |
21 |
28 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/cancel/MultipleCancelSelector.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | This will drag!
7 |
8 |
9 |
10 | This won't drag
11 | This won't drag
12 |
13 | {#snippet caption()}
14 | Multiple cancel
passed as element.
15 | {/snippet}
16 |
17 |
18 |
25 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/cancel/SingleCancelElement.example.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | This will drag!
9 |
10 |
11 |
12 | This won't drag
13 |
14 | {#snippet caption()}
15 | Single cancel
passed as element.
16 | {/snippet}
17 |
18 |
19 |
26 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/cancel/SingleCancelSelector.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | This will drag!
7 |
8 |
9 |
10 | This won't drag
11 |
12 | {#snippet caption()}
13 | Single cancel
with selector
14 | {/snippet}
15 |
16 |
17 |
24 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/cancel/cancel-base.mixin.scss:
--------------------------------------------------------------------------------
1 | @mixin cancelExample {
2 | text-decoration: line-through;
3 | color: hsla(var(--app-color-primary-contrast-hsl), 0.7);
4 |
5 | background-color: hsla(var(--app-color-primary-contrast-hsl), 0.1);
6 |
7 | padding: 0.25rem 0.25rem;
8 |
9 | border-radius: 4px;
10 |
11 | cursor: not-allowed;
12 | }
13 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/defaultClass/+option.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: defaultClass
3 | type: 'string'
4 | defaultValue: 'undefined'
5 | ---
6 |
7 | export const shortDescription = 'Class to apply to draggable element.';
8 |
9 | Class to apply to draggable element.
10 |
11 | ::: warning
12 |
13 | If `handle` is provided, it will still apply class on the parent element, **NOT** the handle.
14 |
15 | :::
16 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/defaultClassDragged/+option.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: defaultClassDragged
3 | type: 'string'
4 | defaultValue: "'neodrag-dragged'"
5 | ---
6 |
7 | export const shortDescription =
8 | 'Class to apply on the parent element if it has been dragged at least once';
9 |
10 | {shortDescription}. Removed once dragging stops.
11 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/defaultClassDragging/+option.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: defaultClassDragging
3 | type: 'string'
4 | defaultValue: "'neodrag-dragging'"
5 | ---
6 |
7 | export const shortDescription = 'Class to apply on the parent element when it is dragging';
8 |
9 | {shortDescription}
10 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/defaultPosition/+option.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: defaultPosition
3 | type: '{ x: number; y: number }'
4 | defaultValue: '{ x: 0, y: 0 }'
5 | ---
6 |
7 | import Code from '$components/options/OptionsCode.astro';
8 | import Example from '$components/options/OptionsExample.astro';
9 | import Examples from '$components/options/OptionsExamples.svelte';
10 |
11 | import DefaultPositionExample from './DefaultPosition.example.svelte';
12 |
13 | export const shortDescription =
14 | 'Offsets your element to the position you specify in the very beginning';
15 |
16 | {shortDescription}. `x` and `y` should be in pixels. Ignored if [position](#position) is passed.
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ```svelte
25 |
26 | Shifted by (100, 40)
27 |
28 | ```
29 |
30 |
31 |
32 | ```vue
33 |
34 |
35 | Shifted by (100, 40)
36 |
37 |
38 | ```
39 |
40 |
41 |
42 | ```jsx
43 |
44 | Shifted by (100, 40)
45 |
46 | ```
47 |
48 |
49 |
50 | ```ts
51 | useDraggable(draggableRef, { defaultPosition: { x: 100, y: 40 } });
52 | ```
53 |
54 |
55 |
56 | ```js
57 | new Draggable(el, { defaultPosition: { x: 100, y: 40 } });
58 | ```
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/defaultPosition/DefaultPosition.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Shifted by (100, 40)
7 |
8 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/disabled/+option.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: disabled
3 | type: 'boolean'
4 | defaultValue: 'false'
5 | ---
6 |
7 | import Code from '$components/options/OptionsCode.astro';
8 | import Example from '$components/options/OptionsExample.astro';
9 | import Examples from '$components/options/OptionsExamples.svelte';
10 |
11 | import DisabledExample from './Disabled.example.svelte';
12 |
13 | export const shortDescription = 'Disables dragging';
14 |
15 | {shortDescription}.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | ```svelte
24 |
25 | Disabled. Won't drag, won't trigger any events
26 |
27 | ```
28 |
29 |
30 |
31 | ```vue
32 |
33 |
34 | Disabled. Won't drag, won't trigger any events
35 |
36 |
37 | ```
38 |
39 |
40 |
41 | ```jsx
42 |
43 | Disabled. Won't drag, won't trigger any events
44 |
45 | ```
46 |
47 |
48 |
49 | ```ts
50 | useDraggable(draggableRef, { disabled: true });
51 | ```
52 |
53 |
54 |
55 | ```js
56 | new Draggable(el, { disabled: true });
57 | ```
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/disabled/Disabled.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Disabled. Won't drag, won't trigger any events
7 |
8 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/gpuAcceleration/+option.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: 'gpuAcceleration'
3 | type: 'boolean'
4 | defaultValue: 'true'
5 | deprecated: true
6 | deprecatedText: 'Will be removed in v3'
7 | ---
8 |
9 | import Code from '$components/options/OptionsCode.astro';
10 | import Example from '$components/options/OptionsExample.astro';
11 | import Examples from '$components/options/OptionsExamples.svelte';
12 |
13 | import NoAccelerationExample from './NoAcceleration.example.svelte';
14 | import AccelerationExample from './Acceleration.example.svelte';
15 |
16 | export let shortDescription =
17 | 'If true, uses `translate3d` instead of `translate` to move the element around, and the hardware acceleration kicks in.';
18 |
19 | {shortDescription}
20 |
21 | `true` by default, but can be set to `false` if [blurry text issue](https://developpaper.com/question/why-does-the-use-of-css3-translate3d-result-in-blurred-display/) occurs.
22 |
23 | ::: info
24 |
25 | Depending on the device, you may not see much difference
26 |
27 | :::
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | ```svelte
36 |
37 | GPU acceleration on
38 |
39 | ```
40 |
41 |
42 |
43 | ```vue
44 |
45 |
46 | GPU acceleration on
47 |
48 |
49 | ```
50 |
51 |
52 |
53 | ```jsx
54 |
55 | GPU acceleration on
56 |
57 | ```
58 |
59 |
60 |
61 | ```ts
62 | useDraggable(draggableRef, { gpuAcceleration: true });
63 | ```
64 |
65 |
66 |
67 | ```js
68 | new Draggable(el, { gpuAcceleration: true });
69 | ```
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | ```svelte
82 |
83 | GPU acceleration off
84 |
85 | ```
86 |
87 |
88 |
89 | ```vue
90 |
91 |
92 | GPU acceleration off
93 |
94 |
95 | ```
96 |
97 |
98 |
99 | ```jsx
100 |
101 | GPU acceleration off
102 |
103 | ```
104 |
105 |
106 |
107 | ```ts
108 | useDraggable(draggableRef, { gpuAcceleration: false });
109 | ```
110 |
111 |
112 |
113 | ```js
114 | new Draggable(el, { gpuAcceleration: false });
115 | ```
116 |
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/gpuAcceleration/Acceleration.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | GPU acceleration on
7 |
8 | {#snippet pos(x, y)}
9 | transform: translate3d({x}px, {y}px, 0)
10 | {/snippet}
11 |
12 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/gpuAcceleration/NoAcceleration.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | GPU acceleration off
7 |
8 | {#snippet pos(x, y)}
9 | transform: translate({x}px, {y}px)
10 | {/snippet}
11 |
12 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/grid/InactiveGrid.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | Snaps to 0x0 grid - Won't drag at all
6 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/grid/RectangleGrid.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | Snaps to 72x91 grid
6 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/grid/SquareGrid.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | Snaps to 50x50 grid
6 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/handle/MultipleHandleElement.example.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 | Won't drag ❌
11 |
12 |
13 |
14 | Drag me ✅
15 | Drag me ✅
16 |
17 | {#snippet caption()}
18 | Multiple handle
with element
19 | {/snippet}
20 |
21 |
22 |
23 |
34 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/handle/MultipleHandleSelector.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 | Won't drag ❌
8 |
9 |
10 |
11 | Drag me ✅
12 | Drag me ✅
13 |
14 | {#snippet caption()}
15 | Multiple handle
with selector
16 | {/snippet}
17 |
18 |
19 |
20 |
31 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/handle/SingleHandleElement.example.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 | Won't drag ❌
10 |
11 |
12 |
13 | Drag me ✅
14 |
15 | {#snippet caption()}
16 | Single handle
with element
17 | {/snippet}
18 |
19 |
20 |
21 |
32 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/handle/SingleHandleSelector.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 | Won't drag ❌
8 |
9 |
10 |
11 | Drag me ✅
12 |
13 | {#snippet caption()}
14 | Single handle
with selector
15 | {/snippet}
16 |
17 |
18 |
19 |
30 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/handle/handle-base.mixin.scss:
--------------------------------------------------------------------------------
1 | @mixin handle {
2 | background-color: hsla(var(--app-color-primary-contrast-hsl), 1);
3 |
4 | padding: 0.25rem 0.25rem;
5 |
6 | border-radius: 4px;
7 | }
8 |
9 | @mixin box {
10 | color: hsla(var(--app-color-primary-contrast-hsl), 0.7);
11 | }
12 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/ignoreMultitouch/+option.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: ignoreMultitouch
3 | type: 'boolean'
4 | defaultValue: 'true'
5 | ---
6 |
7 | import Code from '$components/options/OptionsCode.astro';
8 | import Example from '$components/options/OptionsExample.astro';
9 | import Examples from '$components/options/OptionsExamples.svelte';
10 |
11 | import IgnoredMultitouchExample from './IgnoredMultitouch.example.svelte';
12 | import MultitouchExample from './Multitouch.example.svelte';
13 |
14 | export const shortDescription = 'Ignores touch events with more than 1 touch.';
15 |
16 | {shortDescription}
17 | This helps when you have multiple elements on a canvas where you want to implement pinch-to-zoom behaviour.
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | ```svelte
26 |
27 | Multi touch ignored
28 |
29 | ```
30 |
31 |
32 |
33 | ```vue
34 |
35 |
36 | Multi touch ignored
37 |
38 |
39 | ```
40 |
41 |
42 |
43 | ```jsx
44 |
45 | Multi touch ignored
46 |
47 | ```
48 |
49 |
50 |
51 | ```ts
52 | useDraggable(draggableRef, { ignoreMultitouch: true });
53 | ```
54 |
55 |
56 |
57 | ```js
58 | new Draggable(el, { ignoreMultitouch: true });
59 | ```
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | ```svelte
72 |
73 | Multi touch allowed
74 |
75 | ```
76 |
77 |
78 |
79 | ```vue
80 |
81 |
82 | Multi touch allowed
83 |
84 |
85 | ```
86 |
87 |
88 |
89 | ```jsx
90 |
91 | Multi touch allowed
92 |
93 | ```
94 |
95 |
96 |
97 | ```ts
98 | useDraggable(draggableRef, { ignoreMultitouch: false });
99 | ```
100 |
101 |
102 |
103 | ```js
104 | new Draggable(el, { ignoreMultitouch: false });
105 | ```
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/ignoreMultitouch/IgnoredMultitouch.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | Multi touch ignored
6 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/ignoreMultitouch/Multitouch.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | Multi touch allowed
6 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/legacyTranslate/LegacyTranslateGPU.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Legacy translate with GPU acceleration
7 |
8 | {#snippet pos(x, y)}
9 | transform: translate3d({x}px, {y}px, 0)
10 | {/snippet}
11 |
12 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/legacyTranslate/LegacyTranslateNoGPU.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Legacy translate with no GPU acceleration
7 |
8 | {#snippet pos(x, y)}
9 | transform: translate({x}px, {y}px)
10 | {/snippet}
11 |
12 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/legacyTranslate/TranslateGPU.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Modern translate with GPU acceleration
7 |
8 | {#snippet pos(x, y)}
9 | translate: {x}px {y}px 1px
10 | {/snippet}
11 |
12 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/legacyTranslate/TranslateNoGPU.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Modern translate with no GPU acceleration
7 |
8 | {#snippet pos(x, y)}
9 | translate: {x}px {y}px
10 | {/snippet}
11 |
12 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/onDrag/+option.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: onDrag
3 | type: '(data: DragEventData) => void'
4 | defaultValue: 'undefined'
5 | ---
6 |
7 | export const shortDescription = 'Fires when dragging is going on';
8 |
9 | {shortDescription}.
10 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/onDragEnd/+option.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: onDragEnd
3 | type: '(data: DragEventData) => void'
4 | defaultValue: 'undefined'
5 | ---
6 |
7 | export const shortDescription = 'Fires when dragging stops';
8 |
9 | {shortDescription}.
10 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/onDragStart/+option.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: onDragStart
3 | type: '(data: DragEventData) => void'
4 | defaultValue: 'undefined'
5 | ---
6 |
7 | export const shortDescription = 'Fires when dragging start';
8 |
9 | {shortDescription}.
10 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/position/DisabledPosition.example.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | I can be moved only with the slider
9 |
10 | {#snippet caption()}
11 | X:
12 | Y:
13 |
14 | {/snippet}
15 |
16 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/position/Position.example.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | I can be moved with the slider too
9 |
10 | {#snippet caption()}
11 | X:
12 | Y:
13 |
14 | {/snippet}
15 |
16 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/recomputeBounds/+option.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: recomputeBounds
3 | type: '{ dragStart?: boolean; drag?: boolean; dragEnd?: boolean; }'
4 | defaultValue: '{ dragStart: true, drag: false, dragEnd: false }'
5 | ---
6 |
7 | import Code from '$components/options/OptionsCode.astro';
8 | import Example from '$components/options/OptionsExample.astro';
9 | import Examples from '$components/options/OptionsExamples.svelte';
10 |
11 | export const shortDescription = 'When to recalculate the dimensions of the `bounds` element.';
12 |
13 | {shortDescription}
14 |
15 | By default, bounds are recomputed only on dragStart. Use this options to change that behavior.
16 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/threshold/+option.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: threshold
3 | type: '{ distance?: number; delay?: number }'
4 | defaultValue: '{ distance: 3, delay: 0 }'
5 | ---
6 |
7 | import Code from '$components/options/OptionsCode.astro';
8 | import Example from '$components/options/OptionsExample.astro';
9 | import Examples from '$components/options/OptionsExamples.svelte';
10 |
11 | import DelayExample from './Delay.example.svelte';
12 | import DistanceExample from './Distance.example.svelte';
13 |
14 | export const shortDescription =
15 | "Threshold for dragging to start. If the user moves the mouse/finger less than this distance, the dragging won't start.";
16 |
17 | Allows you to set a threshold for dragging to start. If the user moves the mouse/finger less than this distance, the dragging won't start. Or user must hold the mouse/finger for the specified delay for dragging to begin.
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | ```svelte
26 |
27 | 200ms Delay
28 |
29 | ```
30 |
31 |
32 |
33 | ```vue
34 |
35 |
36 | 200ms Delay
37 |
38 |
39 | ```
40 |
41 |
42 |
43 | ```jsx
44 |
45 | 200ms Delay
46 |
47 | ```
48 |
49 |
50 |
51 | ```ts
52 | useDraggable(draggableRef, { threshold: { delay: 200 } });
53 | ```
54 |
55 |
56 |
57 | ```js
58 | new Draggable(el, { threshold: { delay: 200 } });
59 | ```
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | ```svelte
72 |
73 | 100px distance
74 |
75 | ```
76 |
77 |
78 |
79 | ```vue
80 |
81 |
82 | 100px distance
83 |
84 |
85 | ```
86 |
87 |
88 |
89 | ```jsx
90 |
91 | 100px distance
92 |
93 | ```
94 |
95 |
96 |
97 | ```ts
98 | useDraggable(draggableRef, { threshold: { distance: 100 } });
99 | ```
100 |
101 |
102 |
103 | ```js
104 | new Draggable(el, { threshold: { distance: 100 } });
105 | ```
106 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/threshold/Delay.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
12 | 200ms delay
13 |
14 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/threshold/Distance.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
12 | 100px distance
13 |
14 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/transform/ManualTransform.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | {
8 | rootNode.style.translate = `${offsetX + 50}px ${offsetY + 20}px`;
9 | },
10 | }}
11 | >
12 | Moving by manually setting rootNode.style
13 |
14 |
--------------------------------------------------------------------------------
/docs/src/documentation/options/transform/ReturnTransform.example.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | {
8 | return `translate(${offsetX + 50}px, ${offsetY + 20}px)`;
9 | },
10 | }}
11 | >
12 | Moving by returning transform
string
13 |
14 |
--------------------------------------------------------------------------------
/docs/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
5 | type ObjectKeys = Obj extends object
6 | ? (keyof Obj)[]
7 | : Obj extends number
8 | ? []
9 | : Obj extends Array | string
10 | ? string[]
11 | : never;
12 |
13 | interface ObjectConstructor {
14 | keys(o: ObjectType): ObjectKeys;
15 | entries(o: ObjType): [Unpacked>, ObjType[keyof ObjType]][];
16 | }
17 |
18 | interface Storage {
19 | getItem(key: string): T | null;
20 | }
21 |
22 | type Unpacked = ArrayLike extends (infer RootType)[] ? RootType : ArrayLike;
23 |
24 | type Unpromisify =
25 | PromiseLike extends Promise ? RootType : PromiseLike;
26 |
27 | interface Array {
28 | fill(value: T, start?: number | undefined, end?: number | undefined): T[];
29 | }
30 |
--------------------------------------------------------------------------------
/docs/src/helpers/constants.ts:
--------------------------------------------------------------------------------
1 | export const FRAMEWORKS = [
2 | { name: 'svelte' },
3 | { name: 'react' },
4 | { name: 'vue' },
5 | { name: 'solid' },
6 | { name: 'vanilla' },
7 | ] as const;
8 |
9 | export type Framework = (typeof FRAMEWORKS)[number]['name'];
10 |
--------------------------------------------------------------------------------
/docs/src/helpers/framework-icons.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | import LogosSvelteIcon from '~icons/logos/svelte-icon';
3 | // @ts-ignore
4 | import LogosReactIcon from '~icons/logos/react';
5 | // @ts-ignore
6 | import LogosVueIcon from '~icons/logos/vue';
7 | // @ts-ignore
8 | import LogosSolidIcon from '~icons/logos/solidjs-icon';
9 | // @ts-ignore
10 | import LogosVanillaIcon from '~icons/logos/javascript';
11 | // @ts-ignore
12 | import IonReloadIcon from '~icons/ion/reload';
13 |
14 | export const FRAMEWORK_ICONS = {
15 | svelte: LogosSvelteIcon,
16 | react: LogosReactIcon,
17 | vue: LogosVueIcon,
18 | solid: LogosSolidIcon,
19 | vanilla: LogosVanillaIcon,
20 | };
21 |
--------------------------------------------------------------------------------
/docs/src/helpers/utils.ts:
--------------------------------------------------------------------------------
1 | export const browser = !import.meta.env.SSR;
2 |
3 | export const isMac = browser && navigator.platform.toUpperCase().indexOf('MAC') >= 0;
4 | export const isMobile =
5 | browser && navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(android)|(webOS)/i);
6 | export const isDesktop = !isMobile;
7 |
8 | export function elements_overlap(el1: HTMLElement, el2: HTMLElement) {
9 | const dom_rect1 = el1.getBoundingClientRect();
10 | const dom_rect2 = el2.getBoundingClientRect();
11 |
12 | return !(
13 | dom_rect1.top > dom_rect2.bottom ||
14 | dom_rect1.right < dom_rect2.left ||
15 | dom_rect1.bottom < dom_rect2.top ||
16 | dom_rect1.left > dom_rect2.right
17 | );
18 | }
19 |
20 | export function wait_for(ms: number) {
21 | return new Promise((r) => setTimeout(r, ms));
22 | }
23 |
--------------------------------------------------------------------------------
/docs/src/layouts/Layout.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import '@fontsource/jetbrains-mono/400.css';
3 | import '@fontsource/jetbrains-mono/500.css';
4 | import '@fontsource/jetbrains-mono/600.css';
5 | import '@fontsource/plus-jakarta-sans/400.css';
6 | import '@fontsource/plus-jakarta-sans/500.css';
7 | import '../css/globals.scss';
8 |
9 | import PawCursor from '$components/PawCursor.svelte';
10 | import { SEO } from 'astro-seo';
11 | import { ClientRouter } from 'astro:transitions';
12 | import ThemeWatcher from './ThemeWatcher.svelte';
13 |
14 | export interface Props {
15 | title: string;
16 | }
17 |
18 | const { title } = Astro.props;
19 |
20 | const body_class = /\/docs\/(svelte|react|solid|vanilla|vue)/gi.exec(Astro.url.pathname)?.[1];
21 | ---
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
54 |
55 |
56 |
57 |
58 | {title}
59 |
60 |
77 |
78 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/docs/src/layouts/MainDocsLayout.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import type { MarkdownLayoutProps } from 'astro';
3 |
4 | import DocsLayout from './DocsLayout.astro';
5 |
6 | import type { Framework } from '$helpers/constants';
7 | import SIZES from '../data/sizes.json';
8 |
9 | type Props = MarkdownLayoutProps<{
10 | title: string;
11 | tagline: string;
12 | }>;
13 |
14 | const { frontmatter } = Astro.props;
15 | const { tagline, title, url } = frontmatter;
16 |
17 | const framework = url?.split('/').at(-1) as Framework;
18 | const { size, version } = SIZES[framework];
19 | ---
20 |
21 |
22 |
23 | @neodrag/{framework}
24 |
25 |
26 |
27 |
28 | {version}
29 |
30 |
31 |
32 | {size}KB
33 |
34 |
35 |
36 | {tagline}
37 |
38 |
39 |
40 | Credits
41 |
42 | Inspired from the amazing
43 | react-draggable
44 | library, and implements the same API.
45 |
46 |
47 |
48 |
67 |
--------------------------------------------------------------------------------
/docs/src/layouts/ThemeWatcher.svelte:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/docs/src/pages/docs/index.astro:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/docs/src/pages/docs/migrating/svelte-drag.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | layout: '$layouts/DocsLayout.astro'
3 | title: 'Migrating svelte-drag to neodrag'
4 | ---
5 |
6 | # svelte-drag to neodrag
7 |
8 | ## Installing
9 |
10 | ```bash
11 | npm uninstall svelte-drag
12 | npm install @neodrag/svelte
13 | ```
14 |
15 | Change the imports
16 |
17 | ```diff
18 | - import { draggable } from 'svelte-drag'
19 | + import { draggable } from '@neodrag/svelte'
20 | ```
21 |
22 | ## Options
23 |
24 | Options passed to the object are still the same as latest version of svelte-drag. No changes there
25 |
26 | ## Events
27 |
28 | Events have been renamed
29 |
30 | `on:svelte-drag` -> `on:neodrag`
31 |
32 | `on:svelte-drag:start` -> `on:neodrag:start`
33 |
34 | `on:svelte-drag:end` -> `on:neodrag:end`
35 |
--------------------------------------------------------------------------------
/docs/src/pages/docs/vue.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | layout: '$layouts/MainDocsLayout.astro'
3 | title: '@neodrag/vue'
4 | tagline: 'A lightweight directive to make your elements draggable.'
5 | ---
6 |
7 | import ExportedTypesMDX from '../../documentation/exported-types.mdx';
8 |
9 | import Options from '$components/options/Options.astro';
10 |
11 | ```sh
12 | pnpm add @neodrag/vue
13 |
14 | # or
15 |
16 | bun install @neodrag/vue
17 |
18 | # or
19 |
20 | npm install @neodrag/vue
21 | ```
22 |
23 | ## Usage
24 |
25 | Basic usage
26 |
27 | ```vue
28 |
31 |
32 |
33 | I am draggable
34 |
35 | ```
36 |
37 | With options
38 |
39 | ```vue
40 |
43 |
44 |
45 | I am draggable
46 |
47 | ```
48 |
49 | Defining options elsewhere with typescript
50 |
51 | ```vue
52 |
60 |
61 |
62 | I am draggable
63 |
64 | ```
65 |
66 | ## Options
67 |
68 |
69 |
70 | ## Events
71 |
72 | `@neodrag/vue` emits 3 events, `onDrag`, `onDragStart` & `onDragEnd`.
73 | Example:
74 |
75 | ```vue
76 |
77 |
84 | Hello
85 |
86 |
87 | ```
88 |
89 | ## TypeScript
90 |
91 | This library ships with proper TypeScript typings, for the best Developer Experience, whether authoring JS or TS.
92 |
93 |
94 |
95 | ## Controlled vs Uncontrolled
96 |
97 | This is taken straight from React's philosophy(After all, this package is inspired from [react-draggable](https://github.com/react-grid-layout/react-draggable)).
98 |
99 | Uncontrolled means your app doesn't control the dragging of the app. Meaning, the user drags the element, it changes position, and you do something with that action. You yourself don't change position of the element or anything. This is the default behavior of this library.
100 |
101 | Controlled means your app, using state variables, changes the position of the element, or in simple terms, programmatically drag the element. You basically set the `position` property to `{ x: 10, y: 50 }`(or any other numbers), and voila! yur now controlling the position of the element programmatically 🥳🥳
102 |
103 | OFC, this library doesn't go fully **Controlled**. The user can still drag it around even when `position` is set.
104 |
105 | So, when you change `position`, the element position changes. However, when the element is dragged by user interaction, `position` is not changed. This is done intentionally, as two-way data binding here isn't possible and also will lead to unexpected behavior. To keep the `position` variable up to date, use the `onDrag` event to keep your state up to date to the draggable's internal state.
106 |
107 | To have it be strictly **Controlled**, meaning it can only be moved programmatically, add the `disabled` option to your draggable element's config
108 |
109 | ```vue
110 |
111 | ```
112 |
--------------------------------------------------------------------------------
/docs/src/state/auto-destroy-effect-root.svelte.ts:
--------------------------------------------------------------------------------
1 | import { onDestroy } from 'svelte';
2 |
3 | /**
4 | * Behaves the same as `$effect.root`, but automatically
5 | * cleans up the effect inside Svelte components.
6 | *
7 | * @returns Cleanup function to manually cleanup the effect.
8 | */
9 | export function auto_destroy_effect_root(fn: () => void | VoidFunction) {
10 | let cleanup: VoidFunction | null = $effect.root(fn);
11 |
12 | function destroy() {
13 | if (cleanup === null) {
14 | return;
15 | }
16 |
17 | cleanup();
18 | cleanup = null;
19 | }
20 |
21 | try {
22 | onDestroy(destroy);
23 | } catch {
24 | // Ignore the error. The user is responsible for manually
25 | // cleaning up effects created outside Svelte components.
26 | }
27 |
28 | return destroy;
29 | }
30 |
--------------------------------------------------------------------------------
/docs/src/state/persisted.svelte.ts:
--------------------------------------------------------------------------------
1 | import { browser } from '$helpers/utils.ts';
2 | import { auto_destroy_effect_root } from './auto-destroy-effect-root.svelte.ts';
3 |
4 | type Primitive = string | null | symbol | boolean | number | undefined | bigint;
5 |
6 | const is_primitive = (val: any): val is Primitive => {
7 | return val !== Object(val) || val === null;
8 | };
9 |
10 | function get_value_from_storage(key: string) {
11 | const value = localStorage.getItem(key);
12 |
13 | if (value === null) return { found: false, value: null };
14 |
15 | try {
16 | return {
17 | found: true,
18 | value: JSON.parse(value),
19 | };
20 | } catch (e) {
21 | console.error(`Error when parsing ${value} from persisted store "${key}"`, e);
22 | return {
23 | found: false,
24 | value: null,
25 | };
26 | }
27 | }
28 |
29 | export function persisted(key: string, initial: T) {
30 | const existing = browser ? localStorage.getItem(key) : JSON.stringify(initial);
31 |
32 | const primitive = is_primitive(initial);
33 | const parsed_value = existing ? JSON.parse(existing) : initial;
34 |
35 | let state = $state(
36 | primitive ? { current: parsed_value } : parsed_value,
37 | );
38 |
39 | auto_destroy_effect_root(() => {
40 | $effect(() => {
41 | const controller = new AbortController();
42 |
43 | addEventListener(
44 | 'storage',
45 | (event) => {
46 | if (event.key === key) {
47 | const val = get_value_from_storage(key);
48 | if (val.found) {
49 | state = primitive ? { current: val.value } : val.value;
50 | }
51 | }
52 | },
53 | { signal: controller.signal },
54 | );
55 |
56 | return () => controller.abort();
57 | });
58 |
59 | $effect(() => {
60 | localStorage.setItem(
61 | key,
62 | // @ts-ignore
63 | JSON.stringify(primitive ? state.current : state),
64 | );
65 | });
66 | });
67 |
68 | return state;
69 | }
70 |
--------------------------------------------------------------------------------
/docs/src/state/user-preferences.svelte.ts:
--------------------------------------------------------------------------------
1 | import { browser } from '$helpers/utils';
2 | import { persisted } from './persisted.svelte';
3 |
4 | export type Theme = 'light' | 'dark';
5 | export const theme = persisted(
6 | 'neodrag:theme',
7 | browser ? (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : 'light',
8 | );
9 |
--------------------------------------------------------------------------------
/docs/src/worklet/squircle.js:
--------------------------------------------------------------------------------
1 | const drawSquircle = (ctx, geom, radius, smooth, lineWidth, color) => {
2 | const defaultFill = color;
3 | const lineWidthOffset = lineWidth / 2;
4 | // OPEN LEFT-TOP CORNER
5 | ctx.beginPath();
6 | ctx.lineTo(radius, lineWidthOffset);
7 | // TOP-RIGHT CORNER
8 | ctx.lineTo(geom.width - radius, lineWidthOffset);
9 | ctx.bezierCurveTo(
10 | geom.width - radius / smooth,
11 | lineWidthOffset, // first bezier point
12 | geom.width - lineWidthOffset,
13 | radius / smooth, // second bezier point
14 | geom.width - lineWidthOffset,
15 | radius, // last connect point
16 | );
17 | // BOTTOM-RIGHT CORNER
18 | ctx.lineTo(geom.width - lineWidthOffset, geom.height - radius);
19 | ctx.bezierCurveTo(
20 | geom.width - lineWidthOffset,
21 | geom.height - radius / smooth, // first bezier point
22 | geom.width - radius / smooth,
23 | geom.height - lineWidthOffset, // second bezier point
24 | geom.width - radius,
25 | geom.height - lineWidthOffset, // last connect point
26 | );
27 | // BOTTOM-LEFT CORNER
28 | ctx.lineTo(radius, geom.height - lineWidthOffset);
29 | ctx.bezierCurveTo(
30 | radius / smooth,
31 | geom.height - lineWidthOffset, // first bezier point
32 | lineWidthOffset,
33 | geom.height - radius / smooth, // second bezier point
34 | lineWidthOffset,
35 | geom.height - radius, // last connect point
36 | );
37 | // CLOSE LEFT-TOP CORNER
38 | ctx.lineTo(lineWidthOffset, radius);
39 | ctx.bezierCurveTo(
40 | lineWidthOffset,
41 | radius / smooth, // first bezier point
42 | radius / smooth,
43 | lineWidthOffset, // second bezier point
44 | radius,
45 | lineWidthOffset, // last connect point
46 | );
47 | ctx.closePath();
48 |
49 | if (lineWidth) {
50 | // console.log(lineWidth);
51 | ctx.strokeStyle = defaultFill;
52 | ctx.lineWidth = lineWidth;
53 | ctx.stroke();
54 | } else {
55 | ctx.fillStyle = defaultFill;
56 | ctx.fill();
57 | }
58 | };
59 |
60 | if (typeof registerPaint !== 'undefined') {
61 | class SquircleClass {
62 | static get contextOptions() {
63 | return { alpha: true };
64 | }
65 | static get inputProperties() {
66 | return [
67 | '--squircle-radius',
68 | '--squircle-smooth',
69 | '--squircle-outline',
70 | '--squircle-fill',
71 | '--squircle-ratio',
72 | ];
73 | }
74 |
75 | paint(ctx, geom, properties) {
76 | const customRatio = properties.get('--squircle-ratio');
77 | const smoothRatio = 10;
78 | const distanceRatio = parseFloat(customRatio) ? parseFloat(customRatio) : 1.8;
79 | const squircleSmooth = parseFloat(properties.get('--squircle-smooth') * smoothRatio);
80 | const squircleRadius = parseInt(properties.get('--squircle-radius'), 10) * distanceRatio;
81 | const squrcleOutline = parseFloat(properties.get('--squircle-outline'), 10);
82 | const squrcleColor = properties.get('--squircle-fill').toString().replace(/\s/g, '');
83 |
84 | const isSmooth = () => {
85 | if (typeof properties.get('--squircle-smooth')[0] !== 'undefined') {
86 | if (squircleSmooth === 0) {
87 | return 1;
88 | }
89 | return squircleSmooth;
90 | } else {
91 | return 10;
92 | }
93 | };
94 |
95 | const isOutline = () => {
96 | if (squrcleOutline) {
97 | return squrcleOutline;
98 | } else {
99 | return 0;
100 | }
101 | };
102 |
103 | const isColor = () => {
104 | if (squrcleColor) {
105 | return squrcleColor;
106 | } else {
107 | return '#f45';
108 | }
109 | };
110 |
111 | if (squircleRadius < geom.width / 2 && squircleRadius < geom.height / 2) {
112 | drawSquircle(ctx, geom, squircleRadius, isSmooth(), isOutline(), isColor());
113 | } else {
114 | drawSquircle(
115 | ctx,
116 | geom,
117 | Math.min(geom.width / 2, geom.height / 2),
118 | isSmooth(),
119 | isOutline(),
120 | isColor(),
121 | );
122 | }
123 | }
124 | }
125 |
126 | // eslint-disable-next-line no-undef
127 | registerPaint('squircle', SquircleClass);
128 | }
129 |
--------------------------------------------------------------------------------
/docs/svelte.config.js:
--------------------------------------------------------------------------------
1 | import { vitePreprocess } from '@astrojs/svelte';
2 |
3 | export default {
4 | preprocess: vitePreprocess(),
5 | };
6 |
--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "astro/tsconfigs/strict",
3 | "compilerOptions": {
4 | "strictNullChecks": true,
5 | "baseUrl": ".",
6 | "paths": {
7 | "$layouts/*": ["src/layouts/*"],
8 | "$components/*": ["src/components/*"],
9 | "$state/*": ["src/state/*"],
10 | "$actions/*": ["src/actions/*"],
11 | "$helpers/*": ["src/helpers/*"],
12 | "$/*": ["src/*"]
13 | }
14 | },
15 | "include": [".astro/types.d.ts", "**/*"],
16 | "exclude": ["dist"]
17 | }
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "neodrag",
3 | "version": "1.0.1",
4 | "private": true,
5 | "type": "module",
6 | "scripts": {
7 | "changeset": "changeset",
8 | "compile": "pnpm -r compile",
9 | "docs:build": "pnpm compile && pnpm sizes && cd docs && pnpm build",
10 | "ci:version": "changeset version",
11 | "ci:release": "changeset publish",
12 | "sizes": "tsx scripts/gather-sizes.ts"
13 | },
14 | "devDependencies": {
15 | "@changesets/changelog-github": "^0.5.0",
16 | "@changesets/cli": "^2.27.1",
17 | "@sveltejs/vite-plugin-svelte": "3.1.0",
18 | "@testing-library/jest-dom": "^6.1.5",
19 | "@testing-library/svelte": "^4.0.5",
20 | "@types/react": "^18.2.79",
21 | "@types/react-dom": "^18.2.25",
22 | "brotli-size": "^4.0.0",
23 | "fast-glob": "^3.3.2",
24 | "jsdom": "^24.0.0",
25 | "react": "^18.2.0",
26 | "react-dom": "^18.2.0",
27 | "solid-js": "^1.8.7",
28 | "svelte": "^4.2.14",
29 | "terser": "^5.26.0",
30 | "tsup": "^8.0.1",
31 | "tsx": "^4.6.2",
32 | "typescript": "^5.4.5",
33 | "vite": "^5.2.9",
34 | "vitest": "^1.5.0",
35 | "vue": "^3.4.23"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/config/index.ts:
--------------------------------------------------------------------------------
1 | import { Format } from 'tsup';
2 | import { defineConfig } from 'tsup';
3 |
4 | export const core_config = ({ dtsBanner = '', includeUMD = false, globalName = 'neodrag' } = {}) =>
5 | defineConfig([
6 | {
7 | entry: ['./src/index.ts'],
8 | format: 'esm',
9 | external: ['vue', 'react', 'solid-js', 'svelte'],
10 | dts: { resolve: true, banner: dtsBanner },
11 | clean: true,
12 | treeshake: 'smallest',
13 | },
14 | {
15 | entry: ['./src/index.ts'],
16 | minify: 'terser',
17 | external: ['vue', 'react', 'solid-js', 'svelte'],
18 | format: 'esm',
19 | clean: true,
20 | outDir: 'dist/min',
21 | treeshake: 'smallest',
22 | },
23 | // UMD configuration
24 | ...(includeUMD
25 | ? [
26 | {
27 | entry: ['./src/index.ts'],
28 | format: 'umd' as Format,
29 | globalName,
30 | dts: { resolve: true, banner: dtsBanner },
31 | clean: true,
32 | outDir: 'dist/umd',
33 | treeshake: true,
34 | },
35 | ]
36 | : []),
37 | ]);
38 |
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@neodrag/core",
3 | "private": "true",
4 | "version": "2.3.0",
5 | "main": "./dist/index.js",
6 | "module": "./dist/index.js",
7 | "type": "module",
8 | "types": "./dist/index.d.ts",
9 | "files": [
10 | "dist/*"
11 | ],
12 | "sideEffects": false,
13 | "exports": {
14 | ".": {
15 | "types": "./dist/index.d.ts",
16 | "import": {
17 | "production": "./dist/index.js",
18 | "development": "./dist/index.js"
19 | },
20 | "default": "./dist/index.js"
21 | },
22 | "./package.json": "./package.json"
23 | },
24 | "scripts": {
25 | "compile:watch": "tsup --watch",
26 | "compile": "tsup"
27 | },
28 | "repository": {
29 | "type": "git",
30 | "url": "git+https://github.com/PuruVJ/neodrag.git"
31 | },
32 | "author": "Puru Vijay",
33 | "license": "MIT"
34 | }
35 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "ES2020",
4 | "target": "ESNext",
5 | "declaration": true,
6 | "emitDeclarationOnly": true,
7 | "declarationDir": "dist/",
8 | "strict": true,
9 | "esModuleInterop": true,
10 | "moduleResolution": "bundler",
11 | "isolatedDeclarations": true,
12 | "types": ["vitest/globals"]
13 | },
14 | "files": ["./src/index.ts"]
15 | }
16 |
--------------------------------------------------------------------------------
/packages/core/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { core_config } from '../config';
2 |
3 | export default core_config;
4 |
--------------------------------------------------------------------------------
/packages/react/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | @neodrag/react
7 |
8 |
9 |
10 | One draggable to rule em all
11 |
12 |
13 | A lightweight React hook to make your elements draggable.
14 |
15 |
16 |
17 |
18 |
19 |
Getting Started
20 |
21 | # Features
22 |
23 | - 🤏 Tiny - Only 1.95KB min+brotli.
24 | - 🐇 Simple - Quite simple to use, and effectively no-config required!
25 | - 🧙♀️ Elegant - React hook, to keep the usage simple, elegant and expressive.
26 | - 🗃️ Highly customizable - Offers tons of options that you can modify to get different behavior.
27 | - ⚛️ Reactive - Change options passed to it on the fly, it will **just work 🙂**
28 |
29 | # Installing
30 |
31 | ```bash
32 | pnpm add @neodrag/react
33 |
34 | # npm
35 | npm install @neodrag/react
36 |
37 | # yarn
38 | yarn add @neodrag/react
39 | ```
40 |
41 | # Usage
42 |
43 | Basic usage
44 |
45 | ```tsx
46 | import { useDraggable } from '@neodrag/react';
47 |
48 | function App() {
49 | const draggableRef = useRef(null);
50 | useDraggable(draggableRef);
51 |
52 | return Hello
;
53 | }
54 | ```
55 |
56 | With options
57 |
58 | ```tsx
59 | import { useDraggable } from '@neodrag/react';
60 |
61 | function App() {
62 | const draggableRef = useRef(null);
63 | useDraggable(draggableRef, { axis: 'x', grid: [10, 10] });
64 |
65 | return Hello
;
66 | }
67 | ```
68 |
69 | Defining options elsewhere with typescript
70 |
71 | ```tsx
72 | import { useDraggable, type DragOptions } from '@neodrag/react';
73 |
74 | function App() {
75 | const draggableRef = useRef(null);
76 |
77 | const options: DragOptions = {
78 | axis: 'y',
79 | bounds: 'parent',
80 | };
81 |
82 | useDraggable(draggableRef, options);
83 |
84 | return Hello
;
85 | }
86 | ```
87 |
88 | Getting state
89 |
90 | ```tsx
91 | import { useDraggable } from '@neodrag/react';
92 |
93 | function App() {
94 | const draggableRef = useRef(null);
95 |
96 | const { isDragging, dragState } = useDraggable(draggableRef);
97 |
98 | useEffect(() => {
99 | console.log({ isDragging, dragState });
100 | }, [isDragging, dragState]);
101 |
102 | return Hello
;
103 | }
104 | ```
105 |
106 | `dragState` is of type:
107 |
108 | ```ts
109 | {
110 | /** Distance of element from original position on x-axis */
111 | offsetX: number,
112 |
113 | /** Distance of element from original position on y-axis */
114 | offsetY: number,
115 |
116 | /** element.getBoundingClientRect() result */
117 | domRect: DOMRect,
118 | }
119 | ```
120 |
121 | Read the docs
122 |
123 | ## Credits
124 |
125 | Inspired from the amazing [react-draggable](https://github.com/react-grid-layout/react-draggable) library, and implements even more features with similar API, but 3.7x smaller.
126 |
127 | # License
128 |
129 | MIT License © Puru Vijay
130 |
--------------------------------------------------------------------------------
/packages/react/demo/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 |
--------------------------------------------------------------------------------
/packages/react/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/react/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "private": true,
4 | "version": "0.0.1",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "vite build",
8 | "preview": "vite preview"
9 | },
10 | "dependencies": {
11 | "@neodrag/react": "workspace:*"
12 | },
13 | "devDependencies": {
14 | "@vitejs/plugin-react": "^4.2.1",
15 | "typescript": "^5.3.3",
16 | "vite": "^5.0.10"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/react/demo/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useDraggable } from '@neodrag/react';
2 | import { useRef, useState } from 'react';
3 |
4 | function App() {
5 | const [position, setPosition] = useState({ x: 0, y: 0 });
6 |
7 | const draggableRef = useRef(null);
8 |
9 | useDraggable(draggableRef, {
10 | onDrag: ({ offsetX, offsetY }) => setPosition({ x: offsetX, y: offsetY }),
11 | });
12 |
13 | return (
14 | <>
15 | I can be moved with the slider too
16 | X:
17 | setPosition({ x: +e.target.value, y: position.y })}
23 | />
24 | Y:
25 | setPosition({ x: position.x, y: +e.target.value })}
31 | />
32 | >
33 | );
34 | }
35 |
36 | export default App;
37 |
--------------------------------------------------------------------------------
/packages/react/demo/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/packages/react/demo/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import './index.css'
4 | import App from './App'
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | )
12 |
--------------------------------------------------------------------------------
/packages/react/demo/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/packages/react/demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": false,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["./src"]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/react/demo/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | });
8 |
--------------------------------------------------------------------------------
/packages/react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@neodrag/react",
3 | "version": "2.3.0",
4 | "description": "React library to add dragging to your apps 😉",
5 | "main": "./dist/index.js",
6 | "module": "./dist/index.js",
7 | "type": "module",
8 | "types": "./dist/index.d.ts",
9 | "files": [
10 | "dist/*"
11 | ],
12 | "sideEffects": false,
13 | "exports": {
14 | ".": {
15 | "types": "./dist/index.d.ts",
16 | "import": {
17 | "production": "./dist/min/index.js",
18 | "development": "./dist/index.js"
19 | },
20 | "default": "./dist/min/index.js"
21 | },
22 | "./package.json": "./package.json"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "git+https://github.com/PuruVJ/neodrag.git"
27 | },
28 | "keywords": [
29 | "draggable",
30 | "react",
31 | "react-draggable",
32 | "drag",
33 | "neodrag",
34 | "preact",
35 | "small",
36 | "tiny",
37 | "performant",
38 | "neodrag"
39 | ],
40 | "author": "Puru Vijay",
41 | "license": "MIT",
42 | "bugs": {
43 | "url": "https://github.com/PuruVJ/neodrag/issues"
44 | },
45 | "homepage": "https://github.com/PuruVJ/neodrag/tree/main/packages/react#readme",
46 | "scripts": {
47 | "compile": "tsup",
48 | "compile:watch": "tsup --watch",
49 | "pub": "pnpm compile && pnpm publish --no-git-checks --access public",
50 | "pub:dry": "pnpm compile && pnpm publish --dry-run --no-git-checks --access public"
51 | },
52 | "devDependencies": {
53 | "@neodrag/core": "workspace:*"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/packages/react/src/index.ts:
--------------------------------------------------------------------------------
1 | import { DragEventData, draggable, DragOptions } from '@neodrag/core';
2 | import React, { useEffect, useRef, useState } from 'react';
3 |
4 | type DragState = DragEventData;
5 |
6 | type HandleCancelType =
7 | | string
8 | | HTMLElement
9 | | React.RefObject
10 | | (React.RefObject | HTMLElement)[]
11 | | undefined;
12 |
13 | function unwrap_handle_cancel(
14 | val: HandleCancelType,
15 | ): string | HTMLElement | HTMLElement[] | undefined {
16 | if (val == undefined || typeof val === 'string' || val instanceof HTMLElement) return val;
17 | if ('current' in val) return val.current!;
18 |
19 | if (Array.isArray(val)) {
20 | // It can only be an array now
21 | return val.map((v) => (v instanceof HTMLElement ? v : v.current!));
22 | }
23 | }
24 |
25 | type ReactDragOptions = Omit & {
26 | handle?: HandleCancelType;
27 | cancel?: HandleCancelType;
28 | };
29 |
30 | export function useDraggable(
31 | nodeRef: React.RefObject,
32 | options: ReactDragOptions = {},
33 | ) {
34 | const update_ref = useRef<(options: DragOptions) => void>();
35 |
36 | const [isDragging, set_is_dragging] = useState(false);
37 | const [dragState, set_drag_state] = useState();
38 |
39 | let { onDragStart, onDrag, onDragEnd, handle, cancel } = options;
40 |
41 | let new_handle = unwrap_handle_cancel(handle);
42 | let new_cancel = unwrap_handle_cancel(cancel);
43 |
44 | function call_event(arg: DragState, cb: DragOptions['onDrag']) {
45 | set_drag_state(arg);
46 | cb?.(arg);
47 | }
48 |
49 | function custom_on_drag_start(arg: DragState) {
50 | set_is_dragging(true);
51 | call_event(arg, onDragStart);
52 | }
53 |
54 | function custom_on_drag(arg: DragState) {
55 | call_event(arg, onDrag);
56 | }
57 |
58 | function custom_on_drag_end(arg: DragState) {
59 | set_is_dragging(false);
60 | call_event(arg, onDragEnd);
61 | }
62 |
63 | useEffect(() => {
64 | if (typeof window === 'undefined') return;
65 | const node = nodeRef.current;
66 | if (!node) return;
67 |
68 | // Update callbacks
69 | ({ onDragStart, onDrag, onDragEnd } = options);
70 |
71 | const { update, destroy } = draggable(node, {
72 | ...options,
73 | handle: new_handle,
74 | cancel: new_cancel,
75 | onDragStart: custom_on_drag_start,
76 | onDrag: custom_on_drag,
77 | onDragEnd: custom_on_drag_end,
78 | });
79 |
80 | update_ref.current = update;
81 |
82 | return destroy;
83 | }, []);
84 |
85 | useEffect(() => {
86 | update_ref.current?.({
87 | ...options,
88 | handle: unwrap_handle_cancel(handle),
89 | cancel: unwrap_handle_cancel(cancel),
90 | onDragStart: custom_on_drag_start,
91 | onDrag: custom_on_drag,
92 | onDragEnd: custom_on_drag_end,
93 | });
94 | }, [options]);
95 |
96 | return { isDragging, dragState };
97 | }
98 |
99 | export type { DragAxis, DragBounds, DragBoundsCoords, DragEventData } from '@neodrag/core';
100 | export type { ReactDragOptions as DragOptions };
101 |
--------------------------------------------------------------------------------
/packages/react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "declarationDir": "./dist",
6 | "declaration": true,
7 | "emitDeclarationOnly": true,
8 | "lib": ["DOM", "ESNext"],
9 | "allowJs": false,
10 | "skipLibCheck": false,
11 | "esModuleInterop": false,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "ESNext",
16 | "moduleResolution": "Node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "jsx": "react-jsx"
20 | },
21 | "include": ["./src"]
22 | }
23 |
--------------------------------------------------------------------------------
/packages/react/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { core_config } from '../config';
2 |
3 | export default core_config({});
4 |
--------------------------------------------------------------------------------
/packages/solid/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @neodrag/solid
2 |
3 | ## 2.3.0
4 |
5 | ### Minor Changes
6 |
7 | - [#186](https://github.com/PuruVJ/neodrag/pull/186) [`a917373`](https://github.com/PuruVJ/neodrag/commit/a917373e56378ae9443f3162e428abc8c058b191) Thanks [@PuruVJ](https://github.com/PuruVJ)! - feat: Expose event: PointerEvent
8 |
9 | ## 2.2.0
10 |
11 | ### Minor Changes
12 |
13 | - [#176](https://github.com/PuruVJ/neodrag/pull/176) [`0cead87`](https://github.com/PuruVJ/neodrag/commit/0cead8701f132670bd5618ceeb8fdee8e9a3ad27) Thanks [@PuruVJ](https://github.com/PuruVJ)! - deprecate: legacyTranslate and gpuAcceleration
14 |
15 | ## 2.1.0
16 |
17 | ### Minor Changes
18 |
19 | - [#174](https://github.com/PuruVJ/neodrag/pull/174) [`45d9eeb`](https://github.com/PuruVJ/neodrag/commit/45d9eeb375b18eb0530cc079613dcdc21cce81d4) Thanks [@PuruVJ](https://github.com/PuruVJ)! - feat: threshold option
20 |
21 | - [#172](https://github.com/PuruVJ/neodrag/pull/172) [`ac0e10b`](https://github.com/PuruVJ/neodrag/commit/ac0e10bf287b3577fb926d6ba585e906abeaab72) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Use AbortSignal for unregistering event listeners
22 |
23 | ### Patch Changes
24 |
25 | - [#174](https://github.com/PuruVJ/neodrag/pull/174) [`45d9eeb`](https://github.com/PuruVJ/neodrag/commit/45d9eeb375b18eb0530cc079613dcdc21cce81d4) Thanks [@PuruVJ](https://github.com/PuruVJ)! - fix: drag end no longer causes onclick to trigger.
26 |
27 | ## 2.0.4
28 |
29 | ### Patch Changes
30 |
31 | - [#145](https://github.com/PuruVJ/neodrag/pull/145) [`e19ce73`](https://github.com/PuruVJ/neodrag/commit/e19ce732a9494dc3eb05e0c8702cd802abc0af9a) Thanks [@PuruVJ](https://github.com/PuruVJ)! - fix: ignoreMultitouch now behaves correctly
32 |
33 | ## 2.0.3
34 |
35 | ### Patch Changes
36 |
37 | - [#111](https://github.com/PuruVJ/neodrag/pull/111) [`b736fa6`](https://github.com/PuruVJ/neodrag/commit/b736fa689e06491e348638311900900e35342e6e) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Fix finding elements inside shadow DOM, not just the root
38 |
39 | ## 2.0.2
40 |
41 | ### Patch Changes
42 |
43 | - [#108](https://github.com/PuruVJ/neodrag/pull/108) [`0e6a36a`](https://github.com/PuruVJ/neodrag/commit/0e6a36a8ab1be01b97d8604dbc931c6e7ce4f16b) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Fix usage inside Shadow DOM
44 |
45 | ## 2.0.1
46 |
47 | ### Patch Changes
48 |
49 | - Fix canMoveInY bug in `dragEnd` function
50 |
51 | ## 2.0.0
52 |
53 | ### Patch Changes
54 |
55 | - [#95](https://github.com/PuruVJ/neodrag/pull/95) [`3c10f6ae`](https://github.com/PuruVJ/neodrag/commit/3c10f6ae377c3e9fc9fea963ea99204a4649806c) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Add `legacyTranslate` option, remove memoization code, align to browser's RAF throttling
56 |
57 | - [#97](https://github.com/PuruVJ/neodrag/pull/97) [`9e5c4647`](https://github.com/PuruVJ/neodrag/commit/9e5c46477c7781bc75a57944983434a0c8ceff77) Thanks [@PuruVJ](https://github.com/PuruVJ)! - New output formats
58 |
59 | - [`da98e910`](https://github.com/PuruVJ/neodrag/commit/da98e910469d63e53e2462e74196bad3b90ea053) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Expose rootNode and currentNode from events. Remove node and domRect
60 |
61 | - [#99](https://github.com/PuruVJ/neodrag/pull/99) [`a1572bce`](https://github.com/PuruVJ/neodrag/commit/a1572bce5186051a5114dd580017a49fc2b3c7fc) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Add transform function
62 |
63 | - [`8dd0d88f`](https://github.com/PuruVJ/neodrag/commit/8dd0d88ff0458c0bd6d20e3649371fdf732c9ebb) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Add recomputeBounds option
64 |
65 | - [`ca8cde25`](https://github.com/PuruVJ/neodrag/commit/ca8cde252e555cc50a0919a295d01ec340207f8e) Thanks [@PuruVJ](https://github.com/PuruVJ)! Expose rootNode and currentNode from events. Remove node and domRect
66 |
67 | - [`2ea2bad4`](https://github.com/PuruVJ/neodrag/commit/2ea2bad4f16e798fb0ecb55f8554efcd2e50ca26) Thanks [@PuruVJ](https://github.com/PuruVJ)! Fix ouble click issue
68 |
69 | - Fix behavior when snap provided as 0
70 |
71 | ## 1.0.0
72 |
73 | Skipping for 2.0.0
74 |
75 | ## 0.1.1
76 |
77 | ### Patch Changes
78 |
79 | - Fix snapping while scaled
80 |
--------------------------------------------------------------------------------
/packages/solid/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | @neodrag/solid
7 |
8 |
9 |
10 | One draggable to rule em all
11 |
12 |
13 | A lightweight SolidJS directive to make your elements draggable.
14 |
15 |
16 |
17 |
18 |
19 |
Getting Started
20 |
21 | # Features
22 |
23 | - 🤏 Tiny - Only 1.75KB min+brotli.
24 | - 🐇 Simple - Quite simple to use, and effectively no-config required!
25 | - 🧙♀️ Elegant - SolidJS directive, to keep the usage simple, elegant and straightforward.
26 | - 🗃️ Highly customizable - Offers tons of options that you can modify to get different behavior.
27 | - ⚛️ Reactive - Change options passed to it on the fly, it will **just work 🙂**
28 |
29 | # Installing
30 |
31 | ```bash
32 | pnpm add @neodrag/solid
33 |
34 | # npm
35 | npm install @neodrag/solid
36 |
37 | # yarn
38 | yarn add @neodrag/solid
39 | ```
40 |
41 | # Usage
42 |
43 | Basic usage
44 |
45 | ```tsx
46 | import { createDraggable } from '@neodrag/solid';
47 |
48 | export const App: Component = () => {
49 | const { draggable } = createDraggable();
50 |
51 | return You can drag me
;
52 | };
53 | ```
54 |
55 | With options
56 |
57 | ```tsx
58 | import { createDraggable } from '@neodrag/solid';
59 |
60 | const { draggable } = createDraggable();
61 |
62 | I am draggable
;
63 | ```
64 |
65 | Defining options elsewhere with typescript
66 |
67 | ```tsx
68 | import { createDraggable, type DragOptions } from '@neodrag/solid';
69 |
70 | const options: DragOptions = {
71 | axis: 'y',
72 | bounds: 'parent',
73 | };
74 |
75 | const { draggable } = createDraggable();
76 |
77 | I am draggable
;
78 | ```
79 |
80 | Reactive options:
81 |
82 | ```tsx
83 | import { createSignal } from 'solid-js';
84 | import { createDraggable } from '@neodrag/solid';
85 |
86 | const [options, setOptions] = createSignal({
87 | axis: 'y',
88 | bounds: 'parent',
89 | });
90 |
91 | I am draggable
;
92 |
93 | // You can update `options` with `setOptions` anytime and it'll change. Neodrag will automatically update to the new options 😉
94 | ```
95 |
96 | Read the docs
97 |
98 | ## Credits
99 |
100 | Inspired from the amazing [react-draggable](https://github.com/react-grid-layout/react-draggable) library, and implements even more features with a similar API, but 3.7x smaller.
101 |
102 | # License
103 |
104 | MIT License © Puru Vijay
105 |
--------------------------------------------------------------------------------
/packages/solid/demo/README.md:
--------------------------------------------------------------------------------
1 | ## Usage
2 |
3 | Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`.
4 |
5 | This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template.
6 |
7 | ```bash
8 | $ npm install # or pnpm install or yarn install
9 | ```
10 |
11 | ### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
12 |
13 | ## Available Scripts
14 |
15 | In the project directory, you can run:
16 |
17 | ### `npm dev` or `npm start`
18 |
19 | Runs the app in the development mode.
20 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
21 |
22 | The page will reload if you make edits.
23 |
24 | ### `npm run build`
25 |
26 | Builds the app for production to the `dist` folder.
27 | It correctly bundles Solid in production mode and optimizes the build for the best performance.
28 |
29 | The build is minified and the filenames include the hashes.
30 | Your app is ready to be deployed!
31 |
32 | ## Deployment
33 |
34 | You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.)
35 |
--------------------------------------------------------------------------------
/packages/solid/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Solid App
8 |
9 |
10 | You need to enable JavaScript to run this app.
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/solid/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "vite",
7 | "dev": "vite",
8 | "build": "vite build",
9 | "serve": "vite preview"
10 | },
11 | "license": "MIT",
12 | "devDependencies": {
13 | "typescript": "^5.3.3",
14 | "vite": "^5.0.10",
15 | "vite-plugin-solid": "^2.8.0"
16 | },
17 | "dependencies": {
18 | "@neodrag/solid": "workspace:*",
19 | "solid-js": "^1.8.7"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/solid/demo/src/App.module.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PuruVJ/neodrag/61e34331987a48a49accbe62370867248c35fc07/packages/solid/demo/src/App.module.css
--------------------------------------------------------------------------------
/packages/solid/demo/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { createDraggable } from '@neodrag/solid';
2 | import { Component, createSignal } from 'solid-js';
3 |
4 | const App: Component = () => {
5 | const { draggable } = createDraggable();
6 |
7 | const [position, setPosition] = createSignal({ x: 0, y: 0 });
8 |
9 | return (
10 | <>
11 | setPosition({ x: offsetX, y: offsetY }),
15 | }}
16 | >
17 | I can be moved with the slider too
18 |
19 | X:
20 | setPosition({ x: +e.target.value, y: position().y })}
26 | />
27 | Y:
28 | setPosition({ x: position().x, y: +e.target.value })}
34 | />
35 | >
36 | );
37 | };
38 |
39 | export default App;
40 |
--------------------------------------------------------------------------------
/packages/solid/demo/src/global.d.ts:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/packages/solid/demo/src/index.tsx:
--------------------------------------------------------------------------------
1 | /* @refresh reload */
2 | import { render } from 'solid-js/web';
3 |
4 | import App from './App';
5 |
6 | render(() => , document.getElementById('root') as HTMLElement);
7 |
--------------------------------------------------------------------------------
/packages/solid/demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "target": "ESNext",
5 | "module": "ESNext",
6 | "moduleResolution": "node",
7 | "allowSyntheticDefaultImports": true,
8 | "esModuleInterop": true,
9 | "jsx": "preserve",
10 | "jsxImportSource": "solid-js",
11 | "types": ["vite/client"],
12 | "noEmit": true,
13 | "isolatedModules": true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/solid/demo/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import solidPlugin from 'vite-plugin-solid';
3 |
4 | export default defineConfig({
5 | plugins: [solidPlugin()],
6 | build: {
7 | target: 'esnext',
8 | polyfillDynamicImport: false,
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/packages/solid/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@neodrag/solid",
3 | "version": "2.3.0",
4 | "description": "SolidJS library to add dragging to your apps 😉",
5 | "main": "./dist/index.js",
6 | "module": "./dist/index.js",
7 | "type": "module",
8 | "types": "./dist/index.d.ts",
9 | "files": [
10 | "dist/*"
11 | ],
12 | "sideEffects": false,
13 | "exports": {
14 | ".": {
15 | "types": "./dist/index.d.ts",
16 | "import": {
17 | "production": "./dist/min/index.js",
18 | "development": "./dist/index.js"
19 | },
20 | "default": "./dist/min/index.js"
21 | },
22 | "./package.json": "./package.json"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "git+https://github.com/PuruVJ/neodrag.git"
27 | },
28 | "keywords": [
29 | "draggable",
30 | "solid",
31 | "react-draggable",
32 | "drag",
33 | "neodrag",
34 | "small",
35 | "tiny",
36 | "performant",
37 | "neodrag"
38 | ],
39 | "author": "Puru Vijay",
40 | "license": "MIT",
41 | "bugs": {
42 | "url": "https://github.com/PuruVJ/neodrag/issues"
43 | },
44 | "homepage": "https://github.com/PuruVJ/neodrag/tree/main/packages/solid#readme",
45 | "scripts": {
46 | "compile": "tsup",
47 | "compile:watch": "tsup --watch",
48 | "pub": "pnpm compile && pnpm publish --no-git-checks --access public",
49 | "pub:dry": "pnpm compile && pnpm publish --dry-run --no-git-checks --access public"
50 | },
51 | "devDependencies": {
52 | "@neodrag/core": "workspace:*"
53 | },
54 | "peerDependencies": {
55 | "solid-js": "^1.0.0"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/packages/solid/src/index.ts:
--------------------------------------------------------------------------------
1 | import { draggable, type DragOptions } from '@neodrag/core';
2 | import { createEffect, onCleanup, type Accessor } from 'solid-js';
3 |
4 | export const createDraggable = () => ({
5 | draggable: (node: HTMLElement, options: Accessor) => {
6 | const { update, destroy } = draggable(node, options());
7 |
8 | onCleanup(destroy);
9 | createEffect(() => update(options()));
10 | },
11 | });
12 |
13 | export type {
14 | DragAxis,
15 | DragBounds,
16 | DragBoundsCoords,
17 | DragEventData,
18 | DragOptions,
19 | } from '@neodrag/core';
20 |
--------------------------------------------------------------------------------
/packages/solid/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "declarationDir": "./dist",
6 | "declaration": true,
7 | "emitDeclarationOnly": true,
8 | "lib": ["DOM", "ESNext"],
9 | "allowJs": false,
10 | "skipLibCheck": false,
11 | "esModuleInterop": false,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "ESNext",
16 | "moduleResolution": "Node",
17 | "resolveJsonModule": true,
18 | "preserveSymlinks": false
19 | },
20 | "include": ["./src"]
21 | }
22 |
--------------------------------------------------------------------------------
/packages/solid/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { core_config } from '../config';
2 |
3 | export default core_config({
4 | dtsBanner: `import 'solid-js';
5 |
6 | declare module 'solid-js' {
7 | namespace JSX {
8 | interface Directives {
9 | draggable: DragOptions;
10 | }
11 | }
12 | }
13 | `,
14 | });
15 |
--------------------------------------------------------------------------------
/packages/svelte/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | @neodrag/svelte
7 |
8 |
9 |
10 | One draggable to rule em all
11 |
12 |
13 | A lightweight Svelte action to make your elements draggable.
14 |
15 |
16 |
17 |
18 |
19 |
Getting Started
20 |
21 | # Features
22 |
23 | - 🤏 Tiny - Only 1.68KB min+brotli.
24 | - 🐇 Simple - Quite simple to use, and effectively no-config required!
25 | - 🧙♀️ Elegant - Svelte Action, to keep the usage simple, elegant and expressive.
26 | - 🗃️ Highly customizable - Offers tons of options that you can modify to get different behavior.
27 | - ⚛️ Reactive - Change options passed to it on the fly, it will **just work 🙂**
28 |
29 | [Try it in Svelte REPL](https://svelte.dev/repl/fc972f90450c4945b6f2481d13eafa00?version=3.38.3)
30 |
31 | # Installing
32 |
33 | ```bash
34 | pnpm add @neodrag/svelte
35 |
36 | # npm
37 | npm install @neodrag/svelte
38 |
39 | # yarn
40 | yarn add @neodrag/svelte
41 | ```
42 |
43 | # Migrating from svelte-drag
44 |
45 | svelte-drag is the predecessor of this package. To migrate, follow this short guide: [svelte-drag to @neodrag/svelte migration guide](https://www.neodrag.dev/docs/migrating/svelte-drag)
46 |
47 | # Usage
48 |
49 | Basic usage
50 |
51 | ```svelte
52 |
55 |
56 | Hello
57 | ```
58 |
59 | With options
60 |
61 | ```svelte
62 |
65 |
66 | Hello
67 | ```
68 |
69 | Defining options elsewhere with typescript
70 |
71 | ```svelte
72 |
80 |
81 | Hello
82 | ```
83 |
84 | Read the docs
85 |
86 | ## Credits
87 |
88 | Inspired from the amazing [react-draggable](https://github.com/react-grid-layout/react-draggable) library, and implements a similar API, but 3x smaller.
89 |
90 | # License
91 |
92 | MIT License © Puru Vijay
93 |
--------------------------------------------------------------------------------
/packages/svelte/demo/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /.svelte-kit
5 | /package
6 | .env
7 | .env.*
8 | !.env.example
9 |
--------------------------------------------------------------------------------
/packages/svelte/demo/README.md:
--------------------------------------------------------------------------------
1 | # create-svelte
2 |
3 | Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
4 |
5 | ## Creating a project
6 |
7 | If you're seeing this, you've probably already done this step. Congrats!
8 |
9 | ```bash
10 | # create a new project in the current directory
11 | npm create svelte@latest
12 |
13 | # create a new project in my-app
14 | npm create svelte@latest my-app
15 | ```
16 |
17 | ## Developing
18 |
19 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
20 |
21 | ```bash
22 | npm run dev
23 |
24 | # or start the server and open the app in a new browser tab
25 | npm run dev -- --open
26 | ```
27 |
28 | ## Building
29 |
30 | To create a production version of your app:
31 |
32 | ```bash
33 | npm run build
34 | ```
35 |
36 | You can preview the production build with `npm run preview`.
37 |
38 | > To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
39 |
--------------------------------------------------------------------------------
/packages/svelte/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "dev": "vite dev",
7 | "build": "vite build",
8 | "preview": "vite preview"
9 | },
10 | "devDependencies": {
11 | "@sveltejs/kit": "2.0.2",
12 | "svelte": "^4.2.8",
13 | "svelte-preprocess": "^5.1.3",
14 | "typescript": "^5.3.3",
15 | "vite": "^5.0.10"
16 | },
17 | "dependencies": {
18 | "@neodrag/svelte": "workspace:*"
19 | },
20 | "type": "module"
21 | }
22 |
--------------------------------------------------------------------------------
/packages/svelte/demo/src/app.d.ts:
--------------------------------------------------------------------------------
1 | // See https://kit.svelte.dev/docs/types#app
2 | // for information about these interfaces
3 | // and what to do when importing types
4 | declare namespace App {
5 | // interface Locals {}
6 | // interface PageData {}
7 | // interface Error {}
8 | // interface Platform {}
9 | }
10 |
--------------------------------------------------------------------------------
/packages/svelte/demo/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | %sveltekit.head%
8 |
9 |
10 | %sveltekit.body%
11 |
12 |
13 |
--------------------------------------------------------------------------------
/packages/svelte/demo/src/routes/2/+page.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | {
9 | rootNode.style.translate = `${offsetX}px ${offsetY}px 0`;
10 | },
11 | }}
12 | >
13 | Hello
14 |
15 |
16 |
17 |
Handle 1
18 |
Handle 2
19 |
20 |
--------------------------------------------------------------------------------
/packages/svelte/demo/svelte.config.js:
--------------------------------------------------------------------------------
1 | import preprocess from 'svelte-preprocess';
2 |
3 | /** @type {import('@sveltejs/kit').Config} */
4 | const config = {
5 | // Consult https://github.com/sveltejs/svelte-preprocess
6 | // for more information about preprocessors
7 | preprocess: preprocess(),
8 |
9 | kit: {},
10 | };
11 |
12 | export default config;
13 |
--------------------------------------------------------------------------------
/packages/svelte/demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true
12 | }
13 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
14 | //
15 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
16 | // from the referenced tsconfig.json - TypeScript does not merge them in
17 | }
18 |
--------------------------------------------------------------------------------
/packages/svelte/demo/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { sveltekit } from '@sveltejs/kit/vite';
2 | import type { UserConfig } from 'vite';
3 |
4 | const config: UserConfig = {
5 | plugins: [sveltekit()]
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/packages/svelte/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@neodrag/svelte",
3 | "version": "2.3.2",
4 | "description": "Svelte Action to add dragging to your apps 😉",
5 | "main": "./dist/index.js",
6 | "module": "./dist/index.js",
7 | "type": "module",
8 | "types": "./dist/index.d.ts",
9 | "files": [
10 | "dist/*"
11 | ],
12 | "sideEffects": false,
13 | "exports": {
14 | ".": {
15 | "types": "./dist/index.d.ts",
16 | "import": {
17 | "production": "./dist/min/index.js",
18 | "development": "./dist/index.js"
19 | },
20 | "default": "./dist/min/index.js",
21 | "svelte": "./dist/min/index.js"
22 | },
23 | "./package.json": "./package.json"
24 | },
25 | "scripts": {
26 | "test": "vitest run test",
27 | "test:watch": "vitest test",
28 | "compile:watch": "tsup --watch",
29 | "compile": "tsup ",
30 | "pub": "pnpm compile && pnpm publish --no-git-checks --access public",
31 | "pub:dry": "pnpm compile && pnpm publish --dry-run --no-git-checks --access public"
32 | },
33 | "repository": {
34 | "type": "git",
35 | "url": "git+https://github.com/PuruVJ/neodrag.git"
36 | },
37 | "keywords": [
38 | "draggable",
39 | "svelte",
40 | "react-draggable",
41 | "drag",
42 | "svelte",
43 | "small",
44 | "tiny",
45 | "performant",
46 | "neodrag"
47 | ],
48 | "author": "Puru Vijay",
49 | "license": "MIT",
50 | "bugs": {
51 | "url": "https://github.com/PuruVJ/neodrag/issues"
52 | },
53 | "devDependencies": {
54 | "@neodrag/core": "workspace:*"
55 | },
56 | "peerDependencies": {
57 | "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0"
58 | },
59 | "homepage": "https://neodrag.dev/docs/svelte"
60 | }
61 |
--------------------------------------------------------------------------------
/packages/svelte/src/index.ts:
--------------------------------------------------------------------------------
1 | import { draggable as core_draggable, type DragEventData, type DragOptions } from '@neodrag/core';
2 |
3 | //!THIS IS HACK, WE WANNA IMPORT THE TYPE WHEN THE ISSUE IS FIXED LATER
4 | /**
5 | * Actions can return an object containing the two properties defined in this interface. Both are optional.
6 | * - update: An action can have a parameter. This method will be called whenever that parameter changes,
7 | * immediately after Svelte has applied updates to the markup. `ActionReturn` and `ActionReturn` both
8 | * mean that the action accepts no parameters.
9 | * - destroy: Method that is called after the element is unmounted
10 | *
11 | * Additionally, you can specify which additional attributes and events the action enables on the applied element.
12 | * This applies to TypeScript typings only and has no effect at runtime.
13 | *
14 | * Example usage:
15 | * ```ts
16 | * interface Attributes {
17 | * newprop?: string;
18 | * 'on:event': (e: CustomEvent) => void;
19 | * }
20 | *
21 | * export function myAction(node: HTMLElement, parameter: Parameter): ActionReturn {
22 | * // ...
23 | * return {
24 | * update: (updatedParameter) => {...},
25 | * destroy: () => {...}
26 | * };
27 | * }
28 | * ```
29 | *
30 | * Docs: https://svelte.dev/docs/svelte-action
31 | */
32 | export interface ActionReturn<
33 | Parameter = undefined,
34 | Attributes extends Record = Record,
35 | > {
36 | update?: (parameter: Parameter) => void;
37 | destroy?: () => void;
38 | /**
39 | * ### DO NOT USE THIS
40 | * This exists solely for type-checking and has no effect at runtime.
41 | * Set this through the `Attributes` generic instead.
42 | */
43 | $$_attributes?: Attributes;
44 | }
45 |
46 | /**
47 | * Actions are functions that are called when an element is created.
48 | * You can use this interface to type such actions.
49 | * The following example defines an action that only works on `` elements
50 | * and optionally accepts a parameter which it has a default value for:
51 | * ```ts
52 | * export const myAction: Action
= (node, param = { someProperty: true }) => {
53 | * // ...
54 | * }
55 | * ```
56 | * `Action` and `Action` both signal that the action accepts no parameters.
57 | *
58 | * You can return an object with methods `update` and `destroy` from the function and type which additional attributes and events it has.
59 | * See interface `ActionReturn` for more details.
60 | *
61 | * Docs: https://svelte.dev/docs/svelte-action
62 | */
63 | export interface Action<
64 | Element = HTMLElement,
65 | Parameter = undefined,
66 | Attributes extends Record = Record,
67 | > {
68 | (
69 | ...args: undefined extends Parameter
70 | ? [node: Node, parameter?: Parameter]
71 | : [node: Node, parameter: Parameter]
72 | ): void | ActionReturn;
73 | }
74 |
75 | export const draggable = core_draggable as Action<
76 | HTMLElement,
77 | DragOptions | undefined,
78 | {
79 | 'on:neodrag:start': (e: CustomEvent) => void;
80 | 'on:neodrag': (e: CustomEvent) => void;
81 | 'on:neodrag:end': (e: CustomEvent) => void;
82 | }
83 | >;
84 |
85 | export type {
86 | DragAxis,
87 | DragBounds,
88 | DragBoundsCoords,
89 | DragEventData,
90 | DragOptions,
91 | } from '@neodrag/core';
92 |
--------------------------------------------------------------------------------
/packages/svelte/tests/CancelDraggable.spec.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 | import { render } from '@testing-library/svelte';
3 | import CancelDraggable from './components/CancelDraggable.svelte';
4 | import { drag, touchDrag } from './testHelpers';
5 |
6 | describe('CancelDraggable', () => {
7 | it('renders a basic div', () => {
8 | const { getByText } = render(CancelDraggable);
9 |
10 | const element = getByText('This will drag!');
11 |
12 | expect(element).toBeInTheDocument();
13 | expect(element).not.toHaveClass('neodrag');
14 | expect(element).not.toHaveClass('neodrag-dragged');
15 | expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)');
16 |
17 | const cancelElement = getByText('You shall not drag!!');
18 |
19 | expect(cancelElement).toBeInTheDocument();
20 | expect(cancelElement).toHaveClass('cancel');
21 | });
22 |
23 | it('should drag by the main element', async () => {
24 | const { getByText } = render(CancelDraggable);
25 |
26 | const element = getByText('This will drag!');
27 |
28 | expect(element).toBeInTheDocument();
29 |
30 | await drag(element, 0, 0, 50, 50);
31 |
32 | expect(element).toHaveClass('neodrag');
33 | expect(element).toHaveClass('neodrag-dragged');
34 | expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)');
35 | });
36 |
37 | it('should not drag by the cancel element', async () => {
38 | const { getByText } = render(CancelDraggable);
39 |
40 | const cancelElement = getByText('You shall not drag!!');
41 |
42 | expect(cancelElement).toBeInTheDocument();
43 |
44 | await drag(cancelElement, 0, 0, 50, 50);
45 |
46 | const element = getByText('This will drag!');
47 |
48 | expect(element).toHaveClass('neodrag');
49 | expect(element).not.toHaveClass('neodrag-dragged');
50 | expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)');
51 | });
52 |
53 | it('should drag by the main element by touch', async () => {
54 | const { getByText } = render(CancelDraggable);
55 |
56 | const element = getByText('This will drag!');
57 |
58 | expect(element).toBeInTheDocument();
59 |
60 | await touchDrag(element, 0, 0, 50, 50);
61 |
62 | expect(element).toHaveClass('neodrag');
63 | expect(element).toHaveClass('neodrag-dragged');
64 | expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)');
65 | });
66 |
67 | it('should not drag by the cancel element by touch', async () => {
68 | const { getByText } = render(CancelDraggable);
69 |
70 | const cancelElement = getByText('You shall not drag!!');
71 |
72 | expect(cancelElement).toBeInTheDocument();
73 |
74 | await touchDrag(cancelElement, 0, 0, 50, 50);
75 |
76 | const element = getByText('This will drag!');
77 |
78 | expect(element).toHaveClass('neodrag');
79 | expect(element).not.toHaveClass('neodrag-dragged');
80 | expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)');
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/packages/svelte/tests/HandleDraggable.spec.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 | import { render } from '@testing-library/svelte';
3 | import HandleDraggable from './components/HandleDraggable.svelte';
4 | import { drag, touchDrag } from './testHelpers';
5 |
6 | describe('CancelDraggable', () => {
7 | it('renders a basic div', () => {
8 | const { getByText } = render(HandleDraggable);
9 |
10 | const element = getByText('You shall not drag!!');
11 | const handleElement = getByText('This will drag!');
12 |
13 | expect(element).toBeInTheDocument();
14 | expect(handleElement).toBeInTheDocument();
15 |
16 | expect(element).not.toHaveClass('svelte-draggable');
17 | expect(element).not.toHaveClass('svelte-draggable-dragged');
18 | expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)');
19 | });
20 |
21 | it('should drag by the handle element', async () => {
22 | const { getByText } = render(HandleDraggable);
23 |
24 | const element = getByText('You shall not drag!!');
25 | const handleElement = getByText('This will drag!');
26 |
27 | expect(element).toBeInTheDocument();
28 | expect(handleElement).toBeInTheDocument();
29 |
30 | await drag(handleElement, 0, 0, 50, 50);
31 |
32 | expect(element).toHaveClass('svelte-draggable');
33 | expect(element).toHaveClass('svelte-draggable-dragged');
34 | expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)');
35 | });
36 |
37 | it('should not drag by the main element', async () => {
38 | const { getByText } = render(HandleDraggable);
39 |
40 | const element = getByText('You shall not drag!!');
41 | const handleElement = getByText('This will drag!');
42 |
43 | expect(element).toBeInTheDocument();
44 | expect(handleElement).toBeInTheDocument();
45 |
46 | await drag(element, 0, 0, 50, 50);
47 |
48 | expect(element).toHaveClass('svelte-draggable');
49 | expect(element).not.toHaveClass('svelte-draggable-dragged');
50 | expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)');
51 | });
52 |
53 | it('should drag by the handle element by touch', async () => {
54 | const { getByText } = render(HandleDraggable);
55 |
56 | const element = getByText('You shall not drag!!');
57 | const handleElement = getByText('This will drag!');
58 |
59 | expect(element).toBeInTheDocument();
60 | expect(handleElement).toBeInTheDocument();
61 |
62 | await touchDrag(handleElement, 0, 0, 50, 50);
63 |
64 | expect(element).toHaveClass('svelte-draggable');
65 | expect(element).toHaveClass('svelte-draggable-dragged');
66 | expect(element).toHaveStyle('transform: translate3d(50px, 50px, 0)');
67 | });
68 |
69 | it('should not drag by the main element by touch', async () => {
70 | const { getByText } = render(HandleDraggable);
71 |
72 | const element = getByText('You shall not drag!!');
73 |
74 | expect(element).toBeInTheDocument();
75 |
76 | await touchDrag(element, 0, 0, 50, 50);
77 |
78 | expect(element).toHaveClass('svelte-draggable');
79 | expect(element).not.toHaveClass('svelte-draggable-dragged');
80 | expect(element).toHaveStyle('transform: translate3d(0px, 0px, 0)');
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/packages/svelte/tests/components/CancelDraggable.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | This will drag!
7 |
You shall not drag!!
8 |
9 |
--------------------------------------------------------------------------------
/packages/svelte/tests/components/Draggable.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 | {body}
13 |
--------------------------------------------------------------------------------
/packages/svelte/tests/components/HandleDraggable.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | You shall not drag!!
7 |
This will drag!
8 |
9 |
--------------------------------------------------------------------------------
/packages/svelte/tests/testHelpers.ts:
--------------------------------------------------------------------------------
1 | import { fireEvent } from '@testing-library/svelte';
2 |
3 | /**
4 | * Simulate dragging a draggable element using the mouse.
5 | *
6 | * @param element the element to drag
7 | * @param clientX the X coordinate to drag the element to
8 | * @param clientY the Y coordinate to drag the element to
9 | */
10 | export async function drag(element: HTMLElement, startX = 0, startY = 0, endX = 0, endY = 0) {
11 | await fireEvent.mouseEnter(element);
12 | await fireEvent.mouseOver(element);
13 | await fireEvent.mouseDown(element, { clientX: startX, clientY: startY });
14 | await fireEvent.mouseMove(element, { clientX: endX, clientY: endY });
15 | await fireEvent.mouseUp(element);
16 | }
17 |
18 | function createTouchList(clientX: number, clientY: number) {
19 | let touches: any = {
20 | item: (index: number) => {
21 | return {
22 | clientX,
23 | clientY,
24 | };
25 | },
26 | length: 1,
27 | 0: { clientX, clientY },
28 | };
29 |
30 | touches[Symbol.iterator] = function* () {
31 | yield { clientX, clientY };
32 | };
33 |
34 | return touches;
35 | }
36 |
37 | /**
38 | * Simulate dragging a draggable element using touch.
39 | *
40 | * @param element the element to drag
41 | * @param startX the X coordinate to start the drag event at
42 | * @param startY the Y coordinate to start the drag event at
43 | * @param endX the X coordinate to drag the element to
44 | * @param endY the Y coordinate to drag the element to
45 | */
46 | export async function touchDrag(element: HTMLElement, startX = 0, startY = 0, endX = 0, endY = 0) {
47 | await fireEvent.touchStart(element, { touches: createTouchList(startX, startY) });
48 | await fireEvent.touchMove(element, { touches: createTouchList(endX, endY) });
49 | await fireEvent.touchEnd(element, { touches: createTouchList(endX, endY) });
50 | }
51 |
--------------------------------------------------------------------------------
/packages/svelte/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "ESNext",
4 | "target": "ESNext",
5 | "declaration": true,
6 | "emitDeclarationOnly": true,
7 | "declarationDir": "dist/",
8 | "strict": true,
9 | "esModuleInterop": true,
10 | "moduleResolution": "Bundler",
11 | "types": ["vitest/globals", "svelte"]
12 | },
13 | "files": ["./src/index.ts"]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/svelte/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { core_config } from '../config';
2 |
3 | export default core_config({});
4 |
--------------------------------------------------------------------------------
/packages/svelte/vitest.config.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { svelte } from '@sveltejs/vite-plugin-svelte';
3 | import { defineConfig } from 'vite';
4 |
5 | export default defineConfig({
6 | plugins: [svelte({ hot: !process.env.VITEST })],
7 | test: {
8 | global: true,
9 | environment: 'jsdom',
10 | },
11 | });
12 |
--------------------------------------------------------------------------------
/packages/vanilla/demo-umd/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/packages/vanilla/demo-umd/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/packages/vanilla/demo-umd/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "private": true,
4 | "version": "0.0.0",
5 | "scripts": {},
6 | "devDependencies": {},
7 | "dependencies": {
8 | "@neodrag/vanilla": "workspace:*"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/vanilla/demo-umd/src/main.js:
--------------------------------------------------------------------------------
1 | const Draggable = NeoDrag.Draggable
2 |
3 | const draggableEl = document.querySelector('.box');
4 | const xSlider = document.querySelector('#x');
5 | const ySlider = document.querySelector('#y');
6 |
7 | let position = { x: 0, y: 0 };
8 |
9 | const dragInstance = new Draggable(draggableEl, {
10 | position,
11 | onDrag: ({ offsetX, offsetY }) => {
12 | position = { x: offsetX, y: offsetY };
13 |
14 | xSlider.value = offsetX.toString();
15 | ySlider.value = offsetY.toString();
16 | },
17 | });
18 |
19 | xSlider.addEventListener('input', (e) => {
20 | position.x = +e.target.value;
21 | dragInstance.updateOptions({ position });
22 | });
23 |
24 | ySlider.addEventListener('input', (e) => {
25 | position.y = +e.target.value;
26 | dragInstance.updateOptions({ position });
27 | });
28 |
--------------------------------------------------------------------------------
/packages/vanilla/demo-umd/src/style.css:
--------------------------------------------------------------------------------
1 | #app {
2 | font-family: Avenir, Helvetica, Arial, sans-serif;
3 | -webkit-font-smoothing: antialiased;
4 | -moz-osx-font-smoothing: grayscale;
5 | text-align: center;
6 | color: #2c3e50;
7 | margin-top: 60px;
8 | }
9 |
--------------------------------------------------------------------------------
/packages/vanilla/demo-umd/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ESNext", "DOM"],
7 | "moduleResolution": "Node",
8 | "strict": true,
9 | "sourceMap": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "esModuleInterop": true,
13 | "noEmit": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitReturns": true,
17 | "skipLibCheck": true
18 | },
19 | "include": ["src"]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/vanilla/demo/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/packages/vanilla/demo/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/vanilla/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/packages/vanilla/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "private": true,
4 | "version": "0.0.0",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "tsc && vite build",
8 | "preview": "vite preview"
9 | },
10 | "devDependencies": {
11 | "typescript": "^5.3.3",
12 | "vite": "^5.0.10"
13 | },
14 | "dependencies": {
15 | "@neodrag/vanilla": "workspace:*"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/vanilla/demo/src/main.ts:
--------------------------------------------------------------------------------
1 | import './style.css';
2 | import { Draggable } from '@neodrag/vanilla';
3 |
4 | const draggableEl = document.querySelector('.box')!;
5 | const xSlider = document.querySelector('#x')!;
6 | const ySlider = document.querySelector('#y')!;
7 |
8 | let position = { x: 0, y: 0 };
9 |
10 | const dragInstance = new Draggable(draggableEl, {
11 | position,
12 | onDrag: ({ offsetX, offsetY }) => {
13 | position = { x: offsetX, y: offsetY };
14 |
15 | xSlider.value = offsetX.toString();
16 | ySlider.value = offsetY.toString();
17 | },
18 | });
19 |
20 | xSlider.addEventListener('input', (e: Event) => {
21 | position.x = +e.target.value;
22 | dragInstance.updateOptions({ position });
23 | });
24 |
25 | ySlider.addEventListener('input', (e: Event) => {
26 | position.y = +e.target.value;
27 | dragInstance.updateOptions({ position });
28 | });
29 |
--------------------------------------------------------------------------------
/packages/vanilla/demo/src/style.css:
--------------------------------------------------------------------------------
1 | #app {
2 | font-family: Avenir, Helvetica, Arial, sans-serif;
3 | -webkit-font-smoothing: antialiased;
4 | -moz-osx-font-smoothing: grayscale;
5 | text-align: center;
6 | color: #2c3e50;
7 | margin-top: 60px;
8 | }
9 |
--------------------------------------------------------------------------------
/packages/vanilla/demo/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/packages/vanilla/demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ESNext", "DOM"],
7 | "moduleResolution": "Node",
8 | "strict": true,
9 | "sourceMap": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "esModuleInterop": true,
13 | "noEmit": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitReturns": true,
17 | "skipLibCheck": true
18 | },
19 | "include": ["src"]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/vanilla/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@neodrag/vanilla",
3 | "version": "2.3.0",
4 | "description": "JS library to add dragging to your apps 😉",
5 | "main": "./dist/index.js",
6 | "unpkg": "./dist/umd/index.js",
7 | "jsdelivr": "./dist/umd/index.js",
8 | "module": "./dist/index.js",
9 | "type": "module",
10 | "types": "./dist/index.d.ts",
11 | "files": [
12 | "dist/*"
13 | ],
14 | "sideEffects": false,
15 | "exports": {
16 | ".": {
17 | "types": "./dist/index.d.ts",
18 | "import": {
19 | "production": "./dist/min/index.js",
20 | "development": "./dist/index.js"
21 | },
22 | "default": "./dist/min/index.js"
23 | },
24 | "./package.json": "./package.json"
25 | },
26 | "repository": {
27 | "type": "git",
28 | "url": "git+https://github.com/PuruVJ/neodrag.git"
29 | },
30 | "keywords": [
31 | "draggable",
32 | "vanilla",
33 | "javascript",
34 | "typescript",
35 | "react-draggable",
36 | "drag",
37 | "neodrag",
38 | "small",
39 | "tiny",
40 | "performant",
41 | "neodrag"
42 | ],
43 | "author": "Puru Vijay",
44 | "license": "MIT",
45 | "bugs": {
46 | "url": "https://github.com/PuruVJ/neodrag/issues"
47 | },
48 | "homepage": "https://github.com/PuruVJ/neodrag/tree/main/packages/vanilla#readme",
49 | "scripts": {
50 | "compile": "tsup",
51 | "compile:watch": "tsup --watch",
52 | "pub": "pnpm compile && pnpm publish --no-git-checks --access public",
53 | "pub:dry": "pnpm compile && pnpm publish --dry-run --no-git-checks --access public"
54 | },
55 | "devDependencies": {
56 | "@neodrag/core": "workspace:*"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/packages/vanilla/src/index.ts:
--------------------------------------------------------------------------------
1 | import { draggable, type DragOptions } from '@neodrag/core';
2 |
3 | export class Draggable {
4 | private _drag_instance: ReturnType;
5 | private _options: DragOptions = {};
6 |
7 | constructor(
8 | public node: HTMLElement,
9 | options: DragOptions = {},
10 | ) {
11 | this._drag_instance = draggable(node, (this._options = options));
12 | }
13 |
14 | public updateOptions(options: DragOptions) {
15 | this._drag_instance.update(Object.assign(this._options, options));
16 | }
17 |
18 | set options(options: DragOptions) {
19 | this._drag_instance.update((this._options = options));
20 | }
21 |
22 | get options() {
23 | return this._options;
24 | }
25 |
26 | public destroy() {
27 | this._drag_instance.destroy();
28 | }
29 | }
30 |
31 | export type {
32 | DragAxis,
33 | DragBounds,
34 | DragBoundsCoords,
35 | DragEventData,
36 | DragOptions,
37 | } from '@neodrag/core';
38 |
--------------------------------------------------------------------------------
/packages/vanilla/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "declarationDir": "./dist",
6 | "declaration": true,
7 | "emitDeclarationOnly": true,
8 | "lib": ["DOM", "ESNext"],
9 | "allowJs": false,
10 | "skipLibCheck": false,
11 | "esModuleInterop": false,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "ESNext",
16 | "moduleResolution": "Node",
17 | "resolveJsonModule": true,
18 | "preserveSymlinks": false
19 | },
20 | "include": ["./src"]
21 | }
22 |
--------------------------------------------------------------------------------
/packages/vanilla/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { core_config } from '../config';
2 |
3 | export default core_config({
4 | includeUMD: true,
5 | globalName: 'NeoDrag',
6 | });
7 |
--------------------------------------------------------------------------------
/packages/vue/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @neodrag/vue
2 |
3 | ## 2.3.0
4 |
5 | ### Minor Changes
6 |
7 | - [#186](https://github.com/PuruVJ/neodrag/pull/186) [`a917373`](https://github.com/PuruVJ/neodrag/commit/a917373e56378ae9443f3162e428abc8c058b191) Thanks [@PuruVJ](https://github.com/PuruVJ)! - feat: Expose event: PointerEvent
8 |
9 | ## 2.2.0
10 |
11 | ### Minor Changes
12 |
13 | - [#176](https://github.com/PuruVJ/neodrag/pull/176) [`0cead87`](https://github.com/PuruVJ/neodrag/commit/0cead8701f132670bd5618ceeb8fdee8e9a3ad27) Thanks [@PuruVJ](https://github.com/PuruVJ)! - deprecate: legacyTranslate and gpuAcceleration
14 |
15 | ## 2.1.0
16 |
17 | ### Minor Changes
18 |
19 | - [#174](https://github.com/PuruVJ/neodrag/pull/174) [`45d9eeb`](https://github.com/PuruVJ/neodrag/commit/45d9eeb375b18eb0530cc079613dcdc21cce81d4) Thanks [@PuruVJ](https://github.com/PuruVJ)! - feat: threshold option
20 |
21 | - [#172](https://github.com/PuruVJ/neodrag/pull/172) [`ac0e10b`](https://github.com/PuruVJ/neodrag/commit/ac0e10bf287b3577fb926d6ba585e906abeaab72) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Use AbortSignal for unregistering event listeners
22 |
23 | ### Patch Changes
24 |
25 | - [#174](https://github.com/PuruVJ/neodrag/pull/174) [`45d9eeb`](https://github.com/PuruVJ/neodrag/commit/45d9eeb375b18eb0530cc079613dcdc21cce81d4) Thanks [@PuruVJ](https://github.com/PuruVJ)! - fix: drag end no longer causes onclick to trigger.
26 |
27 | ## 2.0.4
28 |
29 | ### Patch Changes
30 |
31 | - [#145](https://github.com/PuruVJ/neodrag/pull/145) [`e19ce73`](https://github.com/PuruVJ/neodrag/commit/e19ce732a9494dc3eb05e0c8702cd802abc0af9a) Thanks [@PuruVJ](https://github.com/PuruVJ)! - fix: ignoreMultitouch now behaves correctly
32 |
33 | ## 2.0.3
34 |
35 | ### Patch Changes
36 |
37 | - [#111](https://github.com/PuruVJ/neodrag/pull/111) [`b736fa6`](https://github.com/PuruVJ/neodrag/commit/b736fa689e06491e348638311900900e35342e6e) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Fix finding elements inside shadow DOM, not just the root
38 |
39 | ## 2.0.2
40 |
41 | ### Patch Changes
42 |
43 | - [#108](https://github.com/PuruVJ/neodrag/pull/108) [`0e6a36a`](https://github.com/PuruVJ/neodrag/commit/0e6a36a8ab1be01b97d8604dbc931c6e7ce4f16b) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Fix usage inside Shadow DOM
44 |
45 | ## 2.0.1
46 |
47 | ### Patch Changes
48 |
49 | - Fix canMoveInY bug in `dragEnd` function
50 |
51 | ## 2.0.0
52 |
53 | ### Patch Changes
54 |
55 | - [#95](https://github.com/PuruVJ/neodrag/pull/95) [`3c10f6ae`](https://github.com/PuruVJ/neodrag/commit/3c10f6ae377c3e9fc9fea963ea99204a4649806c) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Add `legacyTranslate` option, remove memoization code, align to browser's RAF throttling
56 |
57 | - [#97](https://github.com/PuruVJ/neodrag/pull/97) [`9e5c4647`](https://github.com/PuruVJ/neodrag/commit/9e5c46477c7781bc75a57944983434a0c8ceff77) Thanks [@PuruVJ](https://github.com/PuruVJ)! - New output formats
58 |
59 | - [`da98e910`](https://github.com/PuruVJ/neodrag/commit/da98e910469d63e53e2462e74196bad3b90ea053) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Expose rootNode and currentNode from events. Remove node and domRect
60 |
61 | - [#99](https://github.com/PuruVJ/neodrag/pull/99) [`a1572bce`](https://github.com/PuruVJ/neodrag/commit/a1572bce5186051a5114dd580017a49fc2b3c7fc) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Add transform function
62 |
63 | - [`8dd0d88f`](https://github.com/PuruVJ/neodrag/commit/8dd0d88ff0458c0bd6d20e3649371fdf732c9ebb) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Add recomputeBounds option
64 |
65 | - [`ca8cde25`](https://github.com/PuruVJ/neodrag/commit/ca8cde252e555cc50a0919a295d01ec340207f8e) Thanks [@PuruVJ](https://github.com/PuruVJ)! Expose rootNode and currentNode from events. Remove node and domRect
66 |
67 | - [`2ea2bad4`](https://github.com/PuruVJ/neodrag/commit/2ea2bad4f16e798fb0ecb55f8554efcd2e50ca26) Thanks [@PuruVJ](https://github.com/PuruVJ)! Fix ouble click issue
68 |
69 | - Fix behavior when snap provided as 0
70 |
71 | # 1.0.0
72 |
73 | Skipping for 2.0.0
74 |
75 | ## 0.1.2
76 |
77 | ### Patch Changes
78 |
79 | - Fix snapping while scaled
80 |
--------------------------------------------------------------------------------
/packages/vue/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | @neodrag/vue
7 |
8 |
9 |
10 | One draggable to rule em all
11 |
12 |
13 | A lightweight Vue directive to make your elements draggable.
14 |
15 |
16 |
17 |
18 |
19 |
Getting Started
20 |
21 | ## Features
22 |
23 | - 🤏 Tiny - Only 1.77KB min+brotli.
24 | - 🐇 Simple - Quite simple to use, and effectively no-config required!
25 | - 🧙♀️ Elegant - Vue directive, to keep the usage simple, elegant and straightforward.
26 | - 🗃️ Highly customizable - Offers tons of options that you can modify to get different behavior.
27 | - ⚛️ Reactive - Change options passed to it on the fly, it will **just work 🙂**
28 |
29 | [Try it in Stackblitz](https://stackblitz.com/edit/vitejs-vite-2pg1r1?file=src%2FApp.jsx)
30 |
31 | ## Installing
32 |
33 | ```bash
34 | pnpm add @neodrag/vue
35 |
36 | # npm
37 | npm install @neodrag/vue
38 |
39 | # yarn
40 | yarn add @neodrag/vue
41 | ```
42 |
43 | ## Usage
44 |
45 | Basic usage
46 |
47 | ```vue
48 |
51 |
52 |
53 | I am draggable
54 |
55 | ```
56 |
57 | With options
58 |
59 | ```vue
60 |
63 |
64 |
65 | I am draggable
66 |
67 | ```
68 |
69 | Defining options elsewhere with typescript
70 |
71 | ```vue
72 |
80 |
81 |
82 | I am draggable
83 |
84 | ```
85 |
86 | Read the docs
87 |
88 | ## Credits
89 |
90 | Inspired from the amazing [react-draggable](https://github.com/react-grid-layout/react-draggable) library, and implements a similar API, but 3x smaller.
91 |
92 | # License
93 |
94 | MIT License © Puru Vijay
95 |
--------------------------------------------------------------------------------
/packages/vue/demo/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/packages/vue/demo/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # demo
2 |
3 | ## 0.0.10
4 |
5 | ### Patch Changes
6 |
7 | - Updated dependencies [[`a917373`](https://github.com/PuruVJ/neodrag/commit/a917373e56378ae9443f3162e428abc8c058b191)]:
8 | - @neodrag/vue@2.3.0
9 |
10 | ## 0.0.9
11 |
12 | ### Patch Changes
13 |
14 | - Updated dependencies [[`0cead87`](https://github.com/PuruVJ/neodrag/commit/0cead8701f132670bd5618ceeb8fdee8e9a3ad27)]:
15 | - @neodrag/vue@2.2.0
16 |
17 | ## 0.0.8
18 |
19 | ### Patch Changes
20 |
21 | - Updated dependencies [[`45d9eeb`](https://github.com/PuruVJ/neodrag/commit/45d9eeb375b18eb0530cc079613dcdc21cce81d4), [`45d9eeb`](https://github.com/PuruVJ/neodrag/commit/45d9eeb375b18eb0530cc079613dcdc21cce81d4), [`ac0e10b`](https://github.com/PuruVJ/neodrag/commit/ac0e10bf287b3577fb926d6ba585e906abeaab72)]:
22 | - @neodrag/vue@2.1.0
23 |
24 | ## 0.0.7
25 |
26 | ### Patch Changes
27 |
28 | - Updated dependencies [[`e19ce73`](https://github.com/PuruVJ/neodrag/commit/e19ce732a9494dc3eb05e0c8702cd802abc0af9a)]:
29 | - @neodrag/vue@2.0.4
30 |
31 | ## 0.0.6
32 |
33 | ### Patch Changes
34 |
35 | - Updated dependencies [[`b736fa6`](https://github.com/PuruVJ/neodrag/commit/b736fa689e06491e348638311900900e35342e6e)]:
36 | - @neodrag/vue@2.0.3
37 |
38 | ## 0.0.5
39 |
40 | ### Patch Changes
41 |
42 | - Updated dependencies [[`0e6a36a`](https://github.com/PuruVJ/neodrag/commit/0e6a36a8ab1be01b97d8604dbc931c6e7ce4f16b)]:
43 | - @neodrag/vue@2.0.2
44 |
45 | ## 0.0.4
46 |
47 | ### Patch Changes
48 |
49 | - Updated dependencies []:
50 | - @neodrag/vue@2.0.1
51 |
52 | ## 0.0.3
53 |
54 | ### Patch Changes
55 |
56 | - Updated dependencies [[`bd831dcc`](https://github.com/PuruVJ/neodrag/commit/bd831dcc101d967b78505acd064cdfcde03b62ff), [`3c10f6ae`](https://github.com/PuruVJ/neodrag/commit/3c10f6ae377c3e9fc9fea963ea99204a4649806c), [`9e5c4647`](https://github.com/PuruVJ/neodrag/commit/9e5c46477c7781bc75a57944983434a0c8ceff77), [`da98e910`](https://github.com/PuruVJ/neodrag/commit/da98e910469d63e53e2462e74196bad3b90ea053), [`a1572bce`](https://github.com/PuruVJ/neodrag/commit/a1572bce5186051a5114dd580017a49fc2b3c7fc), [`8dd0d88f`](https://github.com/PuruVJ/neodrag/commit/8dd0d88ff0458c0bd6d20e3649371fdf732c9ebb)]:
57 | - @neodrag/vue@2.0.0
58 |
59 | ## 0.0.3-next.5
60 |
61 | ### Patch Changes
62 |
63 | - Updated dependencies [[`a1572bc`](https://github.com/PuruVJ/neodrag/commit/a1572bce5186051a5114dd580017a49fc2b3c7fc)]:
64 | - @neodrag/vue@2.0.0-next.6
65 |
66 | ## 0.0.3-next.4
67 |
68 | ### Patch Changes
69 |
70 | - Updated dependencies [[`9e5c464`](https://github.com/PuruVJ/neodrag/commit/9e5c46477c7781bc75a57944983434a0c8ceff77)]:
71 | - @neodrag/vue@2.0.0-next.5
72 |
73 | ## 0.0.3-next.3
74 |
75 | ### Patch Changes
76 |
77 | - Updated dependencies []:
78 | - @neodrag/vue@2.0.0-next.4
79 |
80 | ## 0.0.3-next.2
81 |
82 | ### Patch Changes
83 |
84 | - Updated dependencies [[`0f513db2`](https://github.com/PuruVJ/neodrag/commit/0f513db2c0a88ed03f0472311a03b6ae0e4f9483), [`8dd0d88f`](https://github.com/PuruVJ/neodrag/commit/8dd0d88ff0458c0bd6d20e3649371fdf732c9ebb)]:
85 | - @neodrag/vue@2.0.0-next.3
86 |
--------------------------------------------------------------------------------
/packages/vue/demo/README.md:
--------------------------------------------------------------------------------
1 | # Vue 3 + Typescript + Vite
2 |
3 | This template should help get you started developing with Vue 3 and Typescript in Vite. The template uses Vue 3 `
11 |
12 |
13 |
--------------------------------------------------------------------------------
/packages/vue/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "private": true,
4 | "version": "0.0.10",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "vite build",
8 | "preview": "vite preview"
9 | },
10 | "dependencies": {
11 | "@neodrag/vue": "workspace:*",
12 | "vue": "^3.3.12"
13 | },
14 | "devDependencies": {
15 | "@vitejs/plugin-vue": "^4.5.2",
16 | "typescript": "^5.3.3",
17 | "vite": "^5.0.10",
18 | "vue-tsc": "^1.8.25"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/vue/demo/src/App.vue:
--------------------------------------------------------------------------------
1 |
26 |
27 |
28 |
29 |
30 |
31 | 2nd
32 |
33 |
Cancel me out
34 |
Cancel me out pt 2
35 |
36 |
37 |
38 |
39 | Change axis
40 |
41 |
42 |
58 |
--------------------------------------------------------------------------------
/packages/vue/demo/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Hello
7 |
8 |
--------------------------------------------------------------------------------
/packages/vue/demo/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | declare module '*.vue' {
5 | import type { DefineComponent } from 'vue';
6 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
7 | const component: DefineComponent<{}, {}, any>;
8 | export default component;
9 | }
10 |
--------------------------------------------------------------------------------
/packages/vue/demo/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 | import App from './App.vue';
3 |
4 | createApp(App).mount('#app');
5 |
--------------------------------------------------------------------------------
/packages/vue/demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "useDefineForClassFields": true,
5 | "module": "esnext",
6 | "moduleResolution": "node",
7 | "strict": true,
8 | "jsx": "preserve",
9 | "sourceMap": true,
10 | "resolveJsonModule": true,
11 | "esModuleInterop": true,
12 | "lib": ["esnext", "dom"]
13 | },
14 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
15 | "references": [{ "path": "./tsconfig.node.json" }]
16 | }
17 |
--------------------------------------------------------------------------------
/packages/vue/demo/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "esnext",
5 | "moduleResolution": "node"
6 | },
7 | "include": ["vite.config.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/vue/demo/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import vue from '@vitejs/plugin-vue';
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [vue({ reactivityTransform: true })],
7 | });
8 |
--------------------------------------------------------------------------------
/packages/vue/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@neodrag/vue",
3 | "version": "2.3.0",
4 | "description": "Vue library to add dragging to your apps 😉",
5 | "main": "./dist/index.js",
6 | "module": "./dist/index.js",
7 | "type": "module",
8 | "types": "./dist/index.d.ts",
9 | "files": [
10 | "dist/*"
11 | ],
12 | "sideEffects": false,
13 | "exports": {
14 | ".": {
15 | "types": "./dist/index.d.ts",
16 | "import": {
17 | "production": "./dist/min/index.js",
18 | "development": "./dist/index.js"
19 | },
20 | "default": "./dist/min/index.js"
21 | },
22 | "./package.json": "./package.json"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "git+https://github.com/PuruVJ/neodrag.git"
27 | },
28 | "keywords": [
29 | "draggable",
30 | "vue",
31 | "react-draggable",
32 | "drag",
33 | "neodrag",
34 | "small",
35 | "tiny",
36 | "performant",
37 | "neodrag"
38 | ],
39 | "author": "Puru Vijay",
40 | "license": "MIT",
41 | "bugs": {
42 | "url": "https://github.com/PuruVJ/neodrag/issues"
43 | },
44 | "homepage": "https://github.com/PuruVJ/neodrag/tree/main/packages/vue#readme",
45 | "scripts": {
46 | "compile": "tsup",
47 | "compile:watch": "tsup --watch",
48 | "pub": "pnpm compile && pnpm publish --no-git-checks --access public",
49 | "pub:dry": "pnpm compile && pnpm publish --dry-run --no-git-checks --access public"
50 | },
51 | "devDependencies": {
52 | "@neodrag/core": "workspace:*"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/vue/src/index.ts:
--------------------------------------------------------------------------------
1 | import { type Directive } from 'vue';
2 | import { draggable, type DragOptions } from '@neodrag/core';
3 |
4 | const draggable_map = new WeakMap>();
5 |
6 | export const vDraggable: Directive = {
7 | mounted: (el, { value = {} }) =>
8 | !draggable_map.has(el) && draggable_map.set(el, draggable(el, value)),
9 |
10 | updated: (el, { value = {} }) => draggable_map.get(el)!.update(value),
11 |
12 | unmounted: (el) => {
13 | draggable_map.get(el)!.destroy();
14 | draggable_map.delete(el);
15 | },
16 | };
17 |
18 | export type {
19 | DragAxis,
20 | DragBounds,
21 | DragBoundsCoords,
22 | DragOptions,
23 | DragEventData,
24 | } from '@neodrag/core';
25 |
--------------------------------------------------------------------------------
/packages/vue/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "declarationDir": "./dist",
6 | "declaration": true,
7 | "emitDeclarationOnly": true,
8 | "lib": ["DOM", "ESNext"],
9 | "allowJs": false,
10 | "skipLibCheck": false,
11 | "esModuleInterop": false,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "ESNext",
16 | "moduleResolution": "Node",
17 | "resolveJsonModule": true,
18 | "preserveSymlinks": false
19 | },
20 | "include": ["./src"]
21 | }
22 |
--------------------------------------------------------------------------------
/packages/vue/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { core_config } from '../config';
2 |
3 | export default core_config({});
4 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - packages/**
3 | - '!**/test/**'
4 | - docs
5 | - config
6 |
--------------------------------------------------------------------------------
/scripts/gather-sizes.ts:
--------------------------------------------------------------------------------
1 | import { file as brotliSize } from 'brotli-size';
2 | import fg from 'fast-glob';
3 | import { readFileSync } from 'node:fs';
4 | import { mkdir, writeFile } from 'node:fs/promises';
5 |
6 | async function main() {
7 | const files = (
8 | await fg(new URL('../packages/*/dist/min/index.js', import.meta.url).pathname)
9 | ).filter((path) => !path.includes('core'));
10 |
11 | const versions = (await fg(new URL('../packages/*/package.json', import.meta.url).pathname))
12 | .filter((path) => !path.includes('core'))
13 | .map((path) => {
14 | const framework = /packages\/(?[^ $]*)\/package\.json/.exec(path)?.groups
15 | ?.framework!;
16 |
17 | return { framework, version: JSON.parse(readFileSync(path, 'utf-8')).version };
18 | });
19 |
20 | const contents = (
21 | await Promise.all(
22 | files.map(async (file) => {
23 | const framework = /packages\/(?[^ $]*)\/dist/.exec(file)?.groups?.framework!;
24 | const size = ((await brotliSize(file)) / 1024).toFixed(2);
25 |
26 | return { framework, size };
27 | }),
28 | )
29 | ).reduce(
30 | (acc, { framework, size }) => ({
31 | ...acc,
32 | [framework]: {
33 | size: +size,
34 | version: versions.find(({ framework: vFw }) => vFw === framework)?.version,
35 | },
36 | }),
37 | {},
38 | );
39 |
40 | // Ensure folder if not exists
41 | try {
42 | await mkdir(new URL('../docs/src/data', import.meta.url).pathname);
43 | } catch (error) {}
44 |
45 | console.table(Object.entries(contents).sort((a, b) => (a[0] > b[0] ? 1 : -1)));
46 |
47 | writeFile(
48 | new URL('../docs/src/data/sizes.json', import.meta.url),
49 | JSON.stringify(contents, null, 2),
50 | );
51 | }
52 |
53 | main();
54 |
--------------------------------------------------------------------------------