@consent.software/catalog 1.3.5 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@consent.software/catalog",
3
- "version": "1.3.5",
3
+ "version": "1.4.1",
4
4
  "private": false,
5
5
  "description": "A library of web components designed to integrate robust consent management capabilities into web applications, ensuring compliance with privacy regulations.",
6
6
  "exports": {
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@consent.software/catalog',
6
- version: '1.3.5',
6
+ version: '1.4.1',
7
7
  description: 'A library of web components designed to integrate robust consent management capabilities into web applications, ensuring compliance with privacy regulations.'
8
8
  }
@@ -1,19 +1,32 @@
1
1
  import { LitElement, html, css, type TemplateResult } from 'lit';
2
2
  import { customElement } from 'lit/decorators.js';
3
3
  import { property } from 'lit/decorators/property.js';
4
-
5
4
  import { delayFor } from '@push.rocks/smartdelay';
6
5
 
7
6
  @customElement('consentsoftware-toggle')
8
7
  export class ConsentsoftwareToggle extends LitElement {
9
- public static demo = () => html`<consentsoftware-toggle></consentsoftware-toggle>`;
10
-
11
8
  @property({ type: Boolean })
12
9
  public required = false;
13
10
 
14
11
  @property({ type: Boolean, reflect: true })
15
12
  public selected = false;
16
13
 
14
+ /**
15
+ * We always track the knob’s left offset in `currentX`.
16
+ * - 0 => fully left
17
+ * - 30 => fully right
18
+ */
19
+ private currentX = 0;
20
+
21
+ /**
22
+ * Drag state
23
+ */
24
+ private isDragging = false;
25
+ private hasDragged = false;
26
+ private startX = 0; // pointerdown offset
27
+ private readonly knobWidth = 30;
28
+ private readonly trackWidth = 60;
29
+
17
30
  public static styles = css`
18
31
  :host {
19
32
  display: block;
@@ -24,17 +37,21 @@ export class ConsentsoftwareToggle extends LitElement {
24
37
  margin-bottom: 16px;
25
38
  }
26
39
 
40
+ .toggle {
41
+ user-select: none; /* helps avoid text selection on drag */
42
+ }
43
+
27
44
  .toggleKnobArea {
28
- cursor: pointer;
45
+ position: relative;
29
46
  margin: auto;
30
47
  height: 30px;
31
48
  width: 60px;
32
49
  border-radius: 20px;
33
50
  background: rgba(255, 255, 255, 0.1);
34
- position: relative;
35
- overflow: hidden;
36
- transition: all 0.2s;
37
51
  border: 1px solid rgba(255, 255, 255, 0);
52
+ overflow: hidden;
53
+ transition: background 0.2s ease;
54
+ cursor: pointer;
38
55
  }
39
56
 
40
57
  :host([selected]) .toggleKnobArea {
@@ -42,19 +59,27 @@ export class ConsentsoftwareToggle extends LitElement {
42
59
  }
43
60
 
44
61
  .toggleKnobMover {
45
- transition: all 0.2s;
62
+ position: relative;
63
+ height: 100%;
64
+ width: 100%;
46
65
  }
47
66
 
48
67
  .toggleKnobInner {
49
- height: 30px;
50
- width: 30px;
51
- border-radius: 15px;
52
- background: rgba(255, 255, 255, 0.5);
53
68
  position: absolute;
54
69
  top: 0;
55
70
  left: 0;
56
- transition: all 0.2s;
71
+ width: 30px;
72
+ height: 30px;
73
+ border-radius: 15px;
74
+ background: rgba(255, 255, 255, 0.5);
75
+ transition: left 0.2s ease, background 0.2s ease;
57
76
  transform: scale(0.7);
77
+ /* Prevent scroll gestures on mobile */
78
+ touch-action: none;
79
+ }
80
+
81
+ .toggleKnobInner.dragging {
82
+ transition: background 0.2s ease;
58
83
  }
59
84
 
60
85
  :host([selected]) .toggleKnobInner {
@@ -78,50 +103,153 @@ export class ConsentsoftwareToggle extends LitElement {
78
103
  public render(): TemplateResult {
79
104
  return html`
80
105
  <div class="label">${this.getText()}</div>
106
+ <!-- A user can click anywhere in this toggle area. -->
81
107
  <div class="toggle" @click=${this.handleClick}>
82
108
  <div class="toggleKnobArea">
83
109
  <div class="toggleKnobMover">
84
- <div class="toggleKnobInner"></div>
110
+ <!-- The knob itself, with pointer events for dragging. -->
111
+ <div
112
+ class="toggleKnobInner"
113
+ style="left: ${this.currentX}px;"
114
+ @pointerdown=${this.onPointerDown}
115
+ @pointermove=${this.onPointerMove}
116
+ @pointerup=${this.onPointerUp}
117
+ @pointercancel=${this.onPointerUp}
118
+ ></div>
85
119
  </div>
86
120
  </div>
87
121
  </div>
88
122
  `;
89
123
  }
90
124
 
91
- public async firstUpdated(_changedProperties: Map<string | number | symbol, unknown>) {
92
- super.firstUpdated(_changedProperties);
125
+ /**
126
+ * If required = true on first render, auto-select and set the knob to the right.
127
+ */
128
+ public async firstUpdated() {
93
129
  if (this.required) {
130
+ // If "required" => always selected
94
131
  this.selected = true;
95
- this.syncSelection();
132
+ this.currentX = this.knobWidth; // 30
133
+ this.requestUpdate();
134
+ } else {
135
+ // If not required, set knob to 0 or 30 depending on `selected`
136
+ this.currentX = this.selected ? this.knobWidth : 0;
137
+ this.requestUpdate();
96
138
  }
97
139
  }
98
140
 
99
- public async handleClick(mouseEvent) {
141
+ /**
142
+ * CLICK HANDLER
143
+ */
144
+ public async handleClick(event: MouseEvent) {
145
+ // If the user truly dragged the knob, skip the normal click toggle.
146
+ if (this.isDragging || this.hasDragged) {
147
+ event.stopPropagation();
148
+ event.preventDefault();
149
+ return;
150
+ }
151
+
100
152
  if (this.required) {
101
- const moverElement: HTMLDivElement = this.shadowRoot.querySelector('.toggleKnobMover');
102
- moverElement.style.transform = 'translateX(20px)';
103
- await delayFor(250);
104
- moverElement.style.transform = 'translateX(30px)';
153
+ // small bounce from 30 -> 20 -> 30
154
+ this.currentX = this.knobWidth; // ensure at 30
155
+ this.requestUpdate();
156
+ await new Promise((r) => setTimeout(r, 10));
157
+
158
+ this.currentX = 20; // small bounce left
159
+ this.requestUpdate();
160
+ await delayFor(200);
161
+
162
+ this.currentX = this.knobWidth; // back to 30
163
+ this.requestUpdate();
105
164
  return;
106
165
  }
166
+
167
+ // Normal toggle if no drag & not required
168
+ event.stopPropagation();
169
+ event.preventDefault();
170
+
107
171
  this.selected = !this.selected;
108
- mouseEvent.stopPropagation();
109
- mouseEvent.preventDefault();
110
- this.syncSelection();
172
+ this.currentX = this.selected ? this.knobWidth : 0; // snap knob left(0) or right(30)
173
+ this.requestUpdate();
174
+
111
175
  this.dispatchEvent(new CustomEvent('toggle', { detail: { selected: this.selected } }));
176
+ delayFor(0).then(() => {
177
+ this.hasDragged = false;
178
+ });
112
179
  }
113
180
 
114
- public async syncSelection() {
115
- console.log(`Selected ${this.selected}`);
116
- const moverElement: HTMLDivElement = this.shadowRoot.querySelector('.toggleKnobMover');
117
- if (this.selected) {
118
- moverElement.style.transform = 'translateX(30px)';
119
- } else {
120
- moverElement.style.transform = 'translateX(0px)';
181
+ /**
182
+ * DRAG HANDLERS (pointer events)
183
+ */
184
+ private onPointerDown(event: PointerEvent) {
185
+ if (this.required) {
186
+ // If "required", disallow manual dragging
187
+ return;
188
+ }
189
+
190
+ // Start dragging
191
+ this.isDragging = true;
192
+ // The difference between the pointer’s X and the knob’s current position
193
+ this.startX = event.clientX - this.currentX;
194
+
195
+ // capture pointer so we keep receiving pointermove/pointerup
196
+ (event.target as HTMLElement).setPointerCapture(event.pointerId);
197
+ }
198
+
199
+ private onPointerMove(event: PointerEvent) {
200
+ if (!this.isDragging) return;
201
+ const newX = event.clientX - this.startX;
202
+ this.hasDragged = true;
203
+ const toggleKnobInner: HTMLDivElement = this.shadowRoot.querySelector('.toggleKnobInner');
204
+ toggleKnobInner.classList.add('dragging');
205
+
206
+ // Clamp
207
+ this.currentX = Math.max(0, Math.min(newX, this.trackWidth - this.knobWidth));
208
+ this.requestUpdate();
209
+ }
210
+
211
+ private onPointerUp(event: PointerEvent) {
212
+ if (!this.isDragging) return;
213
+ (event.target as HTMLElement).releasePointerCapture(event.pointerId);
214
+ this.isDragging = false;
215
+
216
+ // If we didn’t truly drag, pointerup does nothing; click handler handles toggling.
217
+ if (!this.hasDragged) {
218
+ return;
219
+ }
220
+ const toggleKnobInner: HTMLDivElement = this.shadowRoot.querySelector('.toggleKnobInner');
221
+ toggleKnobInner.classList.remove('dragging');
222
+
223
+ // Real drag => decide final side
224
+ const midpoint = (this.trackWidth - this.knobWidth) / 2; // 15
225
+ this.selected = this.currentX > midpoint;
226
+ this.currentX = this.selected ? this.knobWidth : 0; // snap to edge
227
+ this.requestUpdate();
228
+
229
+ // Dispatch toggle event
230
+ this.dispatchEvent(new CustomEvent('toggle', { detail: { selected: this.selected } }));
231
+ delayFor(0).then(() => {
232
+ this.hasDragged = false;
233
+ });
234
+ }
235
+
236
+ /**
237
+ * If external code sets `selected = true/false`, we also sync the knob position
238
+ */
239
+ protected updated(changedProperties: Map<PropertyKey, unknown>): void {
240
+ if (
241
+ changedProperties.has('selected') &&
242
+ !this.isDragging &&
243
+ !this.hasDragged &&
244
+ !this.required
245
+ ) {
246
+ this.currentX = this.selected ? this.knobWidth : 0;
247
+ this.requestUpdate();
121
248
  }
249
+ super.updated(changedProperties);
122
250
  }
123
251
 
124
- public getText() {
252
+ public getText(): string | null {
125
253
  return this.textContent;
126
254
  }
127
255
  }