Skip to content

Commit 482201c

Browse files
Harshad1610Harshad Sonagaralove1024
authored
feat(directives): add draggable directive for movable popups/modals (#8)
* feat(directives): add draggable directive for movable popups/modals --------- Co-authored-by: Harshad Sonagara <harshad@ifourtechnolab.com> Co-authored-by: Lovepreet Singh <lsk18mto@gmail.com>
1 parent 8f58a18 commit 482201c

File tree

11 files changed

+2300
-2
lines changed

11 files changed

+2300
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export class UserService {
9494
|----------|-----------|
9595
| **Constants** | `breakpoints`, `device`, `file-extension`, `host-platforms`, `http`, `key`, `regex`, `sort-direction`, `time`, `types` |
9696
| **Decorators** | `cache`, `catch-error`, `debounce`, `log-execution-time`, `memoize`, `only-in-browser`, `throttle` |
97-
| **Directives** | `auto-focus`, `click-outside`, `click-throttle`, `hover-class`, `infinite-scroll`, `numbers-only`, `press-enter`, `repeat`, `resized`, `shortcut`, `typed-template`, `visibility-change` |
97+
| **Directives** | `auto-focus`, `click-outside`, `click-throttle`, `draggable`, `hover-class`, `infinite-scroll`, `numbers-only`, `press-enter`, `repeat`, `resized`, `shortcut`, `typed-template`, `visibility-change` |
9898
| **Guards** | `param`, `query-param`, `unsaved-changes` |
9999
| **Interceptors** | `base-url`, `cache`, `correlation-id`, `encryption`, `jwt`, `performance`, `timeout` |
100100
| **Pipes** | `bytes`, `call`, `first-error-key`, `highlight-search`, `initials`, `pluralize`, `range`, `safe-html`, `time-ago`, `truncate` |

jest.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,12 @@ module.exports = {
3333
// Make sure Jest only treats *.spec.ts as test files
3434
testMatch: ['**/?(*.)+(spec).[jt]s?(x)'],
3535
testEnvironment: 'jsdom',
36+
coverageThreshold: {
37+
global: {
38+
branches: 100,
39+
functions: 100,
40+
lines: 100,
41+
statements: 100,
42+
},
43+
},
3644
};
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
h4 {
2+
margin: 24px 0 12px;
3+
color: #333;
4+
font-size: 16px;
5+
6+
&:first-child {
7+
margin-top: 0;
8+
}
9+
}
10+
11+
.demo-wrapper {
12+
border: 2px dashed #ccc;
13+
border-radius: 8px;
14+
padding: 20px;
15+
min-height: 120px;
16+
background: #f9f9f9;
17+
margin-bottom: 8px;
18+
}
19+
20+
.draggable-box {
21+
display: inline-flex;
22+
flex-direction: column;
23+
gap: 8px;
24+
padding: 20px;
25+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
26+
border-radius: 12px;
27+
color: white;
28+
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
29+
cursor: grab;
30+
position: relative;
31+
32+
&:active {
33+
cursor: grabbing;
34+
}
35+
36+
strong {
37+
font-size: 18px;
38+
}
39+
40+
span {
41+
font-size: 12px;
42+
font-family: monospace;
43+
opacity: 0.9;
44+
}
45+
}
46+
47+
// Modal Example
48+
.modal-card {
49+
display: inline-block;
50+
background: white;
51+
border-radius: 8px;
52+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
53+
overflow: hidden;
54+
position: relative;
55+
min-width: 250px;
56+
}
57+
58+
.modal-header {
59+
padding: 12px 16px;
60+
background: #1976d2;
61+
color: white;
62+
font-weight: 500;
63+
cursor: grab;
64+
65+
&:active {
66+
cursor: grabbing;
67+
}
68+
}
69+
70+
.modal-body {
71+
padding: 16px;
72+
73+
p {
74+
margin: 0 0 8px;
75+
font-size: 14px;
76+
color: #555;
77+
78+
&:last-child {
79+
margin-bottom: 0;
80+
}
81+
}
82+
}
83+
84+
// Boundary Constrained Example
85+
.boundary-area {
86+
min-height: 150px;
87+
background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%);
88+
border-color: #4caf50;
89+
}
90+
91+
.constrained-box {
92+
display: inline-flex;
93+
flex-direction: column;
94+
gap: 8px;
95+
padding: 16px 20px;
96+
background: linear-gradient(135deg, #4caf50 0%, #2e7d32 100%);
97+
border-radius: 12px;
98+
color: white;
99+
box-shadow: 0 4px 15px rgba(76, 175, 80, 0.4);
100+
cursor: grab;
101+
position: relative;
102+
103+
&:active {
104+
cursor: grabbing;
105+
}
106+
107+
strong {
108+
font-size: 16px;
109+
}
110+
111+
span {
112+
font-size: 12px;
113+
opacity: 0.9;
114+
}
115+
}
116+
117+
.info {
118+
color: #666;
119+
font-size: 14px;
120+
margin: 0 0 16px;
121+
122+
code {
123+
background: #f0f0f0;
124+
padding: 2px 6px;
125+
border-radius: 4px;
126+
font-size: 13px;
127+
color: #d63384;
128+
}
129+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { Component, signal } from '@angular/core';
2+
import {
3+
DraggableDirective,
4+
DraggableDragEvent,
5+
} from 'ngx-oneforall/directives/draggable';
6+
7+
@Component({
8+
selector: 'lib-draggable-demo',
9+
imports: [DraggableDirective],
10+
template: `
11+
<!-- Example 1: Basic Draggable -->
12+
<h4>1. Basic Draggable</h4>
13+
<div class="demo-wrapper">
14+
<div class="draggable-box" makeDraggable (dragMove)="onDragMove($event)">
15+
<strong>Drag Me!</strong>
16+
<span>Position: ({{ position().x }}, {{ position().y }})</span>
17+
</div>
18+
</div>
19+
<p class="info">Click and drag the purple box to move it freely.</p>
20+
21+
<!-- Example 2: Modal with Drag Handle -->
22+
<h4>2. Modal with Drag Handle</h4>
23+
<div class="demo-wrapper">
24+
<div class="modal-card" #modal>
25+
<div class="modal-header" makeDraggable [makeDraggableTarget]="modal">
26+
<span>Drag this header</span>
27+
</div>
28+
<div class="modal-body">
29+
<p>Only the header is draggable, but the entire modal moves.</p>
30+
<p>This body area cannot initiate drag.</p>
31+
</div>
32+
</div>
33+
</div>
34+
<p class="info">
35+
Use <code>[makeDraggableTarget]</code> to move a parent element by
36+
dragging a child.
37+
</p>
38+
39+
<!-- Example 3: Boundary Constrained -->
40+
<h4>3. Boundary Constrained</h4>
41+
<div class="demo-wrapper boundary-area">
42+
<div
43+
class="constrained-box"
44+
makeDraggable
45+
[makeDraggableBoundary]="'parent'">
46+
<strong>Constrained</strong>
47+
<span>Can't leave the box!</span>
48+
</div>
49+
</div>
50+
<p class="info">
51+
Use <code>[makeDraggableBoundary]="'parent'"</code> to keep element inside
52+
its container.
53+
</p>
54+
`,
55+
styleUrl: './draggable-demo.component.scss',
56+
})
57+
export class DraggableDemoComponent {
58+
position = signal({ x: 0, y: 0 });
59+
60+
onDragMove(event: DraggableDragEvent) {
61+
this.position.set({ x: Math.round(event.x), y: Math.round(event.y) });
62+
}
63+
}

0 commit comments

Comments
 (0)