@radix-ng/primitives 1.0.0-beta.0 → 1.0.0-beta.2
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/radix-ng-primitives-accordion.mjs +2 -2
- package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-calendar.mjs +109 -84
- package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-checkbox.mjs +2 -2
- package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-collapsible.mjs +1 -1
- package/fesm2022/radix-ng-primitives-collapsible.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-combobox.mjs +1923 -0
- package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-context-menu.mjs +1 -1
- package/fesm2022/radix-ng-primitives-context-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-core.mjs +591 -470
- package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-cropper.mjs +287 -308
- package/fesm2022/radix-ng-primitives-cropper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-date-field.mjs +66 -15
- package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-dialog.mjs +1 -1
- package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-drawer.mjs +7 -106
- package/fesm2022/radix-ng-primitives-drawer.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-editable.mjs +305 -24
- package/fesm2022/radix-ng-primitives-editable.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-field.mjs +86 -6
- package/fesm2022/radix-ng-primitives-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-fieldset.mjs +1 -1
- package/fesm2022/radix-ng-primitives-fieldset.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-focus-scope.mjs +1 -1
- package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-form.mjs +207 -0
- package/fesm2022/radix-ng-primitives-form.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-input.mjs +85 -4
- package/fesm2022/radix-ng-primitives-input.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-menu.mjs +413 -5
- package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-menubar.mjs +1 -1
- package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-meter.mjs +1 -1
- package/fesm2022/radix-ng-primitives-meter.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-navigation-menu.mjs +1 -1
- package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-number-field.mjs +2 -2
- package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-popover.mjs +1 -1
- package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-popper.mjs +22 -5
- package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-portal.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-preview-card.mjs +1 -1
- package/fesm2022/radix-ng-primitives-preview-card.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-progress.mjs +1 -1
- package/fesm2022/radix-ng-primitives-progress.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-roving-focus.mjs +1 -1
- package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-scroll-area.mjs +923 -0
- package/fesm2022/radix-ng-primitives-scroll-area.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-select.mjs +421 -224
- package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-slider.mjs +1 -1
- package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-stepper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-switch.mjs +3 -2
- package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-tabs.mjs +12 -3
- package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-time-field.mjs +27 -3
- package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toast.mjs +839 -0
- package/fesm2022/radix-ng-primitives-toast.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-toggle-group.mjs +1 -1
- package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toolbar.mjs +2 -2
- package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-tooltip.mjs +11 -3
- package/fesm2022/radix-ng-primitives-tooltip.mjs.map +1 -1
- package/package.json +18 -2
- package/schematics/ng-add/index.js +57 -0
- package/schematics/ng-add/index.js.map +1 -1
- package/schematics/ng-add/schema.d.ts +1 -0
- package/schematics/ng-add/schema.json +6 -0
- package/types/radix-ng-primitives-accordion.d.ts +3 -2
- package/types/radix-ng-primitives-calendar.d.ts +38 -18
- package/types/radix-ng-primitives-checkbox.d.ts +5 -5
- package/types/radix-ng-primitives-collapsible.d.ts +2 -1
- package/types/radix-ng-primitives-combobox.d.ts +1265 -0
- package/types/radix-ng-primitives-context-menu.d.ts +3 -2
- package/types/radix-ng-primitives-core.d.ts +187 -56
- package/types/radix-ng-primitives-cropper.d.ts +89 -56
- package/types/radix-ng-primitives-date-field.d.ts +11 -5
- package/types/radix-ng-primitives-dialog.d.ts +2 -1
- package/types/radix-ng-primitives-drawer.d.ts +5 -27
- package/types/radix-ng-primitives-editable.d.ts +90 -13
- package/types/radix-ng-primitives-field.d.ts +74 -4
- package/types/radix-ng-primitives-fieldset.d.ts +3 -2
- package/types/radix-ng-primitives-focus-scope.d.ts +2 -1
- package/types/radix-ng-primitives-form.d.ts +124 -0
- package/types/radix-ng-primitives-input.d.ts +75 -5
- package/types/radix-ng-primitives-menu.d.ts +16 -4
- package/types/radix-ng-primitives-menubar.d.ts +2 -1
- package/types/radix-ng-primitives-meter.d.ts +3 -2
- package/types/radix-ng-primitives-navigation-menu.d.ts +1 -1
- package/types/radix-ng-primitives-number-field.d.ts +6 -6
- package/types/radix-ng-primitives-popover.d.ts +2 -1
- package/types/radix-ng-primitives-popper.d.ts +19 -2
- package/types/radix-ng-primitives-preview-card.d.ts +1 -1
- package/types/radix-ng-primitives-progress.d.ts +3 -2
- package/types/radix-ng-primitives-roving-focus.d.ts +4 -3
- package/types/radix-ng-primitives-scroll-area.d.ts +253 -0
- package/types/radix-ng-primitives-select.d.ts +296 -136
- package/types/radix-ng-primitives-slider.d.ts +1 -1
- package/types/radix-ng-primitives-switch.d.ts +1 -1
- package/types/radix-ng-primitives-tabs.d.ts +1 -1
- package/types/radix-ng-primitives-toast.d.ts +378 -0
- package/types/radix-ng-primitives-toggle-group.d.ts +2 -1
- package/types/radix-ng-primitives-toolbar.d.ts +3 -2
- package/types/radix-ng-primitives-tooltip.d.ts +3 -2
|
@@ -0,0 +1,923 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, DestroyRef, input, computed, signal, ElementRef, Directive, PLATFORM_ID, effect, afterNextRender, booleanAttribute, NgModule } from '@angular/core';
|
|
3
|
+
import { clamp, createContext } from '@radix-ng/primitives/core';
|
|
4
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
5
|
+
|
|
6
|
+
/** How long (ms) the `data-scrolling` attribute stays applied after the last scroll movement. */
|
|
7
|
+
const SCROLL_TIMEOUT = 500;
|
|
8
|
+
/** Minimum size (px) of the thumb so it stays grabbable on very large content. */
|
|
9
|
+
const MIN_THUMB_SIZE = 16;
|
|
10
|
+
/** 100 ms without scroll events ≈ scroll end (mirrors the native `scrollend` heuristic). */
|
|
11
|
+
const SCROLL_END_TIMEOUT = 100;
|
|
12
|
+
/** Distance (px) within which a scroll offset is snapped to an edge. */
|
|
13
|
+
const SCROLL_EDGE_TOLERANCE_PX = 1;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns the combined start/end `margin` or `padding` of an element along an axis,
|
|
17
|
+
* resolving logical (inline/block) properties so RTL layouts compute correctly.
|
|
18
|
+
*/
|
|
19
|
+
function getOffset(element, prop, axis) {
|
|
20
|
+
if (!element) {
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
const styles = getComputedStyle(element);
|
|
24
|
+
const propAxis = axis === 'x' ? 'Inline' : 'Block';
|
|
25
|
+
// Safari misreports `marginInlineEnd` in RTL. We have to assume the start/end
|
|
26
|
+
// values are symmetrical, which is likely.
|
|
27
|
+
if (axis === 'x' && prop === 'margin') {
|
|
28
|
+
return parseFloat(styles[`${prop}InlineStart`]) * 2;
|
|
29
|
+
}
|
|
30
|
+
return (parseFloat(styles[`${prop}${propAxis}Start`]) +
|
|
31
|
+
parseFloat(styles[`${prop}${propAxis}End`]));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Snaps a scroll offset that is within {@link SCROLL_EDGE_TOLERANCE_PX} of either end
|
|
35
|
+
* to that boundary, giving the overflow edge attributes a "sticky" feel at scroll limits.
|
|
36
|
+
*/
|
|
37
|
+
function normalizeScrollOffset(value, max) {
|
|
38
|
+
if (max <= 0) {
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
const clamped = clamp(value, 0, max);
|
|
42
|
+
const startDistance = clamped;
|
|
43
|
+
const endDistance = max - clamped;
|
|
44
|
+
const withinStartTolerance = startDistance <= SCROLL_EDGE_TOLERANCE_PX;
|
|
45
|
+
const withinEndTolerance = endDistance <= SCROLL_EDGE_TOLERANCE_PX;
|
|
46
|
+
if (withinStartTolerance && withinEndTolerance) {
|
|
47
|
+
return startDistance <= endDistance ? 0 : max;
|
|
48
|
+
}
|
|
49
|
+
if (withinStartTolerance) {
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
if (withinEndTolerance) {
|
|
53
|
+
return max;
|
|
54
|
+
}
|
|
55
|
+
return clamped;
|
|
56
|
+
}
|
|
57
|
+
let scrollbarHideStylesInjected = false;
|
|
58
|
+
/**
|
|
59
|
+
* Injects (once per document) the CSS that hides the native scrollbars of the viewport.
|
|
60
|
+
* Headless directives can't hide WebKit scrollbars via inline styles, so a small
|
|
61
|
+
* stylesheet keyed by the `[rdxScrollAreaViewport]` selector is appended to `<head>`.
|
|
62
|
+
*/
|
|
63
|
+
function injectScrollbarHideStyles(document) {
|
|
64
|
+
if (scrollbarHideStylesInjected || typeof document === 'undefined') {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
scrollbarHideStylesInjected = true;
|
|
68
|
+
const style = document.createElement('style');
|
|
69
|
+
style.setAttribute('data-rdx-scroll-area', '');
|
|
70
|
+
style.textContent = `[rdxScrollAreaViewport]{scrollbar-width:none;-ms-overflow-style:none}[rdxScrollAreaViewport]::-webkit-scrollbar{display:none}`;
|
|
71
|
+
document.head.appendChild(style);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const DEFAULT_SIZE = { width: 0, height: 0 };
|
|
75
|
+
const DEFAULT_OVERFLOW_EDGES = { xStart: false, xEnd: false, yStart: false, yEnd: false };
|
|
76
|
+
const DEFAULT_HIDDEN_STATE = { x: true, y: true, corner: true };
|
|
77
|
+
const [injectScrollAreaRootContext, provideScrollAreaRootContext] = createContext('ScrollAreaRootContext', 'components/scroll-area');
|
|
78
|
+
const rootContext = () => {
|
|
79
|
+
const root = inject(RdxScrollAreaRoot);
|
|
80
|
+
return {
|
|
81
|
+
rootId: root.rootId,
|
|
82
|
+
direction: root.direction,
|
|
83
|
+
overflowEdgeThreshold: root.normalizedThreshold,
|
|
84
|
+
hovering: root.hovering,
|
|
85
|
+
scrollingX: root.scrollingX,
|
|
86
|
+
scrollingY: root.scrollingY,
|
|
87
|
+
touchModality: root.touchModality,
|
|
88
|
+
hasMeasuredScrollbar: root.hasMeasuredScrollbar,
|
|
89
|
+
cornerSize: root.cornerSize,
|
|
90
|
+
thumbSize: root.thumbSize,
|
|
91
|
+
hiddenState: root.hiddenState,
|
|
92
|
+
overflowEdges: root.overflowEdges,
|
|
93
|
+
rootRef: root.rootRef,
|
|
94
|
+
viewportRef: root.viewportRef,
|
|
95
|
+
scrollbarYRef: root.scrollbarYRef,
|
|
96
|
+
scrollbarXRef: root.scrollbarXRef,
|
|
97
|
+
thumbYRef: root.thumbYRef,
|
|
98
|
+
thumbXRef: root.thumbXRef,
|
|
99
|
+
cornerRef: root.cornerRef,
|
|
100
|
+
setHovering: (v) => root.hovering.set(v),
|
|
101
|
+
setHasMeasuredScrollbar: (v) => root.hasMeasuredScrollbar.set(v),
|
|
102
|
+
setScrollingX: (v) => root.setScrollingX(v),
|
|
103
|
+
setScrollingY: (v) => root.setScrollingY(v),
|
|
104
|
+
setThumbSize: (v) => root.setThumbSize(v),
|
|
105
|
+
setCornerSize: (v) => root.setCornerSize(v),
|
|
106
|
+
setHiddenState: (v) => root.setHiddenState(v),
|
|
107
|
+
setOverflowEdges: (v) => root.setOverflowEdges(v),
|
|
108
|
+
handleScroll: (p) => root.handleScroll(p),
|
|
109
|
+
handlePointerDown: (e) => root.handlePointerDown(e),
|
|
110
|
+
handlePointerMove: (e) => root.handlePointerMove(e),
|
|
111
|
+
handlePointerUp: (e) => root.handlePointerUp(e)
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
let idCounter = 0;
|
|
115
|
+
/**
|
|
116
|
+
* Groups all parts of the scroll area.
|
|
117
|
+
* Renders a `<div>` element.
|
|
118
|
+
*
|
|
119
|
+
* @group Components
|
|
120
|
+
*/
|
|
121
|
+
class RdxScrollAreaRoot {
|
|
122
|
+
constructor() {
|
|
123
|
+
this.destroyRef = inject(DestroyRef);
|
|
124
|
+
this.rootId = `rdx-scroll-area-${idCounter++}`;
|
|
125
|
+
/** Text direction of the scroll area. Affects horizontal (RTL) scroll math. */
|
|
126
|
+
this.dir = input('ltr', ...(ngDevMode ? [{ debugName: "dir" }] : /* istanbul ignore next */ []));
|
|
127
|
+
this.direction = this.dir;
|
|
128
|
+
/**
|
|
129
|
+
* The threshold in pixels that must be passed before the overflow edge attributes are applied.
|
|
130
|
+
* Accepts a single number for all edges or an object to configure them individually.
|
|
131
|
+
*/
|
|
132
|
+
this.overflowEdgeThreshold = input(0, ...(ngDevMode ? [{ debugName: "overflowEdgeThreshold" }] : /* istanbul ignore next */ []));
|
|
133
|
+
this.normalizedThreshold = computed(() => normalizeOverflowEdgeThreshold(this.overflowEdgeThreshold()), ...(ngDevMode ? [{ debugName: "normalizedThreshold" }] : /* istanbul ignore next */ []));
|
|
134
|
+
this.hovering = signal(false, ...(ngDevMode ? [{ debugName: "hovering" }] : /* istanbul ignore next */ []));
|
|
135
|
+
this.scrollingX = signal(false, ...(ngDevMode ? [{ debugName: "scrollingX" }] : /* istanbul ignore next */ []));
|
|
136
|
+
this.scrollingY = signal(false, ...(ngDevMode ? [{ debugName: "scrollingY" }] : /* istanbul ignore next */ []));
|
|
137
|
+
this.touchModality = signal(false, ...(ngDevMode ? [{ debugName: "touchModality" }] : /* istanbul ignore next */ []));
|
|
138
|
+
this.hasMeasuredScrollbar = signal(false, ...(ngDevMode ? [{ debugName: "hasMeasuredScrollbar" }] : /* istanbul ignore next */ []));
|
|
139
|
+
this.cornerSize = signal(DEFAULT_SIZE, ...(ngDevMode ? [{ debugName: "cornerSize" }] : /* istanbul ignore next */ []));
|
|
140
|
+
this.thumbSize = signal(DEFAULT_SIZE, ...(ngDevMode ? [{ debugName: "thumbSize" }] : /* istanbul ignore next */ []));
|
|
141
|
+
this.hiddenState = signal(DEFAULT_HIDDEN_STATE, ...(ngDevMode ? [{ debugName: "hiddenState" }] : /* istanbul ignore next */ []));
|
|
142
|
+
this.overflowEdges = signal(DEFAULT_OVERFLOW_EDGES, ...(ngDevMode ? [{ debugName: "overflowEdges" }] : /* istanbul ignore next */ []));
|
|
143
|
+
this.rootRef = { current: inject(ElementRef).nativeElement };
|
|
144
|
+
this.viewportRef = { current: null };
|
|
145
|
+
this.scrollbarYRef = { current: null };
|
|
146
|
+
this.scrollbarXRef = { current: null };
|
|
147
|
+
this.thumbYRef = { current: null };
|
|
148
|
+
this.thumbXRef = { current: null };
|
|
149
|
+
this.cornerRef = { current: null };
|
|
150
|
+
// Imperative drag state (deliberately non-reactive).
|
|
151
|
+
this.thumbDragging = false;
|
|
152
|
+
this.startY = 0;
|
|
153
|
+
this.startX = 0;
|
|
154
|
+
this.startScrollTop = 0;
|
|
155
|
+
this.startScrollLeft = 0;
|
|
156
|
+
this.currentOrientation = 'vertical';
|
|
157
|
+
this.scrollPosition = { x: 0, y: 0 };
|
|
158
|
+
this.destroyRef.onDestroy(() => {
|
|
159
|
+
clearTimeout(this.scrollYTimer);
|
|
160
|
+
clearTimeout(this.scrollXTimer);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
setScrollingX(value) {
|
|
164
|
+
this.scrollingX.set(value);
|
|
165
|
+
}
|
|
166
|
+
setScrollingY(value) {
|
|
167
|
+
this.scrollingY.set(value);
|
|
168
|
+
}
|
|
169
|
+
setThumbSize(value) {
|
|
170
|
+
const prev = this.thumbSize();
|
|
171
|
+
if (prev.width !== value.width || prev.height !== value.height) {
|
|
172
|
+
this.thumbSize.set(value);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
setCornerSize(value) {
|
|
176
|
+
const prev = this.cornerSize();
|
|
177
|
+
if (prev.width !== value.width || prev.height !== value.height) {
|
|
178
|
+
this.cornerSize.set(value);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
setHiddenState(value) {
|
|
182
|
+
const prev = this.hiddenState();
|
|
183
|
+
if (prev.x !== value.x || prev.y !== value.y || prev.corner !== value.corner) {
|
|
184
|
+
this.hiddenState.set(value);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
setOverflowEdges(value) {
|
|
188
|
+
const prev = this.overflowEdges();
|
|
189
|
+
if (prev.xStart !== value.xStart ||
|
|
190
|
+
prev.xEnd !== value.xEnd ||
|
|
191
|
+
prev.yStart !== value.yStart ||
|
|
192
|
+
prev.yEnd !== value.yEnd) {
|
|
193
|
+
this.overflowEdges.set(value);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
handleScroll(scrollPosition) {
|
|
197
|
+
const offsetX = scrollPosition.x - this.scrollPosition.x;
|
|
198
|
+
const offsetY = scrollPosition.y - this.scrollPosition.y;
|
|
199
|
+
this.scrollPosition = scrollPosition;
|
|
200
|
+
if (offsetY !== 0) {
|
|
201
|
+
this.scrollingY.set(true);
|
|
202
|
+
clearTimeout(this.scrollYTimer);
|
|
203
|
+
this.scrollYTimer = setTimeout(() => this.scrollingY.set(false), SCROLL_TIMEOUT);
|
|
204
|
+
}
|
|
205
|
+
if (offsetX !== 0) {
|
|
206
|
+
this.scrollingX.set(true);
|
|
207
|
+
clearTimeout(this.scrollXTimer);
|
|
208
|
+
this.scrollXTimer = setTimeout(() => this.scrollingX.set(false), SCROLL_TIMEOUT);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
handlePointerDown(event) {
|
|
212
|
+
if (event.button !== 0) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
this.thumbDragging = true;
|
|
216
|
+
this.startY = event.clientY;
|
|
217
|
+
this.startX = event.clientX;
|
|
218
|
+
this.currentOrientation =
|
|
219
|
+
event.currentTarget?.getAttribute('data-orientation') ??
|
|
220
|
+
'vertical';
|
|
221
|
+
const viewport = this.viewportRef.current;
|
|
222
|
+
if (viewport) {
|
|
223
|
+
this.startScrollTop = viewport.scrollTop;
|
|
224
|
+
this.startScrollLeft = viewport.scrollLeft;
|
|
225
|
+
}
|
|
226
|
+
if (this.thumbYRef.current && this.currentOrientation === 'vertical') {
|
|
227
|
+
this.thumbYRef.current.setPointerCapture(event.pointerId);
|
|
228
|
+
}
|
|
229
|
+
if (this.thumbXRef.current && this.currentOrientation === 'horizontal') {
|
|
230
|
+
this.thumbXRef.current.setPointerCapture(event.pointerId);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
handlePointerMove(event) {
|
|
234
|
+
if (!this.thumbDragging) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const deltaY = event.clientY - this.startY;
|
|
238
|
+
const deltaX = event.clientX - this.startX;
|
|
239
|
+
const viewport = this.viewportRef.current;
|
|
240
|
+
if (!viewport) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const scrollableContentHeight = viewport.scrollHeight;
|
|
244
|
+
const viewportHeight = viewport.clientHeight;
|
|
245
|
+
const scrollableContentWidth = viewport.scrollWidth;
|
|
246
|
+
const viewportWidth = viewport.clientWidth;
|
|
247
|
+
if (this.thumbYRef.current && this.scrollbarYRef.current && this.currentOrientation === 'vertical') {
|
|
248
|
+
const scrollbarYOffset = getOffset(this.scrollbarYRef.current, 'padding', 'y');
|
|
249
|
+
const thumbYOffset = getOffset(this.thumbYRef.current, 'margin', 'y');
|
|
250
|
+
const thumbHeight = this.thumbYRef.current.offsetHeight;
|
|
251
|
+
const maxThumbOffsetY = this.scrollbarYRef.current.offsetHeight - thumbHeight - scrollbarYOffset - thumbYOffset;
|
|
252
|
+
const scrollRatioY = deltaY / maxThumbOffsetY;
|
|
253
|
+
viewport.scrollTop = this.startScrollTop + scrollRatioY * (scrollableContentHeight - viewportHeight);
|
|
254
|
+
event.preventDefault();
|
|
255
|
+
this.scrollingY.set(true);
|
|
256
|
+
clearTimeout(this.scrollYTimer);
|
|
257
|
+
this.scrollYTimer = setTimeout(() => this.scrollingY.set(false), SCROLL_TIMEOUT);
|
|
258
|
+
}
|
|
259
|
+
if (this.thumbXRef.current && this.scrollbarXRef.current && this.currentOrientation === 'horizontal') {
|
|
260
|
+
const scrollbarXOffset = getOffset(this.scrollbarXRef.current, 'padding', 'x');
|
|
261
|
+
const thumbXOffset = getOffset(this.thumbXRef.current, 'margin', 'x');
|
|
262
|
+
const thumbWidth = this.thumbXRef.current.offsetWidth;
|
|
263
|
+
const maxThumbOffsetX = this.scrollbarXRef.current.offsetWidth - thumbWidth - scrollbarXOffset - thumbXOffset;
|
|
264
|
+
const scrollRatioX = deltaX / maxThumbOffsetX;
|
|
265
|
+
viewport.scrollLeft = this.startScrollLeft + scrollRatioX * (scrollableContentWidth - viewportWidth);
|
|
266
|
+
event.preventDefault();
|
|
267
|
+
this.scrollingX.set(true);
|
|
268
|
+
clearTimeout(this.scrollXTimer);
|
|
269
|
+
this.scrollXTimer = setTimeout(() => this.scrollingX.set(false), SCROLL_TIMEOUT);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
handlePointerUp(event) {
|
|
273
|
+
this.thumbDragging = false;
|
|
274
|
+
// `pointercancel` releases capture implicitly, so guard against releasing a
|
|
275
|
+
// capture we no longer hold (which would throw).
|
|
276
|
+
if (this.thumbYRef.current &&
|
|
277
|
+
this.currentOrientation === 'vertical' &&
|
|
278
|
+
this.thumbYRef.current.hasPointerCapture(event.pointerId)) {
|
|
279
|
+
this.thumbYRef.current.releasePointerCapture(event.pointerId);
|
|
280
|
+
}
|
|
281
|
+
if (this.thumbXRef.current &&
|
|
282
|
+
this.currentOrientation === 'horizontal' &&
|
|
283
|
+
this.thumbXRef.current.hasPointerCapture(event.pointerId)) {
|
|
284
|
+
this.thumbXRef.current.releasePointerCapture(event.pointerId);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
onTouchModalityChange(event) {
|
|
288
|
+
this.touchModality.set(event.pointerType === 'touch');
|
|
289
|
+
}
|
|
290
|
+
onPointerEnterOrMove(event) {
|
|
291
|
+
this.onTouchModalityChange(event);
|
|
292
|
+
if (event.pointerType !== 'touch') {
|
|
293
|
+
const root = this.rootRef.current;
|
|
294
|
+
const target = event.target;
|
|
295
|
+
this.hovering.set(!!root && !!target && root.contains(target));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
299
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxScrollAreaRoot, isStandalone: true, selector: "[rdxScrollAreaRoot]", inputs: { dir: { classPropertyName: "dir", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, overflowEdgeThreshold: { classPropertyName: "overflowEdgeThreshold", publicName: "overflowEdgeThreshold", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "presentation" }, listeners: { "pointerenter": "onPointerEnterOrMove($event)", "pointermove": "onPointerEnterOrMove($event)", "pointerdown": "onTouchModalityChange($event)", "pointerleave": "hovering.set(false)" }, properties: { "style.position": "\"relative\"", "style.--scroll-area-corner-height": "cornerSize().height + \"px\"", "style.--scroll-area-corner-width": "cornerSize().width + \"px\"", "attr.data-scrolling": "(scrollingX() || scrollingY()) ? \"\" : undefined", "attr.data-has-overflow-x": "!hiddenState().x ? \"\" : undefined", "attr.data-has-overflow-y": "!hiddenState().y ? \"\" : undefined", "attr.data-overflow-x-start": "overflowEdges().xStart ? \"\" : undefined", "attr.data-overflow-x-end": "overflowEdges().xEnd ? \"\" : undefined", "attr.data-overflow-y-start": "overflowEdges().yStart ? \"\" : undefined", "attr.data-overflow-y-end": "overflowEdges().yEnd ? \"\" : undefined" } }, providers: [provideScrollAreaRootContext(rootContext)], exportAs: ["rdxScrollAreaRoot"], ngImport: i0 }); }
|
|
300
|
+
}
|
|
301
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaRoot, decorators: [{
|
|
302
|
+
type: Directive,
|
|
303
|
+
args: [{
|
|
304
|
+
selector: '[rdxScrollAreaRoot]',
|
|
305
|
+
exportAs: 'rdxScrollAreaRoot',
|
|
306
|
+
providers: [provideScrollAreaRootContext(rootContext)],
|
|
307
|
+
host: {
|
|
308
|
+
role: 'presentation',
|
|
309
|
+
'[style.position]': '"relative"',
|
|
310
|
+
'[style.--scroll-area-corner-height]': 'cornerSize().height + "px"',
|
|
311
|
+
'[style.--scroll-area-corner-width]': 'cornerSize().width + "px"',
|
|
312
|
+
'[attr.data-scrolling]': '(scrollingX() || scrollingY()) ? "" : undefined',
|
|
313
|
+
'[attr.data-has-overflow-x]': '!hiddenState().x ? "" : undefined',
|
|
314
|
+
'[attr.data-has-overflow-y]': '!hiddenState().y ? "" : undefined',
|
|
315
|
+
'[attr.data-overflow-x-start]': 'overflowEdges().xStart ? "" : undefined',
|
|
316
|
+
'[attr.data-overflow-x-end]': 'overflowEdges().xEnd ? "" : undefined',
|
|
317
|
+
'[attr.data-overflow-y-start]': 'overflowEdges().yStart ? "" : undefined',
|
|
318
|
+
'[attr.data-overflow-y-end]': 'overflowEdges().yEnd ? "" : undefined',
|
|
319
|
+
'(pointerenter)': 'onPointerEnterOrMove($event)',
|
|
320
|
+
'(pointermove)': 'onPointerEnterOrMove($event)',
|
|
321
|
+
'(pointerdown)': 'onTouchModalityChange($event)',
|
|
322
|
+
'(pointerleave)': 'hovering.set(false)'
|
|
323
|
+
}
|
|
324
|
+
}]
|
|
325
|
+
}], ctorParameters: () => [], propDecorators: { dir: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], overflowEdgeThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "overflowEdgeThreshold", required: false }] }] } });
|
|
326
|
+
function normalizeOverflowEdgeThreshold(threshold) {
|
|
327
|
+
if (typeof threshold === 'number') {
|
|
328
|
+
const value = Math.max(0, threshold);
|
|
329
|
+
return { xStart: value, xEnd: value, yStart: value, yEnd: value };
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
xStart: Math.max(0, threshold?.xStart || 0),
|
|
333
|
+
xEnd: Math.max(0, threshold?.xEnd || 0),
|
|
334
|
+
yStart: Math.max(0, threshold?.yStart || 0),
|
|
335
|
+
yEnd: Math.max(0, threshold?.yEnd || 0)
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const [injectScrollAreaViewportContext, provideScrollAreaViewportContext] = createContext('ScrollAreaViewportContext', 'components/scroll-area');
|
|
340
|
+
const viewportContext = () => {
|
|
341
|
+
const viewport = inject(RdxScrollAreaViewport);
|
|
342
|
+
return {
|
|
343
|
+
computeThumbPosition: () => viewport.computeThumbPosition()
|
|
344
|
+
};
|
|
345
|
+
};
|
|
346
|
+
/**
|
|
347
|
+
* The actual scrollable container of the scroll area.
|
|
348
|
+
* Renders a `<div>` element.
|
|
349
|
+
*
|
|
350
|
+
* @group Components
|
|
351
|
+
*/
|
|
352
|
+
class RdxScrollAreaViewport {
|
|
353
|
+
constructor() {
|
|
354
|
+
this.rootContext = injectScrollAreaRootContext();
|
|
355
|
+
this.element = inject(ElementRef).nativeElement;
|
|
356
|
+
this.destroyRef = inject(DestroyRef);
|
|
357
|
+
this.isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
358
|
+
this.programmaticScroll = true;
|
|
359
|
+
this.lastMeasuredMetrics = [NaN, NaN, NaN, NaN];
|
|
360
|
+
this.rootContext.viewportRef.current = this.element;
|
|
361
|
+
if (this.isBrowser) {
|
|
362
|
+
injectScrollbarHideStyles(document);
|
|
363
|
+
}
|
|
364
|
+
// Recompute after hidden-state toggles (so newly shown scrollbars get measured),
|
|
365
|
+
// on direction flips, and when the overflow-edge threshold changes.
|
|
366
|
+
const hiddenState = this.rootContext.hiddenState;
|
|
367
|
+
const direction = this.rootContext.direction;
|
|
368
|
+
const threshold = this.rootContext.overflowEdgeThreshold;
|
|
369
|
+
effect(() => {
|
|
370
|
+
hiddenState();
|
|
371
|
+
direction();
|
|
372
|
+
threshold();
|
|
373
|
+
queueMicrotask(() => this.computeThumbPosition());
|
|
374
|
+
});
|
|
375
|
+
afterNextRender(() => {
|
|
376
|
+
this.computeThumbPosition();
|
|
377
|
+
this.observeViewport();
|
|
378
|
+
if (this.element.matches(':hover')) {
|
|
379
|
+
this.rootContext.setHovering(true);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
this.destroyRef.onDestroy(() => {
|
|
383
|
+
clearTimeout(this.scrollEndTimer);
|
|
384
|
+
clearTimeout(this.animationsTimer);
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
computeThumbPosition() {
|
|
388
|
+
const viewportEl = this.rootContext.viewportRef.current;
|
|
389
|
+
const scrollbarYEl = this.rootContext.scrollbarYRef.current;
|
|
390
|
+
const scrollbarXEl = this.rootContext.scrollbarXRef.current;
|
|
391
|
+
const thumbYEl = this.rootContext.thumbYRef.current;
|
|
392
|
+
const thumbXEl = this.rootContext.thumbXRef.current;
|
|
393
|
+
const cornerEl = this.rootContext.cornerRef.current;
|
|
394
|
+
if (!viewportEl) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const direction = this.rootContext.direction();
|
|
398
|
+
const threshold = this.rootContext.overflowEdgeThreshold();
|
|
399
|
+
const cornerSize = this.rootContext.cornerSize();
|
|
400
|
+
const scrollableContentHeight = viewportEl.scrollHeight;
|
|
401
|
+
const scrollableContentWidth = viewportEl.scrollWidth;
|
|
402
|
+
const viewportHeight = viewportEl.clientHeight;
|
|
403
|
+
const viewportWidth = viewportEl.clientWidth;
|
|
404
|
+
const scrollTop = viewportEl.scrollTop;
|
|
405
|
+
const scrollLeft = viewportEl.scrollLeft;
|
|
406
|
+
const isFirstMeasurement = Number.isNaN(this.lastMeasuredMetrics[0]);
|
|
407
|
+
this.lastMeasuredMetrics = [viewportHeight, scrollableContentHeight, viewportWidth, scrollableContentWidth];
|
|
408
|
+
if (isFirstMeasurement) {
|
|
409
|
+
this.rootContext.setHasMeasuredScrollbar(true);
|
|
410
|
+
}
|
|
411
|
+
if (scrollableContentHeight === 0 || scrollableContentWidth === 0) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const nextHiddenState = getHiddenState(viewportEl);
|
|
415
|
+
const scrollbarYHidden = nextHiddenState.y;
|
|
416
|
+
const scrollbarXHidden = nextHiddenState.x;
|
|
417
|
+
const ratioX = viewportWidth / scrollableContentWidth;
|
|
418
|
+
const ratioY = viewportHeight / scrollableContentHeight;
|
|
419
|
+
const maxScrollLeft = Math.max(0, scrollableContentWidth - viewportWidth);
|
|
420
|
+
const maxScrollTop = Math.max(0, scrollableContentHeight - viewportHeight);
|
|
421
|
+
let scrollLeftFromStart = 0;
|
|
422
|
+
let scrollLeftFromEnd = 0;
|
|
423
|
+
if (!scrollbarXHidden) {
|
|
424
|
+
const rawScrollLeftFromStart = direction === 'rtl' ? clamp(-scrollLeft, 0, maxScrollLeft) : clamp(scrollLeft, 0, maxScrollLeft);
|
|
425
|
+
scrollLeftFromStart = normalizeScrollOffset(rawScrollLeftFromStart, maxScrollLeft);
|
|
426
|
+
scrollLeftFromEnd = maxScrollLeft - scrollLeftFromStart;
|
|
427
|
+
}
|
|
428
|
+
const rawScrollTopFromStart = !scrollbarYHidden ? clamp(scrollTop, 0, maxScrollTop) : 0;
|
|
429
|
+
const scrollTopFromStart = !scrollbarYHidden ? normalizeScrollOffset(rawScrollTopFromStart, maxScrollTop) : 0;
|
|
430
|
+
const scrollTopFromEnd = !scrollbarYHidden ? maxScrollTop - scrollTopFromStart : 0;
|
|
431
|
+
const nextWidth = scrollbarXHidden ? 0 : viewportWidth;
|
|
432
|
+
const nextHeight = scrollbarYHidden ? 0 : viewportHeight;
|
|
433
|
+
let nextCornerWidth = 0;
|
|
434
|
+
let nextCornerHeight = 0;
|
|
435
|
+
if (!scrollbarXHidden && !scrollbarYHidden) {
|
|
436
|
+
nextCornerWidth = scrollbarYEl?.offsetWidth || 0;
|
|
437
|
+
nextCornerHeight = scrollbarXEl?.offsetHeight || 0;
|
|
438
|
+
}
|
|
439
|
+
// Only subtract corner size from scrollbar dimensions if the corner hasn't been sized yet.
|
|
440
|
+
const cornerNotYetSized = cornerSize.width === 0 && cornerSize.height === 0;
|
|
441
|
+
const cornerWidthOffset = cornerNotYetSized ? nextCornerWidth : 0;
|
|
442
|
+
const cornerHeightOffset = cornerNotYetSized ? nextCornerHeight : 0;
|
|
443
|
+
const scrollbarXOffset = getOffset(scrollbarXEl, 'padding', 'x');
|
|
444
|
+
const scrollbarYOffset = getOffset(scrollbarYEl, 'padding', 'y');
|
|
445
|
+
const thumbXOffset = getOffset(thumbXEl, 'margin', 'x');
|
|
446
|
+
const thumbYOffset = getOffset(thumbYEl, 'margin', 'y');
|
|
447
|
+
const idealNextWidth = nextWidth - scrollbarXOffset - thumbXOffset;
|
|
448
|
+
const idealNextHeight = nextHeight - scrollbarYOffset - thumbYOffset;
|
|
449
|
+
const maxNextWidth = scrollbarXEl
|
|
450
|
+
? Math.min(scrollbarXEl.offsetWidth - cornerWidthOffset, idealNextWidth)
|
|
451
|
+
: idealNextWidth;
|
|
452
|
+
const maxNextHeight = scrollbarYEl
|
|
453
|
+
? Math.min(scrollbarYEl.offsetHeight - cornerHeightOffset, idealNextHeight)
|
|
454
|
+
: idealNextHeight;
|
|
455
|
+
const clampedNextWidth = Math.max(MIN_THUMB_SIZE, maxNextWidth * ratioX);
|
|
456
|
+
const clampedNextHeight = Math.max(MIN_THUMB_SIZE, maxNextHeight * ratioY);
|
|
457
|
+
this.rootContext.setThumbSize({ width: clampedNextWidth, height: clampedNextHeight });
|
|
458
|
+
// Handle Y (vertical) scroll
|
|
459
|
+
if (scrollbarYEl && thumbYEl) {
|
|
460
|
+
const maxThumbOffsetY = scrollbarYEl.offsetHeight - clampedNextHeight - scrollbarYOffset - thumbYOffset;
|
|
461
|
+
const scrollRangeY = scrollableContentHeight - viewportHeight;
|
|
462
|
+
const scrollRatioY = scrollRangeY === 0 ? 0 : scrollTop / scrollRangeY;
|
|
463
|
+
// In Safari, don't allow it to go negative or too far as `scrollTop` considers the rubber band effect.
|
|
464
|
+
const thumbOffsetY = Math.min(maxThumbOffsetY, Math.max(0, scrollRatioY * maxThumbOffsetY));
|
|
465
|
+
thumbYEl.style.transform = `translate3d(0,${thumbOffsetY}px,0)`;
|
|
466
|
+
}
|
|
467
|
+
// Handle X (horizontal) scroll
|
|
468
|
+
if (scrollbarXEl && thumbXEl) {
|
|
469
|
+
const maxThumbOffsetX = scrollbarXEl.offsetWidth - clampedNextWidth - scrollbarXOffset - thumbXOffset;
|
|
470
|
+
const scrollRangeX = scrollableContentWidth - viewportWidth;
|
|
471
|
+
const scrollRatioX = scrollRangeX === 0 ? 0 : scrollLeft / scrollRangeX;
|
|
472
|
+
const thumbOffsetX = direction === 'rtl'
|
|
473
|
+
? clamp(scrollRatioX * maxThumbOffsetX, -maxThumbOffsetX, 0)
|
|
474
|
+
: clamp(scrollRatioX * maxThumbOffsetX, 0, maxThumbOffsetX);
|
|
475
|
+
thumbXEl.style.transform = `translate3d(${thumbOffsetX}px,0,0)`;
|
|
476
|
+
}
|
|
477
|
+
viewportEl.style.setProperty('--scroll-area-overflow-x-start', `${scrollLeftFromStart}px`);
|
|
478
|
+
viewportEl.style.setProperty('--scroll-area-overflow-x-end', `${scrollLeftFromEnd}px`);
|
|
479
|
+
viewportEl.style.setProperty('--scroll-area-overflow-y-start', `${scrollTopFromStart}px`);
|
|
480
|
+
viewportEl.style.setProperty('--scroll-area-overflow-y-end', `${scrollTopFromEnd}px`);
|
|
481
|
+
if (cornerEl) {
|
|
482
|
+
if (scrollbarXHidden || scrollbarYHidden) {
|
|
483
|
+
this.rootContext.setCornerSize({ width: 0, height: 0 });
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
this.rootContext.setCornerSize({ width: nextCornerWidth, height: nextCornerHeight });
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
this.rootContext.setHiddenState(nextHiddenState);
|
|
490
|
+
this.rootContext.setOverflowEdges({
|
|
491
|
+
xStart: !scrollbarXHidden && scrollLeftFromStart > threshold.xStart,
|
|
492
|
+
xEnd: !scrollbarXHidden && scrollLeftFromEnd > threshold.xEnd,
|
|
493
|
+
yStart: !scrollbarYHidden && scrollTopFromStart > threshold.yStart,
|
|
494
|
+
yEnd: !scrollbarYHidden && scrollTopFromEnd > threshold.yEnd
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
onScroll() {
|
|
498
|
+
const viewportEl = this.rootContext.viewportRef.current;
|
|
499
|
+
if (!viewportEl) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
this.computeThumbPosition();
|
|
503
|
+
if (!this.programmaticScroll) {
|
|
504
|
+
this.rootContext.handleScroll({ x: viewportEl.scrollLeft, y: viewportEl.scrollTop });
|
|
505
|
+
}
|
|
506
|
+
// Debounce restoring the programmatic flag so momentum scrolling (no further
|
|
507
|
+
// user-interaction events) is still treated as user-driven.
|
|
508
|
+
clearTimeout(this.scrollEndTimer);
|
|
509
|
+
this.scrollEndTimer = setTimeout(() => {
|
|
510
|
+
this.programmaticScroll = true;
|
|
511
|
+
}, SCROLL_END_TIMEOUT);
|
|
512
|
+
}
|
|
513
|
+
onUserInteraction() {
|
|
514
|
+
this.programmaticScroll = false;
|
|
515
|
+
}
|
|
516
|
+
observeViewport() {
|
|
517
|
+
const viewport = this.element;
|
|
518
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
let hasInitialized = false;
|
|
522
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
523
|
+
if (!hasInitialized) {
|
|
524
|
+
hasInitialized = true;
|
|
525
|
+
const m = this.lastMeasuredMetrics;
|
|
526
|
+
if (m[0] === viewport.clientHeight &&
|
|
527
|
+
m[1] === viewport.scrollHeight &&
|
|
528
|
+
m[2] === viewport.clientWidth &&
|
|
529
|
+
m[3] === viewport.scrollWidth) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
this.computeThumbPosition();
|
|
534
|
+
});
|
|
535
|
+
resizeObserver.observe(viewport);
|
|
536
|
+
// Wait for subtree animations to finish, then recompute geometry that may have
|
|
537
|
+
// been affected by transform-based animations.
|
|
538
|
+
this.animationsTimer = setTimeout(() => {
|
|
539
|
+
const animations = viewport.getAnimations({ subtree: true });
|
|
540
|
+
if (animations.length === 0) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
Promise.allSettled(animations.map((animation) => animation.finished))
|
|
544
|
+
.then(() => this.computeThumbPosition())
|
|
545
|
+
.catch(() => { });
|
|
546
|
+
}, 0);
|
|
547
|
+
this.destroyRef.onDestroy(() => resizeObserver.disconnect());
|
|
548
|
+
}
|
|
549
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaViewport, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
550
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxScrollAreaViewport, isStandalone: true, selector: "[rdxScrollAreaViewport]", host: { attributes: { "role": "presentation" }, listeners: { "scroll": "onScroll()", "wheel": "onUserInteraction()", "touchmove": "onUserInteraction()", "pointermove": "onUserInteraction()", "pointerenter": "onUserInteraction()", "keydown": "onUserInteraction()" }, properties: { "style.overflow": "\"scroll\"", "attr.tabindex": "(rootContext.hiddenState().x && rootContext.hiddenState().y) ? -1 : 0", "attr.data-id": "rootContext.rootId + \"-viewport\"", "attr.data-scrolling": "(rootContext.scrollingX() || rootContext.scrollingY()) ? \"\" : undefined", "attr.data-has-overflow-x": "!rootContext.hiddenState().x ? \"\" : undefined", "attr.data-has-overflow-y": "!rootContext.hiddenState().y ? \"\" : undefined", "attr.data-overflow-x-start": "rootContext.overflowEdges().xStart ? \"\" : undefined", "attr.data-overflow-x-end": "rootContext.overflowEdges().xEnd ? \"\" : undefined", "attr.data-overflow-y-start": "rootContext.overflowEdges().yStart ? \"\" : undefined", "attr.data-overflow-y-end": "rootContext.overflowEdges().yEnd ? \"\" : undefined" } }, providers: [provideScrollAreaViewportContext(viewportContext)], exportAs: ["rdxScrollAreaViewport"], ngImport: i0 }); }
|
|
551
|
+
}
|
|
552
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaViewport, decorators: [{
|
|
553
|
+
type: Directive,
|
|
554
|
+
args: [{
|
|
555
|
+
selector: '[rdxScrollAreaViewport]',
|
|
556
|
+
exportAs: 'rdxScrollAreaViewport',
|
|
557
|
+
providers: [provideScrollAreaViewportContext(viewportContext)],
|
|
558
|
+
host: {
|
|
559
|
+
role: 'presentation',
|
|
560
|
+
'[style.overflow]': '"scroll"',
|
|
561
|
+
'[attr.tabindex]': '(rootContext.hiddenState().x && rootContext.hiddenState().y) ? -1 : 0',
|
|
562
|
+
'[attr.data-id]': 'rootContext.rootId + "-viewport"',
|
|
563
|
+
'[attr.data-scrolling]': '(rootContext.scrollingX() || rootContext.scrollingY()) ? "" : undefined',
|
|
564
|
+
'[attr.data-has-overflow-x]': '!rootContext.hiddenState().x ? "" : undefined',
|
|
565
|
+
'[attr.data-has-overflow-y]': '!rootContext.hiddenState().y ? "" : undefined',
|
|
566
|
+
'[attr.data-overflow-x-start]': 'rootContext.overflowEdges().xStart ? "" : undefined',
|
|
567
|
+
'[attr.data-overflow-x-end]': 'rootContext.overflowEdges().xEnd ? "" : undefined',
|
|
568
|
+
'[attr.data-overflow-y-start]': 'rootContext.overflowEdges().yStart ? "" : undefined',
|
|
569
|
+
'[attr.data-overflow-y-end]': 'rootContext.overflowEdges().yEnd ? "" : undefined',
|
|
570
|
+
'(scroll)': 'onScroll()',
|
|
571
|
+
'(wheel)': 'onUserInteraction()',
|
|
572
|
+
'(touchmove)': 'onUserInteraction()',
|
|
573
|
+
'(pointermove)': 'onUserInteraction()',
|
|
574
|
+
'(pointerenter)': 'onUserInteraction()',
|
|
575
|
+
'(keydown)': 'onUserInteraction()'
|
|
576
|
+
}
|
|
577
|
+
}]
|
|
578
|
+
}], ctorParameters: () => [] });
|
|
579
|
+
function getHiddenState(viewport) {
|
|
580
|
+
const y = viewport.clientHeight >= viewport.scrollHeight;
|
|
581
|
+
const x = viewport.clientWidth >= viewport.scrollWidth;
|
|
582
|
+
return { y, x, corner: y || x };
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* A container for the content of the scroll area.
|
|
587
|
+
* Renders a `<div>` element.
|
|
588
|
+
*
|
|
589
|
+
* @group Components
|
|
590
|
+
*/
|
|
591
|
+
class RdxScrollAreaContent {
|
|
592
|
+
constructor() {
|
|
593
|
+
this.rootContext = injectScrollAreaRootContext();
|
|
594
|
+
this.viewportContext = injectScrollAreaViewportContext();
|
|
595
|
+
this.element = inject(ElementRef).nativeElement;
|
|
596
|
+
this.destroyRef = inject(DestroyRef);
|
|
597
|
+
// Whether the content mounted after the viewport's first measurement; if so the
|
|
598
|
+
// initial ResizeObserver fire is what brings the overflow state in sync.
|
|
599
|
+
const computeOnInitialResize = this.rootContext.hasMeasuredScrollbar();
|
|
600
|
+
afterNextRender(() => {
|
|
601
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
let hasInitialized = false;
|
|
605
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
606
|
+
if (!hasInitialized) {
|
|
607
|
+
hasInitialized = true;
|
|
608
|
+
// ResizeObserver fires once upon observing. Skip that initial call to avoid
|
|
609
|
+
// double-calculating the thumb position on mount, unless the content mounted
|
|
610
|
+
// after the viewport's initial measurement.
|
|
611
|
+
if (!computeOnInitialResize) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
this.viewportContext.computeThumbPosition();
|
|
616
|
+
});
|
|
617
|
+
resizeObserver.observe(this.element);
|
|
618
|
+
this.destroyRef.onDestroy(() => resizeObserver.disconnect());
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaContent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
622
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxScrollAreaContent, isStandalone: true, selector: "[rdxScrollAreaContent]", host: { attributes: { "role": "presentation" }, properties: { "style.min-width": "\"fit-content\"", "attr.data-scrolling": "(rootContext.scrollingX() || rootContext.scrollingY()) ? \"\" : undefined", "attr.data-has-overflow-x": "!rootContext.hiddenState().x ? \"\" : undefined", "attr.data-has-overflow-y": "!rootContext.hiddenState().y ? \"\" : undefined", "attr.data-overflow-x-start": "rootContext.overflowEdges().xStart ? \"\" : undefined", "attr.data-overflow-x-end": "rootContext.overflowEdges().xEnd ? \"\" : undefined", "attr.data-overflow-y-start": "rootContext.overflowEdges().yStart ? \"\" : undefined", "attr.data-overflow-y-end": "rootContext.overflowEdges().yEnd ? \"\" : undefined" } }, exportAs: ["rdxScrollAreaContent"], ngImport: i0 }); }
|
|
623
|
+
}
|
|
624
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaContent, decorators: [{
|
|
625
|
+
type: Directive,
|
|
626
|
+
args: [{
|
|
627
|
+
selector: '[rdxScrollAreaContent]',
|
|
628
|
+
exportAs: 'rdxScrollAreaContent',
|
|
629
|
+
host: {
|
|
630
|
+
role: 'presentation',
|
|
631
|
+
'[style.min-width]': '"fit-content"',
|
|
632
|
+
'[attr.data-scrolling]': '(rootContext.scrollingX() || rootContext.scrollingY()) ? "" : undefined',
|
|
633
|
+
'[attr.data-has-overflow-x]': '!rootContext.hiddenState().x ? "" : undefined',
|
|
634
|
+
'[attr.data-has-overflow-y]': '!rootContext.hiddenState().y ? "" : undefined',
|
|
635
|
+
'[attr.data-overflow-x-start]': 'rootContext.overflowEdges().xStart ? "" : undefined',
|
|
636
|
+
'[attr.data-overflow-x-end]': 'rootContext.overflowEdges().xEnd ? "" : undefined',
|
|
637
|
+
'[attr.data-overflow-y-start]': 'rootContext.overflowEdges().yStart ? "" : undefined',
|
|
638
|
+
'[attr.data-overflow-y-end]': 'rootContext.overflowEdges().yEnd ? "" : undefined'
|
|
639
|
+
}
|
|
640
|
+
}]
|
|
641
|
+
}], ctorParameters: () => [] });
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* A small rectangular area that appears at the intersection of horizontal and vertical scrollbars.
|
|
645
|
+
* Renders a `<div>` element.
|
|
646
|
+
*
|
|
647
|
+
* @group Components
|
|
648
|
+
*/
|
|
649
|
+
class RdxScrollAreaCorner {
|
|
650
|
+
constructor() {
|
|
651
|
+
this.rootContext = injectScrollAreaRootContext();
|
|
652
|
+
this.rootContext.cornerRef.current = inject(ElementRef).nativeElement;
|
|
653
|
+
}
|
|
654
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaCorner, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
655
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxScrollAreaCorner, isStandalone: true, selector: "[rdxScrollAreaCorner]", host: { properties: { "style.position": "\"absolute\"", "style.bottom": "\"0\"", "style.inset-inline-end": "\"0\"", "style.width": "rootContext.cornerSize().width + \"px\"", "style.height": "rootContext.cornerSize().height + \"px\"", "style.display": "rootContext.hiddenState().corner ? \"none\" : null" } }, exportAs: ["rdxScrollAreaCorner"], ngImport: i0 }); }
|
|
656
|
+
}
|
|
657
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaCorner, decorators: [{
|
|
658
|
+
type: Directive,
|
|
659
|
+
args: [{
|
|
660
|
+
selector: '[rdxScrollAreaCorner]',
|
|
661
|
+
exportAs: 'rdxScrollAreaCorner',
|
|
662
|
+
host: {
|
|
663
|
+
'[style.position]': '"absolute"',
|
|
664
|
+
'[style.bottom]': '"0"',
|
|
665
|
+
'[style.inset-inline-end]': '"0"',
|
|
666
|
+
'[style.width]': 'rootContext.cornerSize().width + "px"',
|
|
667
|
+
'[style.height]': 'rootContext.cornerSize().height + "px"',
|
|
668
|
+
'[style.display]': 'rootContext.hiddenState().corner ? "none" : null'
|
|
669
|
+
}
|
|
670
|
+
}]
|
|
671
|
+
}], ctorParameters: () => [] });
|
|
672
|
+
|
|
673
|
+
const [injectScrollAreaScrollbarContext, provideScrollAreaScrollbarContext] = createContext('ScrollAreaScrollbarContext', 'components/scroll-area');
|
|
674
|
+
const scrollbarContext = () => {
|
|
675
|
+
const scrollbar = inject(RdxScrollAreaScrollbar);
|
|
676
|
+
return { orientation: () => scrollbar.orientation() };
|
|
677
|
+
};
|
|
678
|
+
/**
|
|
679
|
+
* A vertical or horizontal scrollbar for the scroll area.
|
|
680
|
+
* Renders a `<div>` element.
|
|
681
|
+
*
|
|
682
|
+
* @group Components
|
|
683
|
+
*/
|
|
684
|
+
class RdxScrollAreaScrollbar {
|
|
685
|
+
constructor() {
|
|
686
|
+
this.rootContext = injectScrollAreaRootContext();
|
|
687
|
+
this.element = inject(ElementRef).nativeElement;
|
|
688
|
+
/** Whether the scrollbar controls vertical or horizontal scroll. */
|
|
689
|
+
this.orientation = input('vertical', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
|
|
690
|
+
/** Whether to keep the element rendered when the viewport isn't scrollable. */
|
|
691
|
+
this.keepMounted = input(false, { ...(ngDevMode ? { debugName: "keepMounted" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
692
|
+
this.scrolling = computed(() => this.orientation() === 'horizontal' ? this.rootContext.scrollingX() : this.rootContext.scrollingY(), ...(ngDevMode ? [{ debugName: "scrolling" }] : /* istanbul ignore next */ []));
|
|
693
|
+
this.isHidden = computed(() => this.orientation() === 'vertical' ? this.rootContext.hiddenState().y : this.rootContext.hiddenState().x, ...(ngDevMode ? [{ debugName: "isHidden" }] : /* istanbul ignore next */ []));
|
|
694
|
+
this.shouldRender = computed(() => this.keepMounted() || !this.isHidden(), ...(ngDevMode ? [{ debugName: "shouldRender" }] : /* istanbul ignore next */ []));
|
|
695
|
+
this.hideTrackUntilMeasured = computed(() => !this.rootContext.hasMeasuredScrollbar() && !this.keepMounted(), ...(ngDevMode ? [{ debugName: "hideTrackUntilMeasured" }] : /* istanbul ignore next */ []));
|
|
696
|
+
// Register this element as the vertical or horizontal scrollbar ref.
|
|
697
|
+
// Runs as an effect so the bound `orientation` input is available.
|
|
698
|
+
effect(() => {
|
|
699
|
+
if (this.orientation() === 'vertical') {
|
|
700
|
+
this.rootContext.scrollbarYRef.current = this.element;
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
this.rootContext.scrollbarXRef.current = this.element;
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
onWheel(event) {
|
|
708
|
+
const viewportEl = this.rootContext.viewportRef.current;
|
|
709
|
+
if (!viewportEl || event.ctrlKey) {
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
const horizontal = this.orientation() === 'horizontal';
|
|
713
|
+
const delta = horizontal ? event.deltaX : event.deltaY;
|
|
714
|
+
if (delta === 0) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
const direction = this.rootContext.direction();
|
|
718
|
+
const maxScroll = horizontal
|
|
719
|
+
? viewportEl.scrollWidth - viewportEl.clientWidth
|
|
720
|
+
: viewportEl.scrollHeight - viewportEl.clientHeight;
|
|
721
|
+
// RTL horizontal scrolling uses a negative `scrollLeft` range, from 0 to `-maxScroll`.
|
|
722
|
+
const minScroll = horizontal && direction === 'rtl' ? -maxScroll : 0;
|
|
723
|
+
const maxScrollValue = horizontal && direction === 'rtl' ? 0 : maxScroll;
|
|
724
|
+
const scrollValue = horizontal ? viewportEl.scrollLeft : viewportEl.scrollTop;
|
|
725
|
+
// At an edge (or with no overflow), let the wheel chain to the parent/page.
|
|
726
|
+
if ((scrollValue <= minScroll && delta < 0) || (scrollValue >= maxScrollValue && delta > 0)) {
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
event.preventDefault();
|
|
730
|
+
const next = Math.min(maxScrollValue, Math.max(minScroll, scrollValue + delta));
|
|
731
|
+
if (horizontal) {
|
|
732
|
+
viewportEl.scrollLeft = next;
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
viewportEl.scrollTop = next;
|
|
736
|
+
}
|
|
737
|
+
this.rootContext.handleScroll({ x: viewportEl.scrollLeft, y: viewportEl.scrollTop });
|
|
738
|
+
}
|
|
739
|
+
onPointerDown(event) {
|
|
740
|
+
if (event.button !== 0) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
const orientation = this.orientation();
|
|
744
|
+
const target = event.target;
|
|
745
|
+
const thumb = orientation === 'vertical' ? this.rootContext.thumbYRef.current : this.rootContext.thumbXRef.current;
|
|
746
|
+
// Ignore clicks on the thumb itself.
|
|
747
|
+
if (thumb && target && thumb.contains(target)) {
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
const viewportEl = this.rootContext.viewportRef.current;
|
|
751
|
+
if (!viewportEl) {
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
const direction = this.rootContext.direction();
|
|
755
|
+
if (orientation === 'vertical' &&
|
|
756
|
+
this.rootContext.thumbYRef.current &&
|
|
757
|
+
this.rootContext.scrollbarYRef.current) {
|
|
758
|
+
const thumbEl = this.rootContext.thumbYRef.current;
|
|
759
|
+
const scrollbarEl = this.rootContext.scrollbarYRef.current;
|
|
760
|
+
const thumbYOffset = getOffset(thumbEl, 'margin', 'y');
|
|
761
|
+
const scrollbarYOffset = getOffset(scrollbarEl, 'padding', 'y');
|
|
762
|
+
const thumbHeight = thumbEl.offsetHeight;
|
|
763
|
+
const trackRectY = scrollbarEl.getBoundingClientRect();
|
|
764
|
+
const clickY = event.clientY - trackRectY.top - thumbHeight / 2 - scrollbarYOffset + thumbYOffset / 2;
|
|
765
|
+
const maxThumbOffsetY = scrollbarEl.offsetHeight - thumbHeight - scrollbarYOffset - thumbYOffset;
|
|
766
|
+
const scrollRatioY = clickY / maxThumbOffsetY;
|
|
767
|
+
viewportEl.scrollTop = scrollRatioY * (viewportEl.scrollHeight - viewportEl.clientHeight);
|
|
768
|
+
}
|
|
769
|
+
if (orientation === 'horizontal' &&
|
|
770
|
+
this.rootContext.thumbXRef.current &&
|
|
771
|
+
this.rootContext.scrollbarXRef.current) {
|
|
772
|
+
const thumbEl = this.rootContext.thumbXRef.current;
|
|
773
|
+
const scrollbarEl = this.rootContext.scrollbarXRef.current;
|
|
774
|
+
const thumbXOffset = getOffset(thumbEl, 'margin', 'x');
|
|
775
|
+
const scrollbarXOffset = getOffset(scrollbarEl, 'padding', 'x');
|
|
776
|
+
const thumbWidth = thumbEl.offsetWidth;
|
|
777
|
+
const trackRectX = scrollbarEl.getBoundingClientRect();
|
|
778
|
+
const clickX = event.clientX - trackRectX.left - thumbWidth / 2 - scrollbarXOffset + thumbXOffset / 2;
|
|
779
|
+
const maxThumbOffsetX = scrollbarEl.offsetWidth - thumbWidth - scrollbarXOffset - thumbXOffset;
|
|
780
|
+
const scrollRatioX = clickX / maxThumbOffsetX;
|
|
781
|
+
const scrollRange = viewportEl.scrollWidth - viewportEl.clientWidth;
|
|
782
|
+
let newScrollLeft;
|
|
783
|
+
if (direction === 'rtl') {
|
|
784
|
+
newScrollLeft = (1 - scrollRatioX) * scrollRange;
|
|
785
|
+
// Adjust for browsers that use negative scrollLeft in RTL.
|
|
786
|
+
if (viewportEl.scrollLeft <= 0) {
|
|
787
|
+
newScrollLeft = -newScrollLeft;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
else {
|
|
791
|
+
newScrollLeft = scrollRatioX * scrollRange;
|
|
792
|
+
}
|
|
793
|
+
viewportEl.scrollLeft = newScrollLeft;
|
|
794
|
+
}
|
|
795
|
+
this.rootContext.handleScroll({ x: viewportEl.scrollLeft, y: viewportEl.scrollTop });
|
|
796
|
+
this.rootContext.handlePointerDown(event);
|
|
797
|
+
}
|
|
798
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaScrollbar, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
799
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxScrollAreaScrollbar, isStandalone: true, selector: "[rdxScrollAreaScrollbar]", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, keepMounted: { classPropertyName: "keepMounted", publicName: "keepMounted", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "pointerdown": "onPointerDown($event)", "pointerup": "rootContext.handlePointerUp($event)", "pointercancel": "rootContext.handlePointerUp($event)", "wheel": "onWheel($event)" }, properties: { "attr.data-id": "rootContext.rootId + \"-scrollbar\"", "attr.data-orientation": "orientation()", "attr.data-hovering": "rootContext.hovering() ? \"\" : undefined", "attr.data-scrolling": "scrolling() ? \"\" : undefined", "attr.data-has-overflow-x": "!rootContext.hiddenState().x ? \"\" : undefined", "attr.data-has-overflow-y": "!rootContext.hiddenState().y ? \"\" : undefined", "attr.data-overflow-x-start": "rootContext.overflowEdges().xStart ? \"\" : undefined", "attr.data-overflow-x-end": "rootContext.overflowEdges().xEnd ? \"\" : undefined", "attr.data-overflow-y-start": "rootContext.overflowEdges().yStart ? \"\" : undefined", "attr.data-overflow-y-end": "rootContext.overflowEdges().yEnd ? \"\" : undefined", "style.position": "\"absolute\"", "style.touch-action": "\"none\"", "style.user-select": "\"none\"", "style.display": "shouldRender() ? null : \"none\"", "style.visibility": "hideTrackUntilMeasured() ? \"hidden\" : null", "style.top": "orientation() === \"vertical\" ? \"0\" : null", "style.bottom": "orientation() === \"vertical\" ? \"var(--scroll-area-corner-height)\" : \"0\"", "style.inset-inline-start": "orientation() === \"horizontal\" ? \"0\" : null", "style.inset-inline-end": "orientation() === \"vertical\" ? \"0\" : \"var(--scroll-area-corner-width)\"", "style.--scroll-area-thumb-height": "orientation() === \"vertical\" ? rootContext.thumbSize().height + \"px\" : null", "style.--scroll-area-thumb-width": "orientation() === \"horizontal\" ? rootContext.thumbSize().width + \"px\" : null" } }, providers: [provideScrollAreaScrollbarContext(scrollbarContext)], exportAs: ["rdxScrollAreaScrollbar"], ngImport: i0 }); }
|
|
800
|
+
}
|
|
801
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaScrollbar, decorators: [{
|
|
802
|
+
type: Directive,
|
|
803
|
+
args: [{
|
|
804
|
+
selector: '[rdxScrollAreaScrollbar]',
|
|
805
|
+
exportAs: 'rdxScrollAreaScrollbar',
|
|
806
|
+
providers: [provideScrollAreaScrollbarContext(scrollbarContext)],
|
|
807
|
+
host: {
|
|
808
|
+
'[attr.data-id]': 'rootContext.rootId + "-scrollbar"',
|
|
809
|
+
'[attr.data-orientation]': 'orientation()',
|
|
810
|
+
'[attr.data-hovering]': 'rootContext.hovering() ? "" : undefined',
|
|
811
|
+
'[attr.data-scrolling]': 'scrolling() ? "" : undefined',
|
|
812
|
+
'[attr.data-has-overflow-x]': '!rootContext.hiddenState().x ? "" : undefined',
|
|
813
|
+
'[attr.data-has-overflow-y]': '!rootContext.hiddenState().y ? "" : undefined',
|
|
814
|
+
'[attr.data-overflow-x-start]': 'rootContext.overflowEdges().xStart ? "" : undefined',
|
|
815
|
+
'[attr.data-overflow-x-end]': 'rootContext.overflowEdges().xEnd ? "" : undefined',
|
|
816
|
+
'[attr.data-overflow-y-start]': 'rootContext.overflowEdges().yStart ? "" : undefined',
|
|
817
|
+
'[attr.data-overflow-y-end]': 'rootContext.overflowEdges().yEnd ? "" : undefined',
|
|
818
|
+
'[style.position]': '"absolute"',
|
|
819
|
+
'[style.touch-action]': '"none"',
|
|
820
|
+
'[style.user-select]': '"none"',
|
|
821
|
+
'[style.display]': 'shouldRender() ? null : "none"',
|
|
822
|
+
'[style.visibility]': 'hideTrackUntilMeasured() ? "hidden" : null',
|
|
823
|
+
'[style.top]': 'orientation() === "vertical" ? "0" : null',
|
|
824
|
+
'[style.bottom]': 'orientation() === "vertical" ? "var(--scroll-area-corner-height)" : "0"',
|
|
825
|
+
'[style.inset-inline-start]': 'orientation() === "horizontal" ? "0" : null',
|
|
826
|
+
'[style.inset-inline-end]': 'orientation() === "vertical" ? "0" : "var(--scroll-area-corner-width)"',
|
|
827
|
+
'[style.--scroll-area-thumb-height]': 'orientation() === "vertical" ? rootContext.thumbSize().height + "px" : null',
|
|
828
|
+
'[style.--scroll-area-thumb-width]': 'orientation() === "horizontal" ? rootContext.thumbSize().width + "px" : null',
|
|
829
|
+
'(pointerdown)': 'onPointerDown($event)',
|
|
830
|
+
'(pointerup)': 'rootContext.handlePointerUp($event)',
|
|
831
|
+
'(pointercancel)': 'rootContext.handlePointerUp($event)',
|
|
832
|
+
'(wheel)': 'onWheel($event)'
|
|
833
|
+
}
|
|
834
|
+
}]
|
|
835
|
+
}], ctorParameters: () => [], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], keepMounted: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepMounted", required: false }] }] } });
|
|
836
|
+
|
|
837
|
+
/**
|
|
838
|
+
* The draggable part of the scrollbar that indicates the current scroll position.
|
|
839
|
+
* Renders a `<div>` element.
|
|
840
|
+
*
|
|
841
|
+
* @group Components
|
|
842
|
+
*/
|
|
843
|
+
class RdxScrollAreaThumb {
|
|
844
|
+
constructor() {
|
|
845
|
+
this.rootContext = injectScrollAreaRootContext();
|
|
846
|
+
this.scrollbarContext = injectScrollAreaScrollbarContext();
|
|
847
|
+
this.element = inject(ElementRef).nativeElement;
|
|
848
|
+
effect(() => {
|
|
849
|
+
if (this.scrollbarContext.orientation() === 'vertical') {
|
|
850
|
+
this.rootContext.thumbYRef.current = this.element;
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
this.rootContext.thumbXRef.current = this.element;
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
endDrag(event) {
|
|
858
|
+
if (this.scrollbarContext.orientation() === 'vertical') {
|
|
859
|
+
this.rootContext.setScrollingY(false);
|
|
860
|
+
}
|
|
861
|
+
else {
|
|
862
|
+
this.rootContext.setScrollingX(false);
|
|
863
|
+
}
|
|
864
|
+
this.rootContext.handlePointerUp(event);
|
|
865
|
+
}
|
|
866
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaThumb, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
867
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxScrollAreaThumb, isStandalone: true, selector: "[rdxScrollAreaThumb]", host: { listeners: { "pointerdown": "rootContext.handlePointerDown($event)", "pointermove": "rootContext.handlePointerMove($event)", "pointerup": "endDrag($event)", "pointercancel": "endDrag($event)" }, properties: { "attr.data-orientation": "scrollbarContext.orientation()", "style.visibility": "rootContext.hasMeasuredScrollbar() ? null : \"hidden\"", "style.height": "scrollbarContext.orientation() === \"vertical\" ? \"var(--scroll-area-thumb-height)\" : null", "style.width": "scrollbarContext.orientation() === \"horizontal\" ? \"var(--scroll-area-thumb-width)\" : null" } }, exportAs: ["rdxScrollAreaThumb"], ngImport: i0 }); }
|
|
868
|
+
}
|
|
869
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaThumb, decorators: [{
|
|
870
|
+
type: Directive,
|
|
871
|
+
args: [{
|
|
872
|
+
selector: '[rdxScrollAreaThumb]',
|
|
873
|
+
exportAs: 'rdxScrollAreaThumb',
|
|
874
|
+
host: {
|
|
875
|
+
'[attr.data-orientation]': 'scrollbarContext.orientation()',
|
|
876
|
+
'[style.visibility]': 'rootContext.hasMeasuredScrollbar() ? null : "hidden"',
|
|
877
|
+
'[style.height]': 'scrollbarContext.orientation() === "vertical" ? "var(--scroll-area-thumb-height)" : null',
|
|
878
|
+
'[style.width]': 'scrollbarContext.orientation() === "horizontal" ? "var(--scroll-area-thumb-width)" : null',
|
|
879
|
+
'(pointerdown)': 'rootContext.handlePointerDown($event)',
|
|
880
|
+
'(pointermove)': 'rootContext.handlePointerMove($event)',
|
|
881
|
+
'(pointerup)': 'endDrag($event)',
|
|
882
|
+
'(pointercancel)': 'endDrag($event)'
|
|
883
|
+
}
|
|
884
|
+
}]
|
|
885
|
+
}], ctorParameters: () => [] });
|
|
886
|
+
|
|
887
|
+
const _imports = [
|
|
888
|
+
RdxScrollAreaRoot,
|
|
889
|
+
RdxScrollAreaViewport,
|
|
890
|
+
RdxScrollAreaContent,
|
|
891
|
+
RdxScrollAreaScrollbar,
|
|
892
|
+
RdxScrollAreaThumb,
|
|
893
|
+
RdxScrollAreaCorner
|
|
894
|
+
];
|
|
895
|
+
class RdxScrollAreaModule {
|
|
896
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
897
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaModule, imports: [RdxScrollAreaRoot,
|
|
898
|
+
RdxScrollAreaViewport,
|
|
899
|
+
RdxScrollAreaContent,
|
|
900
|
+
RdxScrollAreaScrollbar,
|
|
901
|
+
RdxScrollAreaThumb,
|
|
902
|
+
RdxScrollAreaCorner], exports: [RdxScrollAreaRoot,
|
|
903
|
+
RdxScrollAreaViewport,
|
|
904
|
+
RdxScrollAreaContent,
|
|
905
|
+
RdxScrollAreaScrollbar,
|
|
906
|
+
RdxScrollAreaThumb,
|
|
907
|
+
RdxScrollAreaCorner] }); }
|
|
908
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaModule }); }
|
|
909
|
+
}
|
|
910
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxScrollAreaModule, decorators: [{
|
|
911
|
+
type: NgModule,
|
|
912
|
+
args: [{
|
|
913
|
+
imports: [..._imports],
|
|
914
|
+
exports: [..._imports]
|
|
915
|
+
}]
|
|
916
|
+
}] });
|
|
917
|
+
|
|
918
|
+
/**
|
|
919
|
+
* Generated bundle index. Do not edit.
|
|
920
|
+
*/
|
|
921
|
+
|
|
922
|
+
export { RdxScrollAreaContent, RdxScrollAreaCorner, RdxScrollAreaModule, RdxScrollAreaRoot, RdxScrollAreaScrollbar, RdxScrollAreaThumb, RdxScrollAreaViewport, injectScrollAreaRootContext, injectScrollAreaScrollbarContext, injectScrollAreaViewportContext, provideScrollAreaRootContext, provideScrollAreaScrollbarContext, provideScrollAreaViewportContext };
|
|
923
|
+
//# sourceMappingURL=radix-ng-primitives-scroll-area.mjs.map
|