{"id":13474849,"url":"https://github.com/dflex-js/dflex","last_synced_at":"2025-05-14T19:05:04.969Z","repository":{"id":37572332,"uuid":"263372613","full_name":"dflex-js/dflex","owner":"dflex-js","description":"The sophisticated Drag and Drop library you've been waiting for 🥳","archived":false,"fork":false,"pushed_at":"2024-08-01T11:28:21.000Z","size":7786,"stargazers_count":1746,"open_issues_count":15,"forks_count":28,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-04-03T06:07:24.096Z","etag":null,"topics":["dnd","dom-api","dom-manipulation","dom-store","drag-and-drop","draggable","droppable","sortable"],"latest_commit_sha":null,"homepage":"https://www.dflex.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dflex-js.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-12T15:14:09.000Z","updated_at":"2025-04-01T15:49:17.000Z","dependencies_parsed_at":"2024-01-16T07:23:23.277Z","dependency_job_id":"3cf984b8-4829-4023-afc3-bed869806a20","html_url":"https://github.com/dflex-js/dflex","commit_stats":{"total_commits":407,"total_committers":10,"mean_commits":40.7,"dds":0.07862407862407861,"last_synced_commit":"d604e4a66248b3928b169006c326530634ce8883"},"previous_names":[],"tags_count":105,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dflex-js%2Fdflex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dflex-js%2Fdflex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dflex-js%2Fdflex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dflex-js%2Fdflex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dflex-js","download_url":"https://codeload.github.com/dflex-js/dflex/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248202673,"owners_count":21064402,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dnd","dom-api","dom-manipulation","dom-store","drag-and-drop","draggable","droppable","sortable"],"created_at":"2024-07-31T16:01:15.323Z","updated_at":"2025-04-10T10:49:48.858Z","avatar_url":"https://github.com/dflex-js.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  \u003ca href=\"https://www.dflex.dev/\" target=\"_blank\"\u003e\n    \u003cimg\n    src=\"https://raw.githubusercontent.com/dflex-js/dflex/master/DFlex-readme.png\"\n    alt=\"DFlex is a Javascript library for modern Drag and Drop apps\" /\u003e\n  \u003c/a\u003e\n\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/dflex-js/dflex\"\u003e\n    \u003cimg\n    src=\"https://img.shields.io/github/workflow/status/dflex-js/dflex/Unit Test\"\n    alt=\"Dflex build status\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/dflex-js/dflex/pulls\"\u003e\n    \u003cimg\n    src=\"https://img.shields.io/github/issues-pr/dflex-js/dflex\"\n    alt=\"number of opened pull requests\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@dflex/dnd\"\u003e\n    \u003cimg\n    src=\"https://img.shields.io/npm/v/@dflex/dnd\"\n    alt=\"DFlex last released version\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/dflex-js/dflex/issues\"\u003e\n  \u003cimg\n    src=\"https://img.shields.io/github/issues/dflex-js/dflex\"\n    alt=\"number of opened issues\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/dflex-js/dflex/pulls\"\u003e\n   \u003cimg\n   src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg\"\n   alt=\"Dflex welcomes pull request\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://twitter.com/intent/follow?screen_name=dflex_js\"\u003e\n    \u003cimg\n    src=\"https://img.shields.io/twitter/url?label=Follow%20%40dflex_js\u0026style=social\u0026url=https%3A%2F%2Ftwitter.com%2Fdflex_js\"\n    alt=\"Follow DFlex on twitter\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n# DFlex\n\nDFlex is a Javascript library for modern Drag and Drop apps. It's built\nwith vanilla Javascript and implemented an enhanced transformation mechanism to\nmanipulate DOM elements. It is by far the only Drag and Drop library on the\ninternet that manipulates the DOM instead of reconstructing it and has its own\nscheduler and reconciler.\n\n## Features\n\n- Dynamic architecture.\n- Traverse DOM without calling browser API.\n- Infinite DOM transformation instead of reconstructing the DOM tree with every interaction.\n- Customized and enhanced reconciler targets only elements transformed from origin.\n- Isolated from data flow with a scheduler prevents any blocking event.\n- Prevent layout shift that happens with any Drag and Drop mechanism.\n- Animated transformation with each interaction.\n- Headless and compatible with any modern JS framework.\n- Targeting each DOM element individually based on registration.\n- Event driven and fully customized API.\n- Extensible using its own matching algorithm instead of flat recursion algorithm(s).\n- Support three different types of restrictions.\n- Support four types of custom events and custom layout state emitter.\n\n## Implemented Transformation 💡\n\n- The original input order which appears when inspecting elements stays the\n  same. While the visual order happens after transformation and it's supported by the\n  `data-index` attribute to know the order of elements in the visual list.\u003cbr/\u003e\u003cbr/\u003e\n  ![original and visual order](https://user-images.githubusercontent.com/19228730/126757232-0e72a153-7fba-4868-b881-d29f2439d510.gif)\n\n- To enable handling a large set of elements, the transformation is related\n  to the viewport. No matter how many elements are affected, DFlex only\n  transforms elements visible on the screen. Elements outside the viewport are\n  triggered to a new position when they are visible.\u003cbr/\u003e\u003cbr/\u003e\n  ![Trigger elements visible on the screen](https://user-images.githubusercontent.com/19228730/126758576-e716787d-3ff7-44cb-883a-c6b7064e30e5.gif)\n\n- Support strict transformation between containers.\u003cbr/\u003e\u003cbr/\u003e\n  ![Handle orphaned container](https://user-images.githubusercontent.com/19228730/165508982-c4d3b317-19bd-4a98-ba0f-febf772de44a.gif)\n\n## Installation\n\n```bash\nnpm install @dflex/dnd\n```\n\n## API\n\nDFlex DnD depends on three principles to achieve DOM interactivity:\n\n- Register element in the store.\n- Start dragging when mouse is down.\n- End dragging to release element when mouse is up.\n\n```js\nimport { store, DnD } from \"@dflex/dnd\";\n```\n\n### Register element\n\nEach element should be registered in DFlex DnD Store in order to be active for drag\nand drop later.\n\n```ts\nstore.register(RegisterInputOpts): void;\n```\n\nWhere `RegisterInputOpts` is an object with the following properties:\n\n- `id: string` Targeted element-id.\n- `depth?: number` The depth of targeted element starting from zero (The default value is zero).\n- `readonly?: boolean` True for elements that won't be transformed during DnD\n  but belongs to the same interactive container.\n\n### Create Drag and Drop Session\n\nThe responsive drag and drop session should be created when `onmousedown` is\nfired. So it can initialize the element and its siblings before start dragging.\n\n```ts\nconst dflexDnD = new DnD(id, coordinate, opts);\n```\n\n- `id: string` registered element-id in the store.\n- `coordinate: AxesPoint` is an object with `{x: number, y: number}` contains the coordinates of the\n  mouse/touch click.\n- `opts?: DFlexDnDOpts` is DnD options object. You can see [DFlex DnD options\n  full documentation by clicking here.](#options)\n\n### Start responsive dragging\n\n```ts\ndflexDnD.dragAt(x, y);\n```\n\n- `x: number` is event.clientX, the horizontal click coordinate.\n- `y: number` is event.clientY, the vertical click coordinate.\n\n### End Drag and Drop Session\n\n```ts\ndflexDnD.endDragging();\n```\n\n### Cleanup element\n\nIt's necessary to cleanup the element from store when the element won't be used\nor will be removed/unmounted from the DOM to prevent any potential memory leaks.\n\n```ts\nstore.unregister(id: string): void\n```\n\n## Options\n\nYou can pass options when creating a DnD instance that controls each element\nindividually. So your options can be different from each other.\n\n### Dragging Threshold\n\nThe threshold object defines when the dragging event should be fired and\ntriggers the response of other sibling elements.\n\n#### Threshold Interface\n\n```ts\ninterface ThresholdPercentages {\n  /** vertical threshold in percentage from 0-100 */\n  vertical: number;\n\n  /** horizontal threshold in percentage from 0-100 */\n  horizontal: number;\n}\n```\n\n#### Threshold Definition\n\n```ts\ninterface DFlexDnDOpts {\n  // ... other options.\n  threshold?: Partial\u003cThresholdPercentages\u003e;\n}\n```\n\n#### Threshold Default Value\n\n```json\n{\n  \"threshold\": {\n    \"vertical\": 60,\n    \"horizontal\": 60\n  }\n}\n```\n\n### Commit changes to DOM\n\nDFlex is built to manipulate DOM elements with transformation indefinitely. This\nmeans you can always drag and drop elements without reconstruction of the DOM.\nStill, it comes with a reconciler that tracks elements' changes and only\nreconciles the elements that have changed their position from their origin.\n\n#### Commit Interface\n\n```ts\ninterface CommitInterface {\n  enableAfterEndingDrag: boolean;\n  enableForScrollOnly: boolean;\n}\n```\n\n#### Commit Definition\n\n```ts\ninterface DFlexDnDOpts {\n  // ... other options.\n  commit?: Partial\u003cCommitInterface\u003e;\n}\n```\n\n#### Commit Default Value\n\n```json\n{\n  \"commit\": {\n    \"enableAfterEndingDrag\": true,\n    \"enableForScrollOnly\": true\n  }\n}\n```\n\n### Dragging Restrictions\n\nYou can define the dragging restrictions for each element relative:\n\n1. Element position.\n2. Element parent container.\n3. Screen viewport (automatically enabled).\n\n#### Restrictions Interface\n\n```ts\ninterface Restrictions {\n  self: {\n    allowLeavingFromTop: boolean;\n    allowLeavingFromBottom: boolean;\n    allowLeavingFromLeft: boolean;\n    allowLeavingFromRight: boolean;\n  };\n  container: {\n    allowLeavingFromTop: boolean;\n    allowLeavingFromBottom: boolean;\n    allowLeavingFromLeft: boolean;\n    allowLeavingFromRight: boolean;\n  };\n}\n```\n\n#### Restrictions Definition\n\n```ts\ninterface DFlexDnDOpts {\n  // ... other options.\n  restrictions?: {\n    self?: Partial\u003cRestrictions[\"self\"]\u003e;\n    container?: Partial\u003cRestrictions[\"container\"]\u003e;\n  };\n}\n```\n\n#### Restrictions Default Value\n\n```json\n{\n  \"restrictions\": {\n    \"self\": {\n      \"allowLeavingFromTop\": true,\n      \"allowLeavingFromBottom\": true,\n      \"allowLeavingFromLeft\": true,\n      \"allowLeavingFromRight\": true\n    },\n    \"container\": {\n      \"allowLeavingFromTop\": true,\n      \"allowLeavingFromBottom\": true,\n      \"allowLeavingFromLeft\": true,\n      \"allowLeavingFromRight\": true\n    }\n  }\n}\n```\n\n### Auto-Scroll\n\n#### Auto-Scroll Interface\n\n```ts\ninterface ScrollOptions {\n  enable?: boolean;\n  initialSpeed?: number;\n  threshold?: Partial\u003cThresholdPercentages\u003e;\n}\n```\n\n#### Auto-Scroll Definition\n\n```ts\ninterface DFlexDnDOpts {\n  // ... other options.\n  scroll?: Partial\u003cScrollOptions\u003e;\n}\n```\n\n#### Auto-Scroll Default Value\n\n```json\n{\n  \"scroll\": {\n    \"enable\": true,\n    \"initialSpeed\": 10,\n    \"threshold\": {\n      \"vertical\": 15,\n      \"horizontal\": 15\n    }\n  }\n}\n```\n\n## Events\n\nDFlex has three (3) types of [custom\nevents](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent).\n\n1. [DFlex Dragged Event.](#dragged-event)\n2. [DFlex Interactivity Event.](#interactivity-event)\n3. [DFlex Siblings Event.](#siblings-event)\n\n### Event Usage\n\n```js\n// DFlex event handler.\nconst onDFlexEvent = (e: DFlexEvents) =\u003e {\n  // Do something.\n  console.log(`onDFlexEvent: ${e.type}`, e.detail);\n};\n\n// Dragged Events.\nconst ON_OUT_CONTAINER = \"$onDragOutContainer\";\nconst ON_OUT_THRESHOLD = \"$onDragOutThreshold\";\n\n//  Interactivity Events.\nconst ON_DRAG_OVER = \"$onDragOver\";\nconst ON_DRAG_LEAVE = \"$onDragLeave\";\n\n// Sibling Events.\nconst ON_LIFT_UP = \"$onLiftUpSiblings\";\nconst ON_MOVE_DOWN = \"$onMoveDownSiblings\";\n\n// Capture DFlex event.\ndocument.addEventListener(\n  ON_OUT_CONTAINER /** or another event */,\n  onDFlexEvent\n);\n\n// Remove it later when dragging is done.\ndocument.removeEventListener(\n  ON_OUT_CONTAINER /** or another event */,\n  onDFlexEvent\n);\n```\n\n### Dragged Event\n\nIt's an event related to capturing dragged positions. This event is fired when\nthe dragged is out of its threshold position `$onDragOutContainer` or out of its\ncontainer `$onDragOutThreshold`.\n\n#### DraggedEvent interface\n\n```ts\ninterface PayloadDraggedEvent {\n  /** Returns element id in the registry  */\n  id: string;\n\n  /** Returns dragged temp index */\n  index: number;\n}\n\n/** For dragged out of threshold or container event. */\ntype DFlexDraggedEvent = CustomEvent\u003cPayloadDraggedEvent\u003e;\n```\n\n#### Interactivity Event\n\nIt's an event related to capturing dragged interactions with other elements.\nThis event is fired when the dragged is over another element `$onDragOver` or\nwhen the dragged is leaving the occupied position `$onDragLeave`.\n\n##### InteractivityEvent interface\n\n```ts\ninterface PayloadInteractivityEvent {\n  /** Returns element id in the registry  */\n  id: string;\n\n  /** Returns element current index */\n  index: number;\n\n  /** Returns the element that triggered the event  */\n  target: HTMLElement;\n}\n\n/** For dragged over an element or leaving an element. */\ntype DFlexInteractivityEvent = CustomEvent\u003cPayloadInteractivityEvent\u003e;\n```\n\n#### Siblings Event\n\nIt's an event related to capturing siblings' positions. This event is fired when\nthe siblings are lifting up `$onLiftUpSiblings` or moving down `$onMoveDownSiblings`\n\n##### SiblingsEvent interface\n\n```ts\ninterface PayloadSiblingsEvent {\n  /** Returns the index where the dragged left  */\n  from: number;\n\n  /** Returns the last index effected of the dragged leaving/entering  */\n  to: number;\n\n  /** Returns an array of sibling ids in order  */\n  siblings: string[];\n}\n\n/** When dragged movement triggers the siblings up/down. */\ntype DFlexSiblingsEvent = CustomEvent\u003cPayloadSiblingsEvent\u003e;\n```\n\n## Listeners\n\nDFlex listeners are more generic than the custom events and responsible for\nmonitoring the entire layout and reporting back to you.\n\nDFlex has two (2) types of listeners:\n\n1. [Layout state listener.](#layout-state-listener)\n2. [Mutation listener.](#mutation-listener)\n\n### Listener Usage\n\n```js\n// app/index.js\n\nconst unsubscribeLayout = store.listeners.subscribe((e) =\u003e {\n  console.info(\"new layout state\", e);\n}, \"layoutState\");\n\n// call it later for clear listeners from memory.\nunsubscribeLayout();\n\nconst unsubscribeMutation = store.listeners.subscribe((e) =\u003e {\n  console.info(\"new mutation state\", e);\n}, \"mutation\");\n\n// call it later for clear listeners from memory.\nunsubscribeMutation();\n```\n\n### Layout state listener\n\nResponsible for monitoring any change that happens to layout interactivity.\n\n#### Layout state listener interface\n\n```ts\ntype LayoutState =\n  | \"pending\" // when DnD is initiated but not activated yet.\n  | \"ready\" // When clicking over the registered element. The element is ready but not being dragged.\n  | \"dragging\" // as expected.\n  | \"dragEnd\" // as expected.\n  | \"dragCancel\"; // When releasing the drag without settling in the new position.\n\ninterface DFlexLayoutStateEvent {\n  type: \"layoutState\";\n  status: LayoutState;\n}\n```\n\n### Mutation listener\n\nResponsible for monitoring DOM mutation that happens during reconciliation.\n\n#### Mutation listener interface\n\n```ts\ntype ElmMutationType = \"committed\";\n\ninterface DFlexElmMutationEvent {\n  type: \"mutation\";\n  status: ElmMutationType;\n  payload: {\n    target: HTMLElement; // HTML element container.\n    ids: string[]; // Committed Elements' id in order.\n  };\n}\n```\n\n## Advanced\n\n### getSerializedElm\n\nDFlex elements are serialized and exported accordingly.\n\n```ts\nstore.getSerializedElm(elmID: string): DFlexSerializedElement | null\n\ntype DFlexSerializedElement = {\n  type: string;\n  version: number;\n  id: string;\n  translate: PointNum | null;\n  grid: PointNum;\n  order: DFlexDOMGenOrder;\n  initialPosition: AxesPoint;\n  rect: BoxRectAbstract;\n  hasTransformedFromOrigin: boolean;\n  hasPendingTransformation: boolean;\n  isVisible: boolean;\n};\n```\n\n### getSerializedScrollContainer\n\nDFlex scroll containers are serialized and exported accordingly. You can get any\nscroll container for any registered element id.\n\n```ts\nstore.getSerializedScrollContainer(elmID: string): DFlexSerializedScroll | null\n\ntype DFlexSerializedScroll = {\n  type: string;\n  version: number;\n  key: string;\n  hasOverFlow: AxesPoint\u003cboolean\u003e;\n  hasDocumentAsContainer: boolean;\n  scrollRect: AbstractBox;\n  scrollContainerRect: AbstractBox;\n  invisibleDistance: AbstractBox;\n  visibleScreen: Dimensions;\n};\n```\n\n### commit\n\nCommit changes to the DOM. `commit` will always do surgical reconciliation. and\nit's the same function that's used in the [options](#commit-changes-to-dom)\n\n```ts\nstore.commit(): void\n```\n\n### isLayoutAvailable\n\nTrue when DFlex is not transforming any elements and not executing any task.\n\n```ts\nisLayoutAvailable(): boolean\n```\n\n### unregister\n\nsafely removing element from store.\n\n```ts\nstore.unregister(id: string): void\n```\n\n### destroy\n\nTo destroy all DFlex instances. This is what you should do when you are done\nwith DnD completely and your app is about to be closed.\n\n```ts\nstore.destroy(): void;\n```\n\n## Project Content 🚀\n\n### [**@dflex/dom-gen**](https://github.com/dflex-js/dflex/tree/main/packages/dflex-dom-gen)\n\nDFlex DOM relations generator algorithm. It Generates relations between DOM elements based\non element depth so all the registered DOM can be called inside registry without\nthe need to call browser API. Read once, implement everywhere.\n\n### [**@dflex/core-instance**](https://github.com/dflex-js/dflex/tree/main/packages/dflex-core-instance)\n\nCore instance is the mirror of interactive element that includes all the properties and methods to manipulate the node.\n\n### [**@dflex/utils**](https://github.com/dflex-js/dflex/tree/main/packages/dflex-utils)\n\nA collection of shared functions. Mostly classes, and types that are used across\nthe project.\n\n### [**@dflex/store**](https://github.com/dflex-js/dflex/tree/main/packages/dflex-store)\n\nDFex Store has main registry for all DOM elements that will be manipulated. It\nis a singleton object that is accessible from anywhere in the application. The\ninitial release was generic but it only has the Core of the library since ^V3.\n\n### [**@dflex/draggable**](https://github.com/dflex-js/dflex/tree/main/packages/dflex-draggable)\n\nLight weight draggable element without extra functionalities that is\nresponsible for interacting with the DOM and moving the affected element(s).\n\n### [**@dflex/dnd**](https://github.com/dflex-js/dflex/tree/main/packages/dflex-dnd)\n\nThe main package that depends on the other packages. It is responsible for the\nmagical logic of the library to introduce the drag and drop interactive\nfunctionality.\n\n## Documentation 📖\n\nFor documentation, more information about DFlex and a live demo, be sure to visit the DFlex website \u003chttps://www.dflex.dev/\u003e\n\n## Contribution 🌎\n\nPRs are welcome, If you wish to help, you can learn more about how you can\ncontribute to this project in the [Contributing guide](/CONTRIBUTING.md).\n\n## Work in progress 🔨\n\nDFlex is a work-in-progress project and currently in development.\n\n## License 🤝\n\nDFlex is [MIT License](LICENSE).\n\n## Author\n\nJalal Maskoun ([@jalal246](https://github.com/jalal246))\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdflex-js%2Fdflex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdflex-js%2Fdflex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdflex-js%2Fdflex/lists"}