@acorex/cdk 22.1.0-next.8 → 22.1.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/fesm2022/acorex-cdk-common.mjs +23 -1
- package/fesm2022/acorex-cdk-common.mjs.map +1 -1
- package/fesm2022/acorex-cdk-gesture.mjs +173 -0
- package/fesm2022/acorex-cdk-gesture.mjs.map +1 -0
- package/fesm2022/acorex-cdk-highlight.mjs +178 -0
- package/fesm2022/acorex-cdk-highlight.mjs.map +1 -0
- package/fesm2022/acorex-cdk-horizontal-scroll.mjs +111 -9
- package/fesm2022/acorex-cdk-horizontal-scroll.mjs.map +1 -1
- package/fesm2022/acorex-cdk-overlay.mjs +6 -4
- package/fesm2022/acorex-cdk-overlay.mjs.map +1 -1
- package/fesm2022/acorex-cdk-sliding-item.mjs +70 -60
- package/fesm2022/acorex-cdk-sliding-item.mjs.map +1 -1
- package/gesture/README.md +3 -0
- package/horizontal-scroll/README.md +1 -1
- package/package.json +12 -3
- package/sliding-item/README.md +4 -2
- package/types/acorex-cdk-gesture.d.ts +120 -0
- package/types/acorex-cdk-highlight.d.ts +81 -0
- package/types/acorex-cdk-horizontal-scroll.d.ts +26 -7
- package/types/acorex-cdk-sliding-item.d.ts +22 -4
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { inject, ElementRef, NgZone, PLATFORM_ID, input, output, afterNextRender, effect, untracked, Directive } from '@angular/core';
|
|
4
|
+
|
|
5
|
+
const HAMMER_EVENTS = 'tap doubletap press pan panstart panmove panend swipe pinch rotate';
|
|
6
|
+
const HIGH_FREQUENCY_EVENTS = new Set(['pan', 'panmove', 'pinch', 'rotate']);
|
|
7
|
+
/**
|
|
8
|
+
* Attaches Hammer.js gesture recognition to the host element.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* <div axGesture [direction]="'all'" (tap)="onTap($event)" (pan)="onPan($event)"></div>
|
|
12
|
+
*/
|
|
13
|
+
class AXGestureDirective {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.el = inject((ElementRef));
|
|
16
|
+
this.zone = inject(NgZone);
|
|
17
|
+
this.platformId = inject(PLATFORM_ID);
|
|
18
|
+
/**
|
|
19
|
+
* Extra Hammer.js options forwarded to the manager constructor.
|
|
20
|
+
*/
|
|
21
|
+
this.options = input({}, /* @ts-ignore */
|
|
22
|
+
...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
23
|
+
/**
|
|
24
|
+
* Pan and swipe direction. Defaults to horizontal, matching Hammer.js.
|
|
25
|
+
*/
|
|
26
|
+
this.direction = input('horizontal', /* @ts-ignore */
|
|
27
|
+
...(ngDevMode ? [{ debugName: "direction" }] : /* istanbul ignore next */ []));
|
|
28
|
+
/**
|
|
29
|
+
* Enables the multi-touch pinch recognizer. Disabled by default.
|
|
30
|
+
*/
|
|
31
|
+
this.enablePinch = input(false, /* @ts-ignore */
|
|
32
|
+
...(ngDevMode ? [{ debugName: "enablePinch" }] : /* istanbul ignore next */ []));
|
|
33
|
+
/**
|
|
34
|
+
* Enables the multi-touch rotate recognizer. Disabled by default.
|
|
35
|
+
*/
|
|
36
|
+
this.enableRotate = input(false, /* @ts-ignore */
|
|
37
|
+
...(ngDevMode ? [{ debugName: "enableRotate" }] : /* istanbul ignore next */ []));
|
|
38
|
+
/**
|
|
39
|
+
* Stops gesture recognition when true.
|
|
40
|
+
*/
|
|
41
|
+
this.disabled = input(false, /* @ts-ignore */
|
|
42
|
+
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
43
|
+
this.tap = output();
|
|
44
|
+
this.doubletap = output();
|
|
45
|
+
this.press = output();
|
|
46
|
+
this.pan = output();
|
|
47
|
+
this.panstart = output();
|
|
48
|
+
this.panmove = output();
|
|
49
|
+
this.panend = output();
|
|
50
|
+
this.swipe = output();
|
|
51
|
+
this.pinch = output();
|
|
52
|
+
this.rotate = output();
|
|
53
|
+
this.hammer = null;
|
|
54
|
+
this.hammerLib = null;
|
|
55
|
+
this.setupId = 0;
|
|
56
|
+
this.ready = false;
|
|
57
|
+
this.emitters = {
|
|
58
|
+
tap: this.tap,
|
|
59
|
+
doubletap: this.doubletap,
|
|
60
|
+
press: this.press,
|
|
61
|
+
pan: this.pan,
|
|
62
|
+
panstart: this.panstart,
|
|
63
|
+
panmove: this.panmove,
|
|
64
|
+
panend: this.panend,
|
|
65
|
+
swipe: this.swipe,
|
|
66
|
+
pinch: this.pinch,
|
|
67
|
+
rotate: this.rotate,
|
|
68
|
+
};
|
|
69
|
+
this.#init = afterNextRender(() => {
|
|
70
|
+
if (!isPlatformBrowser(this.platformId))
|
|
71
|
+
return;
|
|
72
|
+
this.ready = true;
|
|
73
|
+
void this.setup();
|
|
74
|
+
});
|
|
75
|
+
this.#config = effect(() => {
|
|
76
|
+
this.options();
|
|
77
|
+
this.direction();
|
|
78
|
+
this.enablePinch();
|
|
79
|
+
this.enableRotate();
|
|
80
|
+
this.disabled();
|
|
81
|
+
untracked(() => {
|
|
82
|
+
if (this.ready)
|
|
83
|
+
void this.setup();
|
|
84
|
+
});
|
|
85
|
+
}, /* @ts-ignore */
|
|
86
|
+
...(ngDevMode ? [{ debugName: "#config" }] : /* istanbul ignore next */ []));
|
|
87
|
+
this.onHammerEvent = (ev) => {
|
|
88
|
+
const emit = this.emitters[ev.type];
|
|
89
|
+
if (!emit)
|
|
90
|
+
return;
|
|
91
|
+
if (HIGH_FREQUENCY_EVENTS.has(ev.type)) {
|
|
92
|
+
emit.emit(ev);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this.zone.run(() => emit.emit(ev));
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The underlying Hammer manager, or null when not initialized.
|
|
100
|
+
*/
|
|
101
|
+
get manager() {
|
|
102
|
+
return this.hammer;
|
|
103
|
+
}
|
|
104
|
+
#init;
|
|
105
|
+
#config;
|
|
106
|
+
ngOnDestroy() {
|
|
107
|
+
this.setupId++;
|
|
108
|
+
this.teardown();
|
|
109
|
+
}
|
|
110
|
+
async setup() {
|
|
111
|
+
const id = ++this.setupId;
|
|
112
|
+
this.teardown();
|
|
113
|
+
if (this.disabled() || !isPlatformBrowser(this.platformId))
|
|
114
|
+
return;
|
|
115
|
+
const HammerLib = await this.loadHammer();
|
|
116
|
+
if (id !== this.setupId)
|
|
117
|
+
return;
|
|
118
|
+
this.zone.runOutsideAngular(() => {
|
|
119
|
+
const options = { ...this.options() };
|
|
120
|
+
if (!options.touchAction) {
|
|
121
|
+
const hostTouchAction = this.el.nativeElement.style.touchAction;
|
|
122
|
+
if (hostTouchAction) {
|
|
123
|
+
options.touchAction = hostTouchAction;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const hammer = new HammerLib(this.el.nativeElement, options);
|
|
127
|
+
const direction = this.toHammerDirection(HammerLib, this.direction());
|
|
128
|
+
hammer.get('pan')?.set({ direction });
|
|
129
|
+
hammer.get('swipe')?.set({ direction });
|
|
130
|
+
hammer.get('pinch')?.set({ enable: this.enablePinch() });
|
|
131
|
+
hammer.get('rotate')?.set({ enable: this.enableRotate() });
|
|
132
|
+
hammer.on(HAMMER_EVENTS, this.onHammerEvent);
|
|
133
|
+
this.hammer = hammer;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
async loadHammer() {
|
|
137
|
+
if (this.hammerLib)
|
|
138
|
+
return this.hammerLib;
|
|
139
|
+
const mod = (await import('hammerjs'));
|
|
140
|
+
this.hammerLib = mod.default ?? mod;
|
|
141
|
+
return this.hammerLib;
|
|
142
|
+
}
|
|
143
|
+
toHammerDirection(HammerLib, direction) {
|
|
144
|
+
if (direction === 'all')
|
|
145
|
+
return HammerLib.DIRECTION_ALL;
|
|
146
|
+
if (direction === 'vertical')
|
|
147
|
+
return HammerLib.DIRECTION_VERTICAL;
|
|
148
|
+
return HammerLib.DIRECTION_HORIZONTAL;
|
|
149
|
+
}
|
|
150
|
+
teardown() {
|
|
151
|
+
if (!this.hammer)
|
|
152
|
+
return;
|
|
153
|
+
this.hammer.off(HAMMER_EVENTS, this.onHammerEvent);
|
|
154
|
+
this.hammer.destroy();
|
|
155
|
+
this.hammer = null;
|
|
156
|
+
}
|
|
157
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXGestureDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
158
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: AXGestureDirective, isStandalone: true, selector: "[axGesture]", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, enablePinch: { classPropertyName: "enablePinch", publicName: "enablePinch", isSignal: true, isRequired: false, transformFunction: null }, enableRotate: { classPropertyName: "enableRotate", publicName: "enableRotate", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tap: "tap", doubletap: "doubletap", press: "press", pan: "pan", panstart: "panstart", panmove: "panmove", panend: "panend", swipe: "swipe", pinch: "pinch", rotate: "rotate" }, exportAs: ["axGesture"], ngImport: i0 }); }
|
|
159
|
+
}
|
|
160
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXGestureDirective, decorators: [{
|
|
161
|
+
type: Directive,
|
|
162
|
+
args: [{
|
|
163
|
+
selector: '[axGesture]',
|
|
164
|
+
exportAs: 'axGesture',
|
|
165
|
+
}]
|
|
166
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], enablePinch: [{ type: i0.Input, args: [{ isSignal: true, alias: "enablePinch", required: false }] }], enableRotate: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableRotate", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], tap: [{ type: i0.Output, args: ["tap"] }], doubletap: [{ type: i0.Output, args: ["doubletap"] }], press: [{ type: i0.Output, args: ["press"] }], pan: [{ type: i0.Output, args: ["pan"] }], panstart: [{ type: i0.Output, args: ["panstart"] }], panmove: [{ type: i0.Output, args: ["panmove"] }], panend: [{ type: i0.Output, args: ["panend"] }], swipe: [{ type: i0.Output, args: ["swipe"] }], pinch: [{ type: i0.Output, args: ["pinch"] }], rotate: [{ type: i0.Output, args: ["rotate"] }] } });
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Generated bundle index. Do not edit.
|
|
170
|
+
*/
|
|
171
|
+
|
|
172
|
+
export { AXGestureDirective };
|
|
173
|
+
//# sourceMappingURL=acorex-cdk-gesture.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acorex-cdk-gesture.mjs","sources":["../../../../packages/cdk/gesture/src/lib/gesture.directive.ts","../../../../packages/cdk/gesture/src/acorex-cdk-gesture.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport {\n afterNextRender,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n NgZone,\n OnDestroy,\n output,\n OutputEmitterRef,\n PLATFORM_ID,\n untracked,\n} from '@angular/core';\nimport { AXGestureDirection, AXGestureEvent, AXGestureManager, AXGestureOptions } from './gesture.types';\n\nconst HAMMER_EVENTS = 'tap doubletap press pan panstart panmove panend swipe pinch rotate';\nconst HIGH_FREQUENCY_EVENTS = new Set(['pan', 'panmove', 'pinch', 'rotate']);\n\ntype HammerStatic = {\n new (element: HTMLElement | SVGElement, options?: AXGestureOptions): AXGestureManager;\n DIRECTION_ALL: number;\n DIRECTION_HORIZONTAL: number;\n DIRECTION_VERTICAL: number;\n};\n\n/**\n * Attaches Hammer.js gesture recognition to the host element.\n *\n * Usage:\n * <div axGesture [direction]=\"'all'\" (tap)=\"onTap($event)\" (pan)=\"onPan($event)\"></div>\n */\n@Directive({\n selector: '[axGesture]',\n exportAs: 'axGesture',\n})\nexport class AXGestureDirective implements OnDestroy {\n private readonly el = inject(ElementRef<HTMLElement>);\n private readonly zone = inject(NgZone);\n private readonly platformId = inject(PLATFORM_ID);\n\n /**\n * Extra Hammer.js options forwarded to the manager constructor.\n */\n options = input<AXGestureOptions>({});\n\n /**\n * Pan and swipe direction. Defaults to horizontal, matching Hammer.js.\n */\n direction = input<AXGestureDirection>('horizontal');\n\n /**\n * Enables the multi-touch pinch recognizer. Disabled by default.\n */\n enablePinch = input(false);\n\n /**\n * Enables the multi-touch rotate recognizer. Disabled by default.\n */\n enableRotate = input(false);\n\n /**\n * Stops gesture recognition when true.\n */\n disabled = input(false);\n\n tap = output<AXGestureEvent>();\n doubletap = output<AXGestureEvent>();\n press = output<AXGestureEvent>();\n pan = output<AXGestureEvent>();\n panstart = output<AXGestureEvent>();\n panmove = output<AXGestureEvent>();\n panend = output<AXGestureEvent>();\n swipe = output<AXGestureEvent>();\n pinch = output<AXGestureEvent>();\n rotate = output<AXGestureEvent>();\n\n private hammer: AXGestureManager | null = null;\n private hammerLib: HammerStatic | null = null;\n private setupId = 0;\n private ready = false;\n\n private readonly emitters: Record<string, OutputEmitterRef<AXGestureEvent>> = {\n tap: this.tap,\n doubletap: this.doubletap,\n press: this.press,\n pan: this.pan,\n panstart: this.panstart,\n panmove: this.panmove,\n panend: this.panend,\n swipe: this.swipe,\n pinch: this.pinch,\n rotate: this.rotate,\n };\n\n /**\n * The underlying Hammer manager, or null when not initialized.\n */\n get manager(): AXGestureManager | null {\n return this.hammer;\n }\n\n #init = afterNextRender(() => {\n if (!isPlatformBrowser(this.platformId)) return;\n this.ready = true;\n void this.setup();\n });\n\n #config = effect(() => {\n this.options();\n this.direction();\n this.enablePinch();\n this.enableRotate();\n this.disabled();\n untracked(() => {\n if (this.ready) void this.setup();\n });\n });\n\n ngOnDestroy(): void {\n this.setupId++;\n this.teardown();\n }\n\n private async setup(): Promise<void> {\n const id = ++this.setupId;\n this.teardown();\n if (this.disabled() || !isPlatformBrowser(this.platformId)) return;\n\n const HammerLib = await this.loadHammer();\n if (id !== this.setupId) return;\n\n this.zone.runOutsideAngular(() => {\n const options: AXGestureOptions = { ...this.options() };\n if (!options.touchAction) {\n const hostTouchAction = this.el.nativeElement.style.touchAction;\n if (hostTouchAction) {\n options.touchAction = hostTouchAction;\n }\n }\n\n const hammer = new HammerLib(this.el.nativeElement, options);\n const direction = this.toHammerDirection(HammerLib, this.direction());\n\n hammer.get('pan')?.set({ direction });\n hammer.get('swipe')?.set({ direction });\n hammer.get('pinch')?.set({ enable: this.enablePinch() });\n hammer.get('rotate')?.set({ enable: this.enableRotate() });\n hammer.on(HAMMER_EVENTS, this.onHammerEvent);\n\n this.hammer = hammer;\n });\n }\n\n private async loadHammer(): Promise<HammerStatic> {\n if (this.hammerLib) return this.hammerLib;\n const mod = (await import('hammerjs')) as unknown as HammerStatic & { default?: HammerStatic };\n this.hammerLib = mod.default ?? mod;\n return this.hammerLib;\n }\n\n private toHammerDirection(HammerLib: HammerStatic, direction: AXGestureDirection): number {\n if (direction === 'all') return HammerLib.DIRECTION_ALL;\n if (direction === 'vertical') return HammerLib.DIRECTION_VERTICAL;\n return HammerLib.DIRECTION_HORIZONTAL;\n }\n\n private teardown(): void {\n if (!this.hammer) return;\n this.hammer.off(HAMMER_EVENTS, this.onHammerEvent);\n this.hammer.destroy();\n this.hammer = null;\n }\n\n private readonly onHammerEvent = (ev: AXGestureEvent) => {\n const emit = this.emitters[ev.type];\n if (!emit) return;\n if (HIGH_FREQUENCY_EVENTS.has(ev.type)) {\n emit.emit(ev);\n return;\n }\n this.zone.run(() => emit.emit(ev));\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, ElementRef, DestroyRef, input, booleanAttribute, output, afterRenderEffect, Directive, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, Pipe } from '@angular/core';
|
|
3
|
+
import { AXHighlightService, splitHighlightSegments } from '@acorex/core/utils';
|
|
4
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Directive that highlights text within the host element or its descendants.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* ```html
|
|
11
|
+
* <div [axHighlight]="query()">...</div>
|
|
12
|
+
* <table [axHighlight]="query()" highlightSelector="tbody td">...</table>
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
class AXHighlightDirective {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.host = inject(ElementRef);
|
|
18
|
+
this.destroyRef = inject(DestroyRef);
|
|
19
|
+
this.highlightService = inject(AXHighlightService);
|
|
20
|
+
/**
|
|
21
|
+
* Text or list of texts to search and highlight.
|
|
22
|
+
*/
|
|
23
|
+
this.axHighlight = input('', /* @ts-ignore */
|
|
24
|
+
...(ngDevMode ? [{ debugName: "axHighlight" }] : /* istanbul ignore next */ []));
|
|
25
|
+
/**
|
|
26
|
+
* Optional CSS selector scoped to the host element.
|
|
27
|
+
* When omitted, highlighting is applied to the host element itself.
|
|
28
|
+
*/
|
|
29
|
+
this.highlightSelector = input(/* @ts-ignore */
|
|
30
|
+
...(ngDevMode ? [undefined, { debugName: "highlightSelector" }] : /* istanbul ignore next */ []));
|
|
31
|
+
/**
|
|
32
|
+
* Optional highlight behavior configuration.
|
|
33
|
+
*/
|
|
34
|
+
this.highlightOptions = input(/* @ts-ignore */
|
|
35
|
+
...(ngDevMode ? [undefined, { debugName: "highlightOptions" }] : /* istanbul ignore next */ []));
|
|
36
|
+
/**
|
|
37
|
+
* When true, highlighting is disabled and any existing highlights are cleared.
|
|
38
|
+
*/
|
|
39
|
+
this.highlightDisabled = input(false, { ...(ngDevMode ? { debugName: "highlightDisabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
40
|
+
/**
|
|
41
|
+
* Emits the number of matches highlighted after each render cycle.
|
|
42
|
+
*/
|
|
43
|
+
this.matchCount = output();
|
|
44
|
+
afterRenderEffect(() => {
|
|
45
|
+
const target = this.resolveTarget();
|
|
46
|
+
if (this.highlightDisabled()) {
|
|
47
|
+
this.highlightService.clear(target);
|
|
48
|
+
this.matchCount.emit(0);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const terms = this.axHighlight();
|
|
52
|
+
const normalizedTerms = (Array.isArray(terms) ? terms : [terms]).map((term) => term.trim()).filter(Boolean);
|
|
53
|
+
if (!normalizedTerms.length) {
|
|
54
|
+
this.highlightService.clear(target);
|
|
55
|
+
this.matchCount.emit(0);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const count = this.highlightService.highlight(target, normalizedTerms, this.highlightOptions());
|
|
59
|
+
this.matchCount.emit(count);
|
|
60
|
+
});
|
|
61
|
+
this.destroyRef.onDestroy(() => {
|
|
62
|
+
this.highlightService.clear(this.resolveTarget());
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
resolveTarget() {
|
|
66
|
+
const selector = this.highlightSelector()?.trim();
|
|
67
|
+
const hostElement = this.host.nativeElement;
|
|
68
|
+
if (!selector) {
|
|
69
|
+
return hostElement;
|
|
70
|
+
}
|
|
71
|
+
return Array.from(hostElement.querySelectorAll(selector));
|
|
72
|
+
}
|
|
73
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXHighlightDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
74
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: AXHighlightDirective, isStandalone: true, selector: "[axHighlight]", inputs: { axHighlight: { classPropertyName: "axHighlight", publicName: "axHighlight", isSignal: true, isRequired: false, transformFunction: null }, highlightSelector: { classPropertyName: "highlightSelector", publicName: "highlightSelector", isSignal: true, isRequired: false, transformFunction: null }, highlightOptions: { classPropertyName: "highlightOptions", publicName: "highlightOptions", isSignal: true, isRequired: false, transformFunction: null }, highlightDisabled: { classPropertyName: "highlightDisabled", publicName: "highlightDisabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { matchCount: "matchCount" }, ngImport: i0 }); }
|
|
75
|
+
}
|
|
76
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXHighlightDirective, decorators: [{
|
|
77
|
+
type: Directive,
|
|
78
|
+
args: [{
|
|
79
|
+
selector: '[axHighlight]',
|
|
80
|
+
}]
|
|
81
|
+
}], ctorParameters: () => [], propDecorators: { axHighlight: [{ type: i0.Input, args: [{ isSignal: true, alias: "axHighlight", required: false }] }], highlightSelector: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightSelector", required: false }] }], highlightOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightOptions", required: false }] }], highlightDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightDisabled", required: false }] }], matchCount: [{ type: i0.Output, args: ["matchCount"] }] } });
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Renders text with search-term matches wrapped in `.ax-highlight` spans.
|
|
85
|
+
*
|
|
86
|
+
* Uses a single innerHTML string so Angular control-flow whitespace cannot insert
|
|
87
|
+
* gaps between highlighted and non-highlighted characters.
|
|
88
|
+
*
|
|
89
|
+
* Does not use the `ax-highlight-text` class on the host — that class is reserved for
|
|
90
|
+
* {@link AXHighlightService} DOM wrappers.
|
|
91
|
+
*/
|
|
92
|
+
class AXHighlightTextComponent {
|
|
93
|
+
constructor() {
|
|
94
|
+
this.sanitizer = inject(DomSanitizer);
|
|
95
|
+
/** The full text to render. */
|
|
96
|
+
this.text = input('', /* @ts-ignore */
|
|
97
|
+
...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
98
|
+
/** The search term or terms to highlight within the text. */
|
|
99
|
+
this.query = input('', /* @ts-ignore */
|
|
100
|
+
...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
|
|
101
|
+
/** When true, highlighting is disabled and plain text is shown. */
|
|
102
|
+
this.highlightDisabled = input(false, { ...(ngDevMode ? { debugName: "highlightDisabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
103
|
+
/** Optional highlight behavior configuration. */
|
|
104
|
+
this.highlightOptions = input(/* @ts-ignore */
|
|
105
|
+
...(ngDevMode ? [undefined, { debugName: "highlightOptions" }] : /* istanbul ignore next */ []));
|
|
106
|
+
this.segments = computed(() => {
|
|
107
|
+
const content = this.text() ?? '';
|
|
108
|
+
if (this.highlightDisabled()) {
|
|
109
|
+
return content ? [{ text: content, match: false }] : [];
|
|
110
|
+
}
|
|
111
|
+
const query = this.query();
|
|
112
|
+
const terms = Array.isArray(query) ? query : [query];
|
|
113
|
+
const normalized = terms.map((value) => value.trim()).filter(Boolean);
|
|
114
|
+
if (!normalized.length) {
|
|
115
|
+
return content ? [{ text: content, match: false }] : [];
|
|
116
|
+
}
|
|
117
|
+
return splitHighlightSegments(content, normalized, this.highlightOptions());
|
|
118
|
+
}, /* @ts-ignore */
|
|
119
|
+
...(ngDevMode ? [{ debugName: "segments" }] : /* istanbul ignore next */ []));
|
|
120
|
+
this.highlightedHtml = computed(() => {
|
|
121
|
+
const segments = this.segments();
|
|
122
|
+
if (!segments.length) {
|
|
123
|
+
return this.sanitizer.bypassSecurityTrustHtml('');
|
|
124
|
+
}
|
|
125
|
+
const html = segments
|
|
126
|
+
.map((segment) => segment.match
|
|
127
|
+
? `<span class="ax-highlight">${this.#escapeHtml(segment.text)}</span>`
|
|
128
|
+
: this.#escapeHtml(segment.text))
|
|
129
|
+
.join('');
|
|
130
|
+
return this.sanitizer.bypassSecurityTrustHtml(html);
|
|
131
|
+
}, /* @ts-ignore */
|
|
132
|
+
...(ngDevMode ? [{ debugName: "highlightedHtml" }] : /* istanbul ignore next */ []));
|
|
133
|
+
}
|
|
134
|
+
#escapeHtml(value) {
|
|
135
|
+
return value
|
|
136
|
+
.replace(/&/g, '&')
|
|
137
|
+
.replace(/</g, '<')
|
|
138
|
+
.replace(/>/g, '>')
|
|
139
|
+
.replace(/"/g, '"')
|
|
140
|
+
.replace(/'/g, ''');
|
|
141
|
+
}
|
|
142
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXHighlightTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
143
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: AXHighlightTextComponent, isStandalone: true, selector: "ax-highlight-text", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, highlightDisabled: { classPropertyName: "highlightDisabled", publicName: "highlightDisabled", isSignal: true, isRequired: false, transformFunction: null }, highlightOptions: { classPropertyName: "highlightOptions", publicName: "highlightOptions", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "ax-highlight-text-root" }, ngImport: i0, template: `<span class="ax-highlight-text-content" [innerHTML]="highlightedHtml()"></span>`, isInline: true, styles: ["ax-highlight-text.ax-highlight-text-root{display:contents}ax-highlight-text .ax-highlight-text-content{display:inline;margin:0;padding:0;font:inherit;letter-spacing:inherit;word-spacing:inherit}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
144
|
+
}
|
|
145
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXHighlightTextComponent, decorators: [{
|
|
146
|
+
type: Component,
|
|
147
|
+
args: [{ selector: 'ax-highlight-text', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
148
|
+
class: 'ax-highlight-text-root',
|
|
149
|
+
}, template: `<span class="ax-highlight-text-content" [innerHTML]="highlightedHtml()"></span>`, styles: ["ax-highlight-text.ax-highlight-text-root{display:contents}ax-highlight-text .ax-highlight-text-content{display:inline;margin:0;padding:0;font:inherit;letter-spacing:inherit;word-spacing:inherit}\n"] }]
|
|
150
|
+
}], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], query: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }], highlightDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightDisabled", required: false }] }], highlightOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightOptions", required: false }] }] } });
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Pure pipe that splits text into highlight segments for template rendering.
|
|
154
|
+
*/
|
|
155
|
+
class AXHighlightTextPipe {
|
|
156
|
+
transform(text, query, options) {
|
|
157
|
+
const content = text ?? '';
|
|
158
|
+
if (query == null) {
|
|
159
|
+
return content ? [{ text: content, match: false }] : [];
|
|
160
|
+
}
|
|
161
|
+
return splitHighlightSegments(content, query, options);
|
|
162
|
+
}
|
|
163
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXHighlightTextPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
164
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXHighlightTextPipe, isStandalone: true, name: "axHighlightText" }); }
|
|
165
|
+
}
|
|
166
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXHighlightTextPipe, decorators: [{
|
|
167
|
+
type: Pipe,
|
|
168
|
+
args: [{
|
|
169
|
+
name: 'axHighlightText',
|
|
170
|
+
}]
|
|
171
|
+
}] });
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Generated bundle index. Do not edit.
|
|
175
|
+
*/
|
|
176
|
+
|
|
177
|
+
export { AXHighlightDirective, AXHighlightTextComponent, AXHighlightTextPipe };
|
|
178
|
+
//# sourceMappingURL=acorex-cdk-highlight.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acorex-cdk-highlight.mjs","sources":["../../../../packages/cdk/highlight/src/lib/highlight.directive.ts","../../../../packages/cdk/highlight/src/lib/highlight-text.component.ts","../../../../packages/cdk/highlight/src/lib/highlight-text.pipe.ts","../../../../packages/cdk/highlight/src/acorex-cdk-highlight.ts"],"sourcesContent":["import {\n afterRenderEffect,\n booleanAttribute,\n DestroyRef,\n Directive,\n ElementRef,\n inject,\n input,\n output,\n} from '@angular/core';\nimport { AXHighlightOptions, AXHighlightService } from '@acorex/core/utils';\n\n/**\n * Directive that highlights text within the host element or its descendants.\n *\n * Usage:\n * ```html\n * <div [axHighlight]=\"query()\">...</div>\n * <table [axHighlight]=\"query()\" highlightSelector=\"tbody td\">...</table>\n * ```\n */\n@Directive({\n selector: '[axHighlight]',\n})\nexport class AXHighlightDirective {\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n private readonly highlightService = inject(AXHighlightService);\n\n /**\n * Text or list of texts to search and highlight.\n */\n readonly axHighlight = input<string | readonly string[]>('');\n\n /**\n * Optional CSS selector scoped to the host element.\n * When omitted, highlighting is applied to the host element itself.\n */\n readonly highlightSelector = input<string>();\n\n /**\n * Optional highlight behavior configuration.\n */\n readonly highlightOptions = input<AXHighlightOptions>();\n\n /**\n * When true, highlighting is disabled and any existing highlights are cleared.\n */\n readonly highlightDisabled = input(false, { transform: booleanAttribute });\n\n /**\n * Emits the number of matches highlighted after each render cycle.\n */\n readonly matchCount = output<number>();\n\n constructor() {\n afterRenderEffect(() => {\n const target = this.resolveTarget();\n\n if (this.highlightDisabled()) {\n this.highlightService.clear(target);\n this.matchCount.emit(0);\n return;\n }\n\n const terms = this.axHighlight();\n const normalizedTerms = (Array.isArray(terms) ? terms : [terms]).map((term) => term.trim()).filter(Boolean);\n\n if (!normalizedTerms.length) {\n this.highlightService.clear(target);\n this.matchCount.emit(0);\n return;\n }\n\n const count = this.highlightService.highlight(target, normalizedTerms, this.highlightOptions());\n this.matchCount.emit(count);\n });\n\n this.destroyRef.onDestroy(() => {\n this.highlightService.clear(this.resolveTarget());\n });\n }\n\n private resolveTarget(): Element | Element[] {\n const selector = this.highlightSelector()?.trim();\n const hostElement = this.host.nativeElement;\n\n if (!selector) {\n return hostElement;\n }\n\n return Array.from(hostElement.querySelectorAll(selector));\n }\n}\n","import { AXHighlightOptions, AXHighlightSegment, splitHighlightSegments } from '@acorex/core/utils';\nimport {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n booleanAttribute,\n computed,\n inject,\n input,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\n\n/**\n * Renders text with search-term matches wrapped in `.ax-highlight` spans.\n *\n * Uses a single innerHTML string so Angular control-flow whitespace cannot insert\n * gaps between highlighted and non-highlighted characters.\n *\n * Does not use the `ax-highlight-text` class on the host — that class is reserved for\n * {@link AXHighlightService} DOM wrappers.\n */\n@Component({\n selector: 'ax-highlight-text',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'ax-highlight-text-root',\n },\n styles: [\n `\n ax-highlight-text.ax-highlight-text-root {\n display: contents;\n }\n\n ax-highlight-text .ax-highlight-text-content {\n display: inline;\n margin: 0;\n padding: 0;\n font: inherit;\n letter-spacing: inherit;\n word-spacing: inherit;\n }\n `,\n ],\n template: `<span class=\"ax-highlight-text-content\" [innerHTML]=\"highlightedHtml()\"></span>`,\n})\nexport class AXHighlightTextComponent {\n private readonly sanitizer = inject(DomSanitizer);\n\n /** The full text to render. */\n readonly text = input('');\n\n /** The search term or terms to highlight within the text. */\n readonly query = input<string | readonly string[]>('');\n\n /** When true, highlighting is disabled and plain text is shown. */\n readonly highlightDisabled = input(false, { transform: booleanAttribute });\n\n /** Optional highlight behavior configuration. */\n readonly highlightOptions = input<AXHighlightOptions>();\n\n protected readonly segments = computed((): AXHighlightSegment[] => {\n const content = this.text() ?? '';\n if (this.highlightDisabled()) {\n return content ? [{ text: content, match: false }] : [];\n }\n\n const query = this.query();\n const terms = Array.isArray(query) ? query : [query];\n const normalized = terms.map((value) => value.trim()).filter(Boolean);\n if (!normalized.length) {\n return content ? [{ text: content, match: false }] : [];\n }\n\n return splitHighlightSegments(content, normalized, this.highlightOptions());\n });\n\n protected readonly highlightedHtml = computed((): SafeHtml => {\n const segments = this.segments();\n if (!segments.length) {\n return this.sanitizer.bypassSecurityTrustHtml('');\n }\n\n const html = segments\n .map((segment) =>\n segment.match\n ? `<span class=\"ax-highlight\">${this.#escapeHtml(segment.text)}</span>`\n : this.#escapeHtml(segment.text),\n )\n .join('');\n\n return this.sanitizer.bypassSecurityTrustHtml(html);\n });\n\n #escapeHtml(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n }\n}\n","import { AXHighlightOptions, AXHighlightSegment, splitHighlightSegments } from '@acorex/core/utils';\nimport { Pipe, PipeTransform } from '@angular/core';\n\n/**\n * Pure pipe that splits text into highlight segments for template rendering.\n */\n@Pipe({\n name: 'axHighlightText',\n})\nexport class AXHighlightTextPipe implements PipeTransform {\n transform(\n text: string | null | undefined,\n query: string | readonly string[] | null | undefined,\n options?: AXHighlightOptions,\n ): AXHighlightSegment[] {\n const content = text ?? '';\n if (query == null) {\n return content ? [{ text: content, match: false }] : [];\n }\n\n return splitHighlightSegments(content, query, options);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
import { isPlatformBrowser } from '@angular/common';
|
|
2
|
+
import * as i1 from '@acorex/cdk/gesture';
|
|
3
|
+
import { AXGestureDirective } from '@acorex/cdk/gesture';
|
|
2
4
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { inject, ElementRef, PLATFORM_ID, NgZone, input, afterNextRender, Directive } from '@angular/core';
|
|
5
|
+
import { inject, ElementRef, PLATFORM_ID, NgZone, DestroyRef, input, afterNextRender, Directive } from '@angular/core';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
|
-
* Converts vertical mouse-wheel scrolling into horizontal scrolling on the host element.
|
|
7
|
-
* Applies only when the host can scroll horizontally.
|
|
8
|
+
* Converts vertical mouse-wheel scrolling and horizontal pan into horizontal scrolling on the host element.
|
|
9
|
+
* Applies only when the host can scroll horizontally. Pan is provided by the hosted AXGestureDirective.
|
|
8
10
|
*
|
|
9
11
|
* Usage:
|
|
10
12
|
* <div axHorizontalScroll [speed]="150" class="overflow-x-auto">
|
|
11
13
|
* ...
|
|
12
14
|
* </div>
|
|
15
|
+
*
|
|
16
|
+
* Do not put `axGesture` on the same element; this directive already hosts it.
|
|
13
17
|
*/
|
|
14
18
|
class AXHorizontalScrollDirective {
|
|
15
19
|
constructor() {
|
|
16
20
|
this.elementRef = inject((ElementRef));
|
|
17
21
|
this.platformId = inject(PLATFORM_ID);
|
|
18
22
|
this.zone = inject(NgZone);
|
|
23
|
+
this.gesture = inject(AXGestureDirective);
|
|
24
|
+
this.destroyRef = inject(DestroyRef);
|
|
19
25
|
/**
|
|
20
|
-
* Disables wheel-to-horizontal conversion when true.
|
|
26
|
+
* Disables wheel-to-horizontal conversion and pan scrolling when true.
|
|
21
27
|
*/
|
|
22
28
|
this.disabled = input(false, /* @ts-ignore */
|
|
23
29
|
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
@@ -31,6 +37,12 @@ class AXHorizontalScrollDirective {
|
|
|
31
37
|
this.targetScrollLeft = null;
|
|
32
38
|
this.scrollPosition = 0;
|
|
33
39
|
this.lastFrameTime = 0;
|
|
40
|
+
this.isPanning = false;
|
|
41
|
+
this.ignorePan = false;
|
|
42
|
+
this.panStartScrollLeft = 0;
|
|
43
|
+
this.suppressClickListener = null;
|
|
44
|
+
this.suppressClickTimer = null;
|
|
45
|
+
this.interactiveSelector = 'input, textarea, select, option, [contenteditable]:not([contenteditable="false"])';
|
|
34
46
|
this.wheelListener = (event) => this.handleWheel(event);
|
|
35
47
|
this.#init = afterNextRender(() => {
|
|
36
48
|
if (!isPlatformBrowser(this.platformId))
|
|
@@ -39,26 +51,37 @@ class AXHorizontalScrollDirective {
|
|
|
39
51
|
this.nativeElement.addEventListener('wheel', this.wheelListener, { passive: false });
|
|
40
52
|
});
|
|
41
53
|
});
|
|
54
|
+
const subscriptions = [
|
|
55
|
+
this.gesture.panstart.subscribe((event) => this.handlePanStart(event)),
|
|
56
|
+
this.gesture.panmove.subscribe((event) => this.handlePanMove(event)),
|
|
57
|
+
this.gesture.panend.subscribe((event) => this.handlePanEnd(event)),
|
|
58
|
+
];
|
|
59
|
+
this.destroyRef.onDestroy(() => {
|
|
60
|
+
for (const subscription of subscriptions) {
|
|
61
|
+
subscription.unsubscribe();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
42
64
|
}
|
|
43
65
|
#init;
|
|
44
66
|
ngOnDestroy() {
|
|
45
67
|
if (!isPlatformBrowser(this.platformId))
|
|
46
68
|
return;
|
|
47
69
|
this.stopAnimation();
|
|
70
|
+
this.clearPanStyles();
|
|
71
|
+
this.clearClickSuppression();
|
|
48
72
|
this.nativeElement.removeEventListener('wheel', this.wheelListener);
|
|
49
73
|
}
|
|
50
74
|
get nativeElement() {
|
|
51
75
|
return this.elementRef.nativeElement;
|
|
52
76
|
}
|
|
53
77
|
handleWheel(event) {
|
|
54
|
-
if (this.disabled() || !this.canScrollHorizontally())
|
|
78
|
+
if (this.disabled() || this.isPanning || !this.canScrollHorizontally())
|
|
55
79
|
return;
|
|
56
80
|
if (event.shiftKey || Math.abs(event.deltaX) > Math.abs(event.deltaY))
|
|
57
81
|
return;
|
|
58
82
|
if (event.deltaY === 0)
|
|
59
83
|
return;
|
|
60
|
-
const
|
|
61
|
-
const maxScrollLeft = element.scrollWidth - element.clientWidth;
|
|
84
|
+
const maxScrollLeft = this.getMaxScrollLeft();
|
|
62
85
|
this.syncScrollState();
|
|
63
86
|
const currentPosition = this.targetScrollLeft ?? this.scrollPosition;
|
|
64
87
|
const atStart = currentPosition <= 0 && event.deltaY < 0;
|
|
@@ -75,6 +98,77 @@ class AXHorizontalScrollDirective {
|
|
|
75
98
|
}
|
|
76
99
|
this.startAnimation();
|
|
77
100
|
}
|
|
101
|
+
handlePanStart(event) {
|
|
102
|
+
if (this.disabled() || !this.canScrollHorizontally()) {
|
|
103
|
+
this.ignorePan = true;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (event.target instanceof Element && event.target.closest(this.interactiveSelector)) {
|
|
107
|
+
this.ignorePan = true;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
this.ignorePan = false;
|
|
111
|
+
this.isPanning = true;
|
|
112
|
+
this.stopAnimation();
|
|
113
|
+
this.targetScrollLeft = null;
|
|
114
|
+
this.scrollPosition = this.nativeElement.scrollLeft;
|
|
115
|
+
this.panStartScrollLeft = this.scrollPosition;
|
|
116
|
+
this.nativeElement.style.userSelect = 'none';
|
|
117
|
+
if (event.pointerType === 'mouse') {
|
|
118
|
+
this.nativeElement.style.cursor = 'grabbing';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
handlePanMove(event) {
|
|
122
|
+
if (this.ignorePan || this.disabled() || !this.isPanning)
|
|
123
|
+
return;
|
|
124
|
+
event.preventDefault();
|
|
125
|
+
const next = Math.max(0, Math.min(this.getMaxScrollLeft(), this.panStartScrollLeft - event.deltaX));
|
|
126
|
+
this.applyScrollPosition(next);
|
|
127
|
+
}
|
|
128
|
+
handlePanEnd(event) {
|
|
129
|
+
if (this.ignorePan) {
|
|
130
|
+
this.ignorePan = false;
|
|
131
|
+
this.isPanning = false;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
this.isPanning = false;
|
|
135
|
+
this.clearPanStyles();
|
|
136
|
+
if (this.disabled())
|
|
137
|
+
return;
|
|
138
|
+
this.suppressClick();
|
|
139
|
+
if (this.speed() <= 0)
|
|
140
|
+
return;
|
|
141
|
+
const distance = -event.velocityX * this.speed();
|
|
142
|
+
if (Math.abs(distance) < 8)
|
|
143
|
+
return;
|
|
144
|
+
this.targetScrollLeft = Math.max(0, Math.min(this.getMaxScrollLeft(), this.scrollPosition + distance));
|
|
145
|
+
this.startAnimation();
|
|
146
|
+
}
|
|
147
|
+
clearPanStyles() {
|
|
148
|
+
this.nativeElement.style.userSelect = '';
|
|
149
|
+
this.nativeElement.style.cursor = '';
|
|
150
|
+
}
|
|
151
|
+
suppressClick() {
|
|
152
|
+
this.clearClickSuppression();
|
|
153
|
+
const preventClick = (event) => {
|
|
154
|
+
event.preventDefault();
|
|
155
|
+
event.stopImmediatePropagation();
|
|
156
|
+
this.clearClickSuppression();
|
|
157
|
+
};
|
|
158
|
+
this.suppressClickListener = preventClick;
|
|
159
|
+
this.nativeElement.addEventListener('click', preventClick, true);
|
|
160
|
+
this.suppressClickTimer = setTimeout(() => this.clearClickSuppression(), 400);
|
|
161
|
+
}
|
|
162
|
+
clearClickSuppression() {
|
|
163
|
+
if (this.suppressClickTimer !== null) {
|
|
164
|
+
clearTimeout(this.suppressClickTimer);
|
|
165
|
+
this.suppressClickTimer = null;
|
|
166
|
+
}
|
|
167
|
+
if (!this.suppressClickListener)
|
|
168
|
+
return;
|
|
169
|
+
this.nativeElement.removeEventListener('click', this.suppressClickListener, true);
|
|
170
|
+
this.suppressClickListener = null;
|
|
171
|
+
}
|
|
78
172
|
syncScrollState() {
|
|
79
173
|
if (this.targetScrollLeft !== null)
|
|
80
174
|
return;
|
|
@@ -120,15 +214,23 @@ class AXHorizontalScrollDirective {
|
|
|
120
214
|
const element = this.nativeElement;
|
|
121
215
|
return element.scrollWidth > element.clientWidth;
|
|
122
216
|
}
|
|
217
|
+
getMaxScrollLeft() {
|
|
218
|
+
const element = this.nativeElement;
|
|
219
|
+
return element.scrollWidth - element.clientWidth;
|
|
220
|
+
}
|
|
123
221
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXHorizontalScrollDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
124
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: AXHorizontalScrollDirective, isStandalone: true, selector: "[axHorizontalScroll]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, speed: { classPropertyName: "speed", publicName: "speed", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
222
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: AXHorizontalScrollDirective, isStandalone: true, selector: "[axHorizontalScroll]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, speed: { classPropertyName: "speed", publicName: "speed", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.touch-action": "disabled() ? null : \"pan-y\"" } }, hostDirectives: [{ directive: i1.AXGestureDirective }], ngImport: i0 }); }
|
|
125
223
|
}
|
|
126
224
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXHorizontalScrollDirective, decorators: [{
|
|
127
225
|
type: Directive,
|
|
128
226
|
args: [{
|
|
129
227
|
selector: '[axHorizontalScroll]',
|
|
228
|
+
hostDirectives: [AXGestureDirective],
|
|
229
|
+
host: {
|
|
230
|
+
'[style.touch-action]': 'disabled() ? null : "pan-y"',
|
|
231
|
+
},
|
|
130
232
|
}]
|
|
131
|
-
}], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], speed: [{ type: i0.Input, args: [{ isSignal: true, alias: "speed", required: false }] }] } });
|
|
233
|
+
}], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], speed: [{ type: i0.Input, args: [{ isSignal: true, alias: "speed", required: false }] }] } });
|
|
132
234
|
|
|
133
235
|
/**
|
|
134
236
|
* Generated bundle index. Do not edit.
|