@financial-times/cp-content-pipeline-ui 13.3.2 → 13.4.0
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/CHANGELOG.md +7 -0
- package/lib/components/content-tree/CustomCodeComponent/client/index.d.ts +5 -1
- package/lib/components/content-tree/CustomCodeComponent/client/index.js +37 -2
- package/lib/components/content-tree/CustomCodeComponent/client/index.js.map +1 -1
- package/lib/components/content-tree/CustomCodeComponent/client/index.spec.d.ts +1 -0
- package/lib/components/content-tree/CustomCodeComponent/client/index.spec.js +89 -0
- package/lib/components/content-tree/CustomCodeComponent/client/index.spec.js.map +1 -0
- package/package.json +1 -1
- package/src/components/content-tree/CustomCodeComponent/client/index.spec.ts +96 -0
- package/src/components/content-tree/CustomCodeComponent/client/index.ts +50 -2
- package/src/types/window.d.ts +2 -1
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -574,6 +574,13 @@
|
|
|
574
574
|
* @financial-times/cp-content-pipeline-client bumped from ^3.7.2 to ^3.7.3
|
|
575
575
|
* @financial-times/cp-content-pipeline-schema bumped from ^2.10.1 to ^2.10.2
|
|
576
576
|
|
|
577
|
+
## [13.4.0](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-ui-v13.3.2...cp-content-pipeline-ui-v13.4.0) (2026-09-17)
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
### Features
|
|
581
|
+
|
|
582
|
+
* horizontal scrolling in ft-app ([6e31e9a](https://github.com/Financial-Times/cp-content-pipeline/commit/6e31e9aea847009cb426b9b83828145fd1d0bba1))
|
|
583
|
+
|
|
577
584
|
## [13.3.2](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-ui-v13.3.1...cp-content-pipeline-ui-v13.3.2) (2026-09-15)
|
|
578
585
|
|
|
579
586
|
|
|
@@ -10,7 +10,11 @@ declare class CustomCodeComponentClient {
|
|
|
10
10
|
* Track the mount of a custom code component
|
|
11
11
|
*/
|
|
12
12
|
tracking: CustomCodeComponentTracker;
|
|
13
|
-
|
|
13
|
+
ftAppSwipeGestures: boolean | undefined;
|
|
14
|
+
element: Element;
|
|
15
|
+
constructor(el: Element, ftAppSwipeGestures?: boolean);
|
|
16
|
+
private cccTouched;
|
|
17
|
+
private cccReleased;
|
|
14
18
|
static init(rootEl?: Element): CustomCodeComponentClient[] | void;
|
|
15
19
|
static destroy(components: CustomCodeComponentClient[]): void;
|
|
16
20
|
}
|
|
@@ -12,7 +12,11 @@ class CustomCodeComponentClient {
|
|
|
12
12
|
* Track the mount of a custom code component
|
|
13
13
|
*/
|
|
14
14
|
tracking;
|
|
15
|
-
|
|
15
|
+
ftAppSwipeGestures;
|
|
16
|
+
element;
|
|
17
|
+
constructor(el, ftAppSwipeGestures) {
|
|
18
|
+
this.element = el;
|
|
19
|
+
this.ftAppSwipeGestures = ftAppSwipeGestures;
|
|
16
20
|
const trackingData = {
|
|
17
21
|
component: {
|
|
18
22
|
id: el.getAttribute('id') ?? '',
|
|
@@ -21,14 +25,45 @@ class CustomCodeComponentClient {
|
|
|
21
25
|
},
|
|
22
26
|
};
|
|
23
27
|
this.tracking = new tracking_1.CustomCodeComponentTracker(el, trackingData);
|
|
28
|
+
// ft-app only: ftAppSwipeGestures is false by default,
|
|
29
|
+
// meaning horizontal swipe gestures navigate to adjacent articles in the swipe flow
|
|
30
|
+
// if set to true, the ccc will signal to ft-app that the component swipe gestures should be given precendence.
|
|
31
|
+
// useful for horizontal scrolling of elements such as carousels
|
|
32
|
+
if (this.ftAppSwipeGestures) {
|
|
33
|
+
el.addEventListener('pointerdown', this.cccTouched);
|
|
34
|
+
el.addEventListener('pointerup', this.cccReleased);
|
|
35
|
+
el.addEventListener('pointercancel', this.cccReleased);
|
|
36
|
+
}
|
|
24
37
|
}
|
|
38
|
+
cccTouched = () => {
|
|
39
|
+
if (!window.ftmob_scrolling) {
|
|
40
|
+
window.ftmob_scrolling = this;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
cccReleased = () => {
|
|
44
|
+
if (window.ftmob_scrolling === this) {
|
|
45
|
+
window.ftmob_scrolling = false;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
25
48
|
static init(rootEl) {
|
|
26
49
|
const root = rootEl instanceof Element ? rootEl : document;
|
|
27
|
-
return Array.from(root.querySelectorAll('custom-code-component:not([data-initialised])')).map((el) =>
|
|
50
|
+
return Array.from(root.querySelectorAll('custom-code-component:not([data-initialised])')).map((el) => {
|
|
51
|
+
const ftAppSwipeGestures = el.getAttribute('enable-ft-app-swipe-gestures') === 'true';
|
|
52
|
+
const client = new CustomCodeComponentClient(el, ftAppSwipeGestures);
|
|
53
|
+
return client;
|
|
54
|
+
});
|
|
28
55
|
}
|
|
29
56
|
static destroy(components) {
|
|
30
57
|
components.forEach((component) => {
|
|
31
58
|
component.tracking.destroy();
|
|
59
|
+
if (component.ftAppSwipeGestures) {
|
|
60
|
+
component.element.removeEventListener('pointerdown', component.cccTouched);
|
|
61
|
+
component.element.removeEventListener('pointerup', component.cccReleased);
|
|
62
|
+
component.element.removeEventListener('pointercancel', component.cccReleased);
|
|
63
|
+
if (window.ftmob_scrolling === component) {
|
|
64
|
+
window.ftmob_scrolling = false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
32
67
|
});
|
|
33
68
|
}
|
|
34
69
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/components/content-tree/CustomCodeComponent/client/index.ts"],"names":[],"mappings":";;AAAA,yCAAuD;AAEvD;;;;;GAKG;AACH,MAAM,yBAAyB;IAC7B;;OAEG;IACH,QAAQ,CAA4B;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/components/content-tree/CustomCodeComponent/client/index.ts"],"names":[],"mappings":";;AAAA,yCAAuD;AAEvD;;;;;GAKG;AACH,MAAM,yBAAyB;IAC7B;;OAEG;IACH,QAAQ,CAA4B;IACpC,kBAAkB,CAAqB;IACvC,OAAO,CAAS;IAEhB,YAAY,EAAW,EAAE,kBAA4B;QACnD,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;QACjB,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,CAAA;QAC5C,MAAM,YAAY,GAAG;YACnB,SAAS,EAAE;gBACT,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE;gBAC/B,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE;gBACnC,IAAI,EAAE,uBAAuB;aAC9B;SACF,CAAA;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,qCAA0B,CAC5C,EAAiB,EACjB,YAAY,CACb,CAAA;QAED,uDAAuD;QACvD,oFAAoF;QACpF,+GAA+G;QAC/G,gEAAgE;QAChE,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5B,EAAE,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;YACnD,EAAE,CAAC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,CAAA;YAClD,EAAE,CAAC,gBAAgB,CAAC,eAAe,EAAE,IAAI,CAAC,WAAW,CAAC,CAAA;QACxD,CAAC;IACH,CAAC;IAEO,UAAU,GAAG,GAAS,EAAE;QAC9B,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAC5B,MAAM,CAAC,eAAe,GAAG,IAAI,CAAA;QAC/B,CAAC;IACH,CAAC,CAAA;IAEO,WAAW,GAAG,GAAS,EAAE;QAC/B,IAAI,MAAM,CAAC,eAAe,KAAK,IAAI,EAAE,CAAC;YACpC,MAAM,CAAC,eAAe,GAAG,KAAK,CAAA;QAChC,CAAC;IACH,CAAC,CAAA;IAED,MAAM,CAAC,IAAI,CAAC,MAAgB;QAC1B,MAAM,IAAI,GAAG,MAAM,YAAY,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAA;QAC1D,OAAO,KAAK,CAAC,IAAI,CACf,IAAI,CAAC,gBAAgB,CAAC,+CAA+C,CAAC,CACvE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;YACX,MAAM,kBAAkB,GACtB,EAAE,CAAC,YAAY,CAAC,8BAA8B,CAAC,KAAK,MAAM,CAAA;YAC5D,MAAM,MAAM,GAAG,IAAI,yBAAyB,CAAC,EAAE,EAAE,kBAAkB,CAAC,CAAA;YACpE,OAAO,MAAM,CAAA;QACf,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,UAAuC;QACpD,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE;YAC/B,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAA;YAC5B,IAAI,SAAS,CAAC,kBAAkB,EAAE,CAAC;gBACjC,SAAS,CAAC,OAAO,CAAC,mBAAmB,CACnC,aAAa,EACb,SAAS,CAAC,UAAU,CACrB,CAAA;gBACD,SAAS,CAAC,OAAO,CAAC,mBAAmB,CACnC,WAAW,EACX,SAAS,CAAC,WAAW,CACtB,CAAA;gBACD,SAAS,CAAC,OAAO,CAAC,mBAAmB,CACnC,eAAe,EACf,SAAS,CAAC,WAAW,CACtB,CAAA;gBACD,IAAI,MAAM,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;oBACzC,MAAM,CAAC,eAAe,GAAG,KAAK,CAAA;gBAChC,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;CACF;AAED,kBAAe,yBAAyB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const tracking_1 = require("./tracking");
|
|
7
|
+
const index_1 = __importDefault(require("./index"));
|
|
8
|
+
jest.mock('./tracking', () => ({
|
|
9
|
+
CustomCodeComponentTracker: jest.fn(),
|
|
10
|
+
}));
|
|
11
|
+
describe('CustomCodeComponentClient', () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
document.body.innerHTML = '';
|
|
14
|
+
});
|
|
15
|
+
it('should initialize correctly', () => {
|
|
16
|
+
const el = document.createElement('custom-code-component');
|
|
17
|
+
el.setAttribute('id', 'test-id');
|
|
18
|
+
el.setAttribute('path', 'test-path');
|
|
19
|
+
document.body.appendChild(el);
|
|
20
|
+
const clients = index_1.default.init();
|
|
21
|
+
expect(clients).toHaveLength(1);
|
|
22
|
+
const client = clients[0];
|
|
23
|
+
expect(client).toBeInstanceOf(index_1.default);
|
|
24
|
+
expect(tracking_1.CustomCodeComponentTracker).toHaveBeenCalledWith(el, {
|
|
25
|
+
component: {
|
|
26
|
+
id: 'test-id',
|
|
27
|
+
name: 'test-path',
|
|
28
|
+
type: 'custom-code-component',
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
it('should return an empty array if no elements are present', () => {
|
|
33
|
+
const clients = index_1.default.init();
|
|
34
|
+
expect(clients).toHaveLength(0);
|
|
35
|
+
});
|
|
36
|
+
it('should handle multiple elements correctly', () => {
|
|
37
|
+
const el1 = document.createElement('custom-code-component');
|
|
38
|
+
el1.setAttribute('id', 'test-id-1');
|
|
39
|
+
el1.setAttribute('path', 'test-path-1');
|
|
40
|
+
document.body.appendChild(el1);
|
|
41
|
+
const el2 = document.createElement('custom-code-component');
|
|
42
|
+
el2.setAttribute('id', 'test-id-2');
|
|
43
|
+
el2.setAttribute('path', 'test-path-2');
|
|
44
|
+
document.body.appendChild(el2);
|
|
45
|
+
const clients = index_1.default.init();
|
|
46
|
+
expect(clients).toHaveLength(2);
|
|
47
|
+
expect(tracking_1.CustomCodeComponentTracker).toHaveBeenCalledWith(el1, {
|
|
48
|
+
component: {
|
|
49
|
+
id: 'test-id-1',
|
|
50
|
+
name: 'test-path-1',
|
|
51
|
+
type: 'custom-code-component',
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
expect(tracking_1.CustomCodeComponentTracker).toHaveBeenCalledWith(el2, {
|
|
55
|
+
component: {
|
|
56
|
+
id: 'test-id-2',
|
|
57
|
+
name: 'test-path-2',
|
|
58
|
+
type: 'custom-code-component',
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
it('should initialise with ft app scrolling if the element has the appropriate attribute', () => {
|
|
63
|
+
const el = document.createElement('custom-code-component');
|
|
64
|
+
el.setAttribute('id', 'test-id');
|
|
65
|
+
el.setAttribute('path', 'test-path');
|
|
66
|
+
el.setAttribute('enable-ft-app-swipe-gestures', 'true');
|
|
67
|
+
document.body.appendChild(el);
|
|
68
|
+
const client = index_1.default.init()[0];
|
|
69
|
+
expect(client?.ftAppSwipeGestures).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
it('should not initialize with ft app scrolling if the attribute is not set to true', () => {
|
|
72
|
+
const el = document.createElement('custom-code-component');
|
|
73
|
+
el.setAttribute('id', 'test-id');
|
|
74
|
+
el.setAttribute('path', 'test-path');
|
|
75
|
+
el.setAttribute('enable-ft-app-swipe-gestures', 'false');
|
|
76
|
+
document.body.appendChild(el);
|
|
77
|
+
const client = index_1.default.init()[0];
|
|
78
|
+
expect(client?.ftAppSwipeGestures).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
it('should not initialize with ft app scrolling if the attribute is missing', () => {
|
|
81
|
+
const el = document.createElement('custom-code-component');
|
|
82
|
+
el.setAttribute('id', 'test-id');
|
|
83
|
+
el.setAttribute('path', 'test-path');
|
|
84
|
+
document.body.appendChild(el);
|
|
85
|
+
const client = index_1.default.init()[0];
|
|
86
|
+
expect(client?.ftAppSwipeGestures).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
//# sourceMappingURL=index.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.spec.js","sourceRoot":"","sources":["../../../../../src/components/content-tree/CustomCodeComponent/client/index.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { CustomCodeComponentTracker } from './tracking'
|
|
2
|
+
import CustomCodeComponentClient from './index'
|
|
3
|
+
|
|
4
|
+
jest.mock('./tracking', () => ({
|
|
5
|
+
CustomCodeComponentTracker: jest.fn(),
|
|
6
|
+
}))
|
|
7
|
+
|
|
8
|
+
describe('CustomCodeComponentClient', () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
document.body.innerHTML = ''
|
|
11
|
+
})
|
|
12
|
+
it('should initialize correctly', () => {
|
|
13
|
+
const el = document.createElement('custom-code-component')
|
|
14
|
+
el.setAttribute('id', 'test-id')
|
|
15
|
+
el.setAttribute('path', 'test-path')
|
|
16
|
+
document.body.appendChild(el)
|
|
17
|
+
|
|
18
|
+
const clients = CustomCodeComponentClient.init()
|
|
19
|
+
expect(clients).toHaveLength(1)
|
|
20
|
+
const client = clients![0]
|
|
21
|
+
expect(client).toBeInstanceOf(CustomCodeComponentClient)
|
|
22
|
+
expect(CustomCodeComponentTracker).toHaveBeenCalledWith(el, {
|
|
23
|
+
component: {
|
|
24
|
+
id: 'test-id',
|
|
25
|
+
name: 'test-path',
|
|
26
|
+
type: 'custom-code-component',
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should return an empty array if no elements are present', () => {
|
|
32
|
+
const clients = CustomCodeComponentClient.init()
|
|
33
|
+
expect(clients).toHaveLength(0)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should handle multiple elements correctly', () => {
|
|
37
|
+
const el1 = document.createElement('custom-code-component')
|
|
38
|
+
el1.setAttribute('id', 'test-id-1')
|
|
39
|
+
el1.setAttribute('path', 'test-path-1')
|
|
40
|
+
document.body.appendChild(el1)
|
|
41
|
+
|
|
42
|
+
const el2 = document.createElement('custom-code-component')
|
|
43
|
+
el2.setAttribute('id', 'test-id-2')
|
|
44
|
+
el2.setAttribute('path', 'test-path-2')
|
|
45
|
+
document.body.appendChild(el2)
|
|
46
|
+
|
|
47
|
+
const clients = CustomCodeComponentClient.init()
|
|
48
|
+
expect(clients).toHaveLength(2)
|
|
49
|
+
expect(CustomCodeComponentTracker).toHaveBeenCalledWith(el1, {
|
|
50
|
+
component: {
|
|
51
|
+
id: 'test-id-1',
|
|
52
|
+
name: 'test-path-1',
|
|
53
|
+
type: 'custom-code-component',
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
expect(CustomCodeComponentTracker).toHaveBeenCalledWith(el2, {
|
|
57
|
+
component: {
|
|
58
|
+
id: 'test-id-2',
|
|
59
|
+
name: 'test-path-2',
|
|
60
|
+
type: 'custom-code-component',
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('should initialise with ft app scrolling if the element has the appropriate attribute', () => {
|
|
66
|
+
const el = document.createElement('custom-code-component')
|
|
67
|
+
el.setAttribute('id', 'test-id')
|
|
68
|
+
el.setAttribute('path', 'test-path')
|
|
69
|
+
el.setAttribute('enable-ft-app-swipe-gestures', 'true')
|
|
70
|
+
document.body.appendChild(el)
|
|
71
|
+
|
|
72
|
+
const client = CustomCodeComponentClient.init()![0]
|
|
73
|
+
expect(client?.ftAppSwipeGestures).toBe(true)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('should not initialize with ft app scrolling if the attribute is not set to true', () => {
|
|
77
|
+
const el = document.createElement('custom-code-component')
|
|
78
|
+
el.setAttribute('id', 'test-id')
|
|
79
|
+
el.setAttribute('path', 'test-path')
|
|
80
|
+
el.setAttribute('enable-ft-app-swipe-gestures', 'false')
|
|
81
|
+
document.body.appendChild(el)
|
|
82
|
+
|
|
83
|
+
const client = CustomCodeComponentClient.init()![0]
|
|
84
|
+
expect(client?.ftAppSwipeGestures).toBe(false)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('should not initialize with ft app scrolling if the attribute is missing', () => {
|
|
88
|
+
const el = document.createElement('custom-code-component')
|
|
89
|
+
el.setAttribute('id', 'test-id')
|
|
90
|
+
el.setAttribute('path', 'test-path')
|
|
91
|
+
document.body.appendChild(el)
|
|
92
|
+
|
|
93
|
+
const client = CustomCodeComponentClient.init()![0]
|
|
94
|
+
expect(client?.ftAppSwipeGestures).toBe(false)
|
|
95
|
+
})
|
|
96
|
+
})
|
|
@@ -11,8 +11,12 @@ class CustomCodeComponentClient {
|
|
|
11
11
|
* Track the mount of a custom code component
|
|
12
12
|
*/
|
|
13
13
|
tracking: CustomCodeComponentTracker
|
|
14
|
+
ftAppSwipeGestures: boolean | undefined
|
|
15
|
+
element: Element
|
|
14
16
|
|
|
15
|
-
constructor(el: Element) {
|
|
17
|
+
constructor(el: Element, ftAppSwipeGestures?: boolean) {
|
|
18
|
+
this.element = el
|
|
19
|
+
this.ftAppSwipeGestures = ftAppSwipeGestures
|
|
16
20
|
const trackingData = {
|
|
17
21
|
component: {
|
|
18
22
|
id: el.getAttribute('id') ?? '',
|
|
@@ -24,18 +28,62 @@ class CustomCodeComponentClient {
|
|
|
24
28
|
el as HTMLElement,
|
|
25
29
|
trackingData
|
|
26
30
|
)
|
|
31
|
+
|
|
32
|
+
// ft-app only: ftAppSwipeGestures is false by default,
|
|
33
|
+
// meaning horizontal swipe gestures navigate to adjacent articles in the swipe flow
|
|
34
|
+
// if set to true, the ccc will signal to ft-app that the component swipe gestures should be given precendence.
|
|
35
|
+
// useful for horizontal scrolling of elements such as carousels
|
|
36
|
+
if (this.ftAppSwipeGestures) {
|
|
37
|
+
el.addEventListener('pointerdown', this.cccTouched)
|
|
38
|
+
el.addEventListener('pointerup', this.cccReleased)
|
|
39
|
+
el.addEventListener('pointercancel', this.cccReleased)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private cccTouched = (): void => {
|
|
44
|
+
if (!window.ftmob_scrolling) {
|
|
45
|
+
window.ftmob_scrolling = this
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private cccReleased = (): void => {
|
|
50
|
+
if (window.ftmob_scrolling === this) {
|
|
51
|
+
window.ftmob_scrolling = false
|
|
52
|
+
}
|
|
27
53
|
}
|
|
28
54
|
|
|
29
55
|
static init(rootEl?: Element): CustomCodeComponentClient[] | void {
|
|
30
56
|
const root = rootEl instanceof Element ? rootEl : document
|
|
31
57
|
return Array.from(
|
|
32
58
|
root.querySelectorAll('custom-code-component:not([data-initialised])')
|
|
33
|
-
).map((el) =>
|
|
59
|
+
).map((el) => {
|
|
60
|
+
const ftAppSwipeGestures =
|
|
61
|
+
el.getAttribute('enable-ft-app-swipe-gestures') === 'true'
|
|
62
|
+
const client = new CustomCodeComponentClient(el, ftAppSwipeGestures)
|
|
63
|
+
return client
|
|
64
|
+
})
|
|
34
65
|
}
|
|
35
66
|
|
|
36
67
|
static destroy(components: CustomCodeComponentClient[]): void {
|
|
37
68
|
components.forEach((component) => {
|
|
38
69
|
component.tracking.destroy()
|
|
70
|
+
if (component.ftAppSwipeGestures) {
|
|
71
|
+
component.element.removeEventListener(
|
|
72
|
+
'pointerdown',
|
|
73
|
+
component.cccTouched
|
|
74
|
+
)
|
|
75
|
+
component.element.removeEventListener(
|
|
76
|
+
'pointerup',
|
|
77
|
+
component.cccReleased
|
|
78
|
+
)
|
|
79
|
+
component.element.removeEventListener(
|
|
80
|
+
'pointercancel',
|
|
81
|
+
component.cccReleased
|
|
82
|
+
)
|
|
83
|
+
if (window.ftmob_scrolling === component) {
|
|
84
|
+
window.ftmob_scrolling = false
|
|
85
|
+
}
|
|
86
|
+
}
|
|
39
87
|
})
|
|
40
88
|
}
|
|
41
89
|
}
|
package/src/types/window.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type CarouselClient from '../components/content-tree/Carousel/client'
|
|
2
|
+
import type CustomCodeComponentClient from '../components/content-tree/CustomCodeComponent/client'
|
|
2
3
|
|
|
3
4
|
export {}
|
|
4
5
|
|
|
5
6
|
declare global {
|
|
6
7
|
interface Window {
|
|
7
|
-
ftmob_scrolling?: CarouselClient | false
|
|
8
|
+
ftmob_scrolling?: CarouselClient | CustomCodeComponentClient | false
|
|
8
9
|
}
|
|
9
10
|
}
|