@eric-emg/symphiq-components 1.2.507 → 1.2.508
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/symphiq-components.mjs +118 -42
- package/fesm2022/symphiq-components.mjs.map +1 -1
- package/index.d.ts +8 -3
- package/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1906,11 +1906,13 @@ class MetricFormatterService {
|
|
|
1906
1906
|
|
|
1907
1907
|
class HeaderScrollService {
|
|
1908
1908
|
constructor() {
|
|
1909
|
-
this.DEFAULT_COLLAPSE_THRESHOLD =
|
|
1910
|
-
this.DEFAULT_EXPAND_THRESHOLD =
|
|
1911
|
-
this.DEFAULT_STATE_CHANGE_COOLDOWN =
|
|
1912
|
-
this.DEFAULT_DEBOUNCE_DELAY =
|
|
1909
|
+
this.DEFAULT_COLLAPSE_THRESHOLD = 180;
|
|
1910
|
+
this.DEFAULT_EXPAND_THRESHOLD = 5;
|
|
1911
|
+
this.DEFAULT_STATE_CHANGE_COOLDOWN = 500;
|
|
1912
|
+
this.DEFAULT_DEBOUNCE_DELAY = 250;
|
|
1913
1913
|
this.HEADER_HEIGHT_DIFF = 48;
|
|
1914
|
+
this.STABILITY_THRESHOLD = 8;
|
|
1915
|
+
this.DEBUG = true;
|
|
1914
1916
|
this.isScrolled = signal(false, ...(ngDevMode ? [{ debugName: "isScrolled" }] : []));
|
|
1915
1917
|
this.scrollProgress = signal(0, ...(ngDevMode ? [{ debugName: "scrollProgress" }] : []));
|
|
1916
1918
|
this.scrollTimeout = null;
|
|
@@ -1918,7 +1920,9 @@ class HeaderScrollService {
|
|
|
1918
1920
|
this.isProgrammaticScroll = false;
|
|
1919
1921
|
this.lastScrollPosition = 0;
|
|
1920
1922
|
this.scrollStabilityCount = 0;
|
|
1921
|
-
this.
|
|
1923
|
+
this.scrollDirection = 'none';
|
|
1924
|
+
this.consecutiveDirectionCount = 0;
|
|
1925
|
+
this.stateChangeCount = 0;
|
|
1922
1926
|
this.config = {
|
|
1923
1927
|
collapseThreshold: this.DEFAULT_COLLAPSE_THRESHOLD,
|
|
1924
1928
|
expandThreshold: this.DEFAULT_EXPAND_THRESHOLD,
|
|
@@ -1926,86 +1930,158 @@ class HeaderScrollService {
|
|
|
1926
1930
|
debounceDelay: this.DEFAULT_DEBOUNCE_DELAY
|
|
1927
1931
|
};
|
|
1928
1932
|
}
|
|
1933
|
+
log(message, data) {
|
|
1934
|
+
if (this.DEBUG) {
|
|
1935
|
+
const timestamp = new Date().toISOString().split('T')[1];
|
|
1936
|
+
console.log(`[HeaderScroll ${timestamp}] ${message}`, data ?? '');
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1929
1939
|
configure(config) {
|
|
1930
1940
|
this.config = {
|
|
1931
1941
|
...this.config,
|
|
1932
1942
|
...config
|
|
1933
1943
|
};
|
|
1944
|
+
this.log('Configured', { config: this.config });
|
|
1934
1945
|
}
|
|
1935
1946
|
handleScroll(embedded = false) {
|
|
1936
1947
|
if (embedded) {
|
|
1937
1948
|
return;
|
|
1938
1949
|
}
|
|
1939
1950
|
const scrollPosition = window.scrollY;
|
|
1951
|
+
const now = Date.now();
|
|
1940
1952
|
const windowHeight = window.innerHeight;
|
|
1941
1953
|
const documentHeight = document.documentElement.scrollHeight;
|
|
1942
1954
|
const maxScroll = documentHeight - windowHeight;
|
|
1943
1955
|
const progress = maxScroll > 0 ? (scrollPosition / maxScroll) * 100 : 0;
|
|
1944
1956
|
this.scrollProgress.set(Math.min(100, Math.max(0, progress)));
|
|
1945
1957
|
if (this.isProgrammaticScroll) {
|
|
1958
|
+
this.log('Ignored: programmatic scroll', { scrollPosition });
|
|
1946
1959
|
return;
|
|
1947
1960
|
}
|
|
1948
|
-
const now = Date.now();
|
|
1949
1961
|
const timeSinceLastChange = now - this.lastStateChangeTime;
|
|
1950
1962
|
if (timeSinceLastChange < this.config.stateChangeCooldown) {
|
|
1963
|
+
this.log('Ignored: cooldown active', {
|
|
1964
|
+
timeSinceLastChange,
|
|
1965
|
+
cooldown: this.config.stateChangeCooldown,
|
|
1966
|
+
scrollPosition
|
|
1967
|
+
});
|
|
1951
1968
|
return;
|
|
1952
1969
|
}
|
|
1953
|
-
|
|
1954
|
-
|
|
1970
|
+
const scrollDelta = scrollPosition - this.lastScrollPosition;
|
|
1971
|
+
const absScrollDelta = Math.abs(scrollDelta);
|
|
1972
|
+
const newDirection = scrollDelta > 2 ? 'down' : scrollDelta < -2 ? 'up' : 'none';
|
|
1973
|
+
if (newDirection !== 'none') {
|
|
1974
|
+
if (newDirection === this.scrollDirection) {
|
|
1975
|
+
this.consecutiveDirectionCount++;
|
|
1976
|
+
}
|
|
1977
|
+
else {
|
|
1978
|
+
this.consecutiveDirectionCount = 1;
|
|
1979
|
+
this.scrollDirection = newDirection;
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
if (absScrollDelta < 3) {
|
|
1983
|
+
this.scrollStabilityCount++;
|
|
1984
|
+
}
|
|
1985
|
+
else {
|
|
1986
|
+
this.scrollStabilityCount = 0;
|
|
1955
1987
|
}
|
|
1988
|
+
this.lastScrollPosition = scrollPosition;
|
|
1956
1989
|
const currentState = this.isScrolled();
|
|
1957
|
-
let
|
|
1990
|
+
let desiredState = currentState;
|
|
1958
1991
|
if (!currentState && scrollPosition > this.config.collapseThreshold) {
|
|
1959
|
-
|
|
1992
|
+
desiredState = true;
|
|
1960
1993
|
}
|
|
1961
1994
|
else if (currentState && scrollPosition < this.config.expandThreshold) {
|
|
1962
|
-
|
|
1995
|
+
desiredState = false;
|
|
1963
1996
|
}
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
if (scrollDelta < 5) {
|
|
1967
|
-
this.scrollStabilityCount++;
|
|
1997
|
+
if (currentState === desiredState) {
|
|
1998
|
+
return;
|
|
1968
1999
|
}
|
|
1969
|
-
|
|
1970
|
-
|
|
2000
|
+
const isStable = this.scrollStabilityCount >= this.STABILITY_THRESHOLD;
|
|
2001
|
+
const hasConsistentDirection = this.consecutiveDirectionCount >= 3;
|
|
2002
|
+
const directionMatchesIntent = (desiredState && this.scrollDirection === 'down') ||
|
|
2003
|
+
(!desiredState && this.scrollDirection === 'up');
|
|
2004
|
+
this.log('State change candidate', {
|
|
2005
|
+
scrollPosition,
|
|
2006
|
+
currentState: currentState ? 'collapsed' : 'expanded',
|
|
2007
|
+
desiredState: desiredState ? 'collapsed' : 'expanded',
|
|
2008
|
+
stability: this.scrollStabilityCount,
|
|
2009
|
+
direction: this.scrollDirection,
|
|
2010
|
+
directionCount: this.consecutiveDirectionCount,
|
|
2011
|
+
isStable,
|
|
2012
|
+
hasConsistentDirection,
|
|
2013
|
+
directionMatchesIntent
|
|
2014
|
+
});
|
|
2015
|
+
if (!isStable && !hasConsistentDirection) {
|
|
2016
|
+
this.log('Blocked: insufficient stability or direction consistency');
|
|
2017
|
+
return;
|
|
1971
2018
|
}
|
|
1972
|
-
if (
|
|
1973
|
-
this.
|
|
1974
|
-
|
|
1975
|
-
const finalCurrentState = this.isScrolled();
|
|
1976
|
-
let finalState = finalCurrentState;
|
|
1977
|
-
if (!finalCurrentState && finalPosition > this.config.collapseThreshold) {
|
|
1978
|
-
finalState = true;
|
|
1979
|
-
}
|
|
1980
|
-
else if (finalCurrentState && finalPosition < this.config.expandThreshold) {
|
|
1981
|
-
finalState = false;
|
|
1982
|
-
}
|
|
1983
|
-
if (this.isScrolled() !== finalState) {
|
|
1984
|
-
this.isProgrammaticScroll = true;
|
|
1985
|
-
this.isScrolled.set(finalState);
|
|
1986
|
-
this.lastStateChangeTime = Date.now();
|
|
1987
|
-
this.scrollStabilityCount = 0;
|
|
1988
|
-
if (finalState) {
|
|
1989
|
-
window.scrollBy(0, this.HEADER_HEIGHT_DIFF);
|
|
1990
|
-
}
|
|
1991
|
-
setTimeout(() => {
|
|
1992
|
-
this.isProgrammaticScroll = false;
|
|
1993
|
-
this.lastScrollPosition = window.scrollY;
|
|
1994
|
-
}, 350);
|
|
1995
|
-
}
|
|
1996
|
-
}, this.config.debounceDelay);
|
|
2019
|
+
if (!directionMatchesIntent && !isStable) {
|
|
2020
|
+
this.log('Blocked: direction does not match intent and not stable');
|
|
2021
|
+
return;
|
|
1997
2022
|
}
|
|
2023
|
+
if (this.scrollTimeout) {
|
|
2024
|
+
clearTimeout(this.scrollTimeout);
|
|
2025
|
+
}
|
|
2026
|
+
this.scrollTimeout = setTimeout(() => {
|
|
2027
|
+
const finalPosition = window.scrollY;
|
|
2028
|
+
const finalCurrentState = this.isScrolled();
|
|
2029
|
+
let finalDesiredState = finalCurrentState;
|
|
2030
|
+
if (!finalCurrentState && finalPosition > this.config.collapseThreshold) {
|
|
2031
|
+
finalDesiredState = true;
|
|
2032
|
+
}
|
|
2033
|
+
else if (finalCurrentState && finalPosition < this.config.expandThreshold) {
|
|
2034
|
+
finalDesiredState = false;
|
|
2035
|
+
}
|
|
2036
|
+
if (finalCurrentState === finalDesiredState) {
|
|
2037
|
+
this.log('Debounce check: no change needed', { finalPosition, finalCurrentState });
|
|
2038
|
+
return;
|
|
2039
|
+
}
|
|
2040
|
+
this.stateChangeCount++;
|
|
2041
|
+
this.log('STATE CHANGE EXECUTING', {
|
|
2042
|
+
stateChangeNumber: this.stateChangeCount,
|
|
2043
|
+
from: finalCurrentState ? 'collapsed' : 'expanded',
|
|
2044
|
+
to: finalDesiredState ? 'collapsed' : 'expanded',
|
|
2045
|
+
finalPosition,
|
|
2046
|
+
willCompensate: finalDesiredState
|
|
2047
|
+
});
|
|
2048
|
+
this.isProgrammaticScroll = true;
|
|
2049
|
+
this.isScrolled.set(finalDesiredState);
|
|
2050
|
+
this.lastStateChangeTime = Date.now();
|
|
2051
|
+
this.scrollStabilityCount = 0;
|
|
2052
|
+
this.consecutiveDirectionCount = 0;
|
|
2053
|
+
if (finalDesiredState) {
|
|
2054
|
+
const beforeScroll = window.scrollY;
|
|
2055
|
+
window.scrollBy(0, this.HEADER_HEIGHT_DIFF);
|
|
2056
|
+
this.log('Scroll compensation applied', {
|
|
2057
|
+
before: beforeScroll,
|
|
2058
|
+
after: window.scrollY,
|
|
2059
|
+
compensation: this.HEADER_HEIGHT_DIFF
|
|
2060
|
+
});
|
|
2061
|
+
}
|
|
2062
|
+
setTimeout(() => {
|
|
2063
|
+
this.isProgrammaticScroll = false;
|
|
2064
|
+
this.lastScrollPosition = window.scrollY;
|
|
2065
|
+
this.log('Programmatic scroll lock released', { scrollPosition: window.scrollY });
|
|
2066
|
+
}, 400);
|
|
2067
|
+
}, this.config.debounceDelay);
|
|
1998
2068
|
}
|
|
1999
2069
|
resetHeaderState() {
|
|
2070
|
+
this.log('resetHeaderState called');
|
|
2000
2071
|
this.isProgrammaticScroll = true;
|
|
2001
2072
|
setTimeout(() => {
|
|
2002
2073
|
this.isProgrammaticScroll = false;
|
|
2003
2074
|
this.isScrolled.set(false);
|
|
2075
|
+
this.log('resetHeaderState complete');
|
|
2004
2076
|
}, 800);
|
|
2005
2077
|
}
|
|
2006
2078
|
resetState() {
|
|
2079
|
+
this.log('resetState called');
|
|
2007
2080
|
this.isScrolled.set(false);
|
|
2008
2081
|
this.scrollProgress.set(0);
|
|
2082
|
+
this.scrollStabilityCount = 0;
|
|
2083
|
+
this.consecutiveDirectionCount = 0;
|
|
2084
|
+
this.scrollDirection = 'none';
|
|
2009
2085
|
if (this.scrollTimeout) {
|
|
2010
2086
|
clearTimeout(this.scrollTimeout);
|
|
2011
2087
|
this.scrollTimeout = null;
|