@searchspring/snap-preact-components 0.70.1 → 0.72.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.
@@ -29,7 +29,7 @@ var CSS = {
29
29
  justifyContent: 'center',
30
30
  height: 'auto',
31
31
  '& img': {
32
- visibility: visibility,
32
+ visibility: visibility === 'hidden' ? 'hidden' : undefined,
33
33
  flexShrink: '0',
34
34
  objectFit: 'contain',
35
35
  maxWidth: '100%',
@@ -12,6 +12,7 @@ export interface FacetSliderProps extends ComponentProps {
12
12
  tickSize?: number;
13
13
  tickTextColor?: string;
14
14
  stickyHandleLabel?: boolean;
15
+ separateHandles?: boolean;
15
16
  facet: RangeFacet;
16
17
  onChange?: (values: number[]) => void;
17
18
  onDrag?: (values: number[]) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"FacetSlider.d.ts","sourceRoot":"","sources":["../../../../../src/components/Molecules/FacetSlider/FacetSlider.tsx"],"names":[],"mappings":";AAUA,OAAO,EAAE,cAAc,EAAc,MAAM,gBAAgB,CAAC;AAE5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAyIhE,eAAO,MAAM,WAAW,eAAyB,gBAAgB,KAAG,WAyJlE,CAAC;AAEH,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACpC"}
1
+ {"version":3,"file":"FacetSlider.d.ts","sourceRoot":"","sources":["../../../../../src/components/Molecules/FacetSlider/FacetSlider.tsx"],"names":[],"mappings":";AAUA,OAAO,EAAE,cAAc,EAAc,MAAM,gBAAgB,CAAC;AAE5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAyIhE,eAAO,MAAM,WAAW,eAAyB,gBAAgB,KAAG,WA4LlE,CAAC;AAEH,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACpC"}
@@ -150,7 +150,7 @@ exports.FacetSlider = (0, mobx_react_1.observer)(function (properties) {
150
150
  var props = __assign(__assign(__assign({
151
151
  // default props
152
152
  tickSize: ((_a = properties.facet) === null || _a === void 0 ? void 0 : _a.step) ? ((_b = properties.facet) === null || _b === void 0 ? void 0 : _b.step) * 10 : 20 }, (_c = globalTheme === null || globalTheme === void 0 ? void 0 : globalTheme.components) === null || _c === void 0 ? void 0 : _c.facetSlider), properties), (_e = (_d = properties.theme) === null || _d === void 0 ? void 0 : _d.components) === null || _e === void 0 ? void 0 : _e.facetSlider);
153
- var tickTextColor = props.tickTextColor, trackColor = props.trackColor, valueTextColor = props.valueTextColor, railColor = props.railColor, handleColor = props.handleColor, handleDraggingColor = props.handleDraggingColor, showTicks = props.showTicks, facet = props.facet, stickyHandleLabel = props.stickyHandleLabel, onChange = props.onChange, onDrag = props.onDrag, disableStyles = props.disableStyles, className = props.className, style = props.style;
153
+ var tickTextColor = props.tickTextColor, trackColor = props.trackColor, valueTextColor = props.valueTextColor, railColor = props.railColor, handleColor = props.handleColor, handleDraggingColor = props.handleDraggingColor, showTicks = props.showTicks, facet = props.facet, stickyHandleLabel = props.stickyHandleLabel, separateHandles = props.separateHandles, onChange = props.onChange, onDrag = props.onDrag, disableStyles = props.disableStyles, className = props.className, style = props.style;
154
154
  var tickSize = props.tickSize;
155
155
  if (isNaN(Number(tickSize)) || Number(tickSize) <= 0) {
156
156
  // fallback to default (causes chrome to crash)
@@ -161,6 +161,34 @@ exports.FacetSlider = (0, mobx_react_1.observer)(function (properties) {
161
161
  }
162
162
  var _y = (0, hooks_1.useState)([(_h = facet.active) === null || _h === void 0 ? void 0 : _h.low, (_j = facet.active) === null || _j === void 0 ? void 0 : _j.high]), values = _y[0], setValues = _y[1];
163
163
  var _z = (0, hooks_1.useState)([(_k = facet.active) === null || _k === void 0 ? void 0 : _k.low, (_l = facet.active) === null || _l === void 0 ? void 0 : _l.high]), active = _z[0], setActive = _z[1];
164
+ // Helper function to ensure min and max values are not equal
165
+ var ensureValueSeparation = function (val) {
166
+ var _a, _b;
167
+ if (!separateHandles || !facet.step) {
168
+ return val;
169
+ }
170
+ var minVal = val[0], maxVal = val[1];
171
+ var rangeMin = (_a = facet.range) === null || _a === void 0 ? void 0 : _a.low;
172
+ var rangeMax = (_b = facet.range) === null || _b === void 0 ? void 0 : _b.high;
173
+ var step = facet.step;
174
+ // If values are equal, separate them by one step
175
+ if (minVal === maxVal) {
176
+ // Determine which value to adjust based on range boundaries
177
+ if (maxVal + step <= rangeMax) {
178
+ // Prefer to increase max if possible
179
+ return [minVal, maxVal + step];
180
+ }
181
+ else if (minVal - step >= rangeMin) {
182
+ // Otherwise, decrease min if possible
183
+ return [minVal - step, maxVal];
184
+ }
185
+ else {
186
+ // If neither is possible (range too small), return original
187
+ return val;
188
+ }
189
+ }
190
+ return val;
191
+ };
164
192
  if (((((_m = facet.active) === null || _m === void 0 ? void 0 : _m.low) || ((_o = facet.active) === null || _o === void 0 ? void 0 : _o.low) === 0) && ((_p = facet.active) === null || _p === void 0 ? void 0 : _p.high) && values[0] != ((_q = facet.active) === null || _q === void 0 ? void 0 : _q.low)) || values[1] != ((_r = facet.active) === null || _r === void 0 ? void 0 : _r.high)) {
165
193
  setActive([(_s = facet.active) === null || _s === void 0 ? void 0 : _s.low, (_t = facet.active) === null || _t === void 0 ? void 0 : _t.high]);
166
194
  setValues([(_u = facet.active) === null || _u === void 0 ? void 0 : _u.low, (_v = facet.active) === null || _v === void 0 ? void 0 : _v.high]);
@@ -169,20 +197,23 @@ exports.FacetSlider = (0, mobx_react_1.observer)(function (properties) {
169
197
  values: active,
170
198
  onChange: function (val) {
171
199
  var _a;
172
- setActive(val);
200
+ var adjustedVal = ensureValueSeparation(val);
201
+ setActive(adjustedVal);
202
+ // onChange called prior to urlManager changes to allow for mutation in function
203
+ onChange && onChange(adjustedVal);
173
204
  if ((_a = facet === null || facet === void 0 ? void 0 : facet.services) === null || _a === void 0 ? void 0 : _a.urlManager) {
174
- if (val[0] == facet.range.low && val[1] == facet.range.high) {
205
+ if (adjustedVal[0] == facet.range.low && adjustedVal[1] == facet.range.high) {
175
206
  facet.services.urlManager.remove('page').remove("filter.".concat(facet.field)).go();
176
207
  }
177
208
  else {
178
- facet.services.urlManager.remove('page').set("filter.".concat(facet.field), { low: val[0], high: val[1] }).go();
209
+ facet.services.urlManager.remove('page').set("filter.".concat(facet.field), { low: adjustedVal[0], high: adjustedVal[1] }).go();
179
210
  }
180
211
  }
181
- onChange && onChange(val);
182
212
  },
183
213
  onDrag: function (val) {
184
- setActive(val);
185
- onDrag && onDrag(val);
214
+ var adjustedVal = ensureValueSeparation(val);
215
+ setActive(adjustedVal);
216
+ onDrag && onDrag(adjustedVal);
186
217
  },
187
218
  min: (_w = facet.range) === null || _w === void 0 ? void 0 : _w.low,
188
219
  max: (_x = facet.range) === null || _x === void 0 ? void 0 : _x.high,
@@ -1 +1 @@
1
- {"version":3,"file":"useIntersectionAdvanced.d.ts","sourceRoot":"","sources":["../../../src/hooks/useIntersectionAdvanced.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,UAAU,EAAE,MAAM,cAAc,CAAC;AAEvE,MAAM,WAAW,sBAAsB;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,uBAAuB,QAAS,WAAW,WAAW,GAAG,IAAI,CAAC,YAAW,sBAAsB,KAAQ,OA+FnH,CAAC"}
1
+ {"version":3,"file":"useIntersectionAdvanced.d.ts","sourceRoot":"","sources":["../../../src/hooks/useIntersectionAdvanced.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,UAAU,EAAE,MAAM,cAAc,CAAC;AAEvE,MAAM,WAAW,sBAAsB;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAGD,eAAO,MAAM,uBAAuB,QAAS,WAAW,WAAW,GAAG,IAAI,CAAC,YAAW,sBAAsB,KAAQ,OAqInH,CAAC"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useIntersectionAdvanced = void 0;
4
4
  var hooks_1 = require("preact/hooks");
5
+ var VISIBILITY_POLL_INTERVAL = 250;
5
6
  var useIntersectionAdvanced = function (ref, options) {
6
7
  if (options === void 0) { options = {}; }
7
8
  var _a = options.rootMargin, rootMargin = _a === void 0 ? '0px' : _a, _b = options.fireOnce, fireOnce = _b === void 0 ? false : _b, _c = options.threshold, threshold = _c === void 0 ? 0 : _c, _d = options.minVisibleTime, minVisibleTime = _d === void 0 ? 0 : _d, resetKey = options.resetKey;
@@ -26,44 +27,78 @@ var useIntersectionAdvanced = function (ref, options) {
26
27
  (0, hooks_1.useEffect)(function () {
27
28
  setIntersecting(false);
28
29
  var observer = null;
30
+ var pollInterval = null;
29
31
  if (!ref.current)
30
32
  return;
33
+ var clearPoll = function () {
34
+ if (pollInterval) {
35
+ window.clearInterval(pollInterval);
36
+ pollInterval = null;
37
+ }
38
+ };
39
+ var handleVisible = function () {
40
+ // Start tracking visibility time if minVisibleTime is set
41
+ if (minVisibleTime > 0) {
42
+ visibleStartRef.current = Date.now();
43
+ // Clear any existing timer
44
+ if (visibleTimerRef.current) {
45
+ window.clearTimeout(visibleTimerRef.current);
46
+ }
47
+ // Set up a timer for the minimum visibility duration
48
+ visibleTimerRef.current = window.setTimeout(function () {
49
+ setIntersecting(true);
50
+ if (fireOnce && ref.current && observer) {
51
+ observer.unobserve(ref.current);
52
+ }
53
+ }, minVisibleTime);
54
+ }
55
+ else {
56
+ // If no minimum time required, update state immediately
57
+ setIntersecting(true);
58
+ if (fireOnce && ref.current && observer) {
59
+ observer.unobserve(ref.current);
60
+ }
61
+ }
62
+ };
63
+ var handleHidden = function () {
64
+ // Element is no longer visible
65
+ if (visibleTimerRef.current) {
66
+ // Clear the timer if element goes out of view before minimum time
67
+ window.clearTimeout(visibleTimerRef.current);
68
+ }
69
+ visibleTimerRef.current = null;
70
+ visibleStartRef.current = null;
71
+ setIntersecting(false);
72
+ };
31
73
  observer = new IntersectionObserver(function (_a) {
32
74
  var entry = _a[0];
33
- // If element becomes visible
34
75
  if (entry.isIntersecting) {
35
- // Start tracking visibility time if minVisibleTime is set
36
- if (minVisibleTime > 0) {
37
- visibleStartRef.current = Date.now();
38
- // Clear any existing timer
39
- if (visibleTimerRef.current) {
40
- window.clearTimeout(visibleTimerRef.current);
41
- }
42
- // Set up a timer for the minimum visibility duration
43
- visibleTimerRef.current = window.setTimeout(function () {
44
- setIntersecting(true);
45
- if (fireOnce && ref.current && observer) {
46
- observer.unobserve(ref.current);
47
- }
48
- }, minVisibleTime);
76
+ if (ref.current && elementIsVisible(ref.current)) {
77
+ clearPoll();
78
+ handleVisible();
49
79
  }
50
80
  else {
51
- // If no minimum time required, update state immediately
52
- setIntersecting(true);
53
- if (fireOnce && ref.current && observer) {
54
- observer.unobserve(ref.current);
81
+ // Element is intersecting but not visible (e.g. opacity 0)
82
+ // Treat as hidden but start polling for visibility
83
+ handleHidden();
84
+ if (!pollInterval) {
85
+ pollInterval = window.setInterval(function () {
86
+ if (!ref.current) {
87
+ clearPoll();
88
+ return;
89
+ }
90
+ if (elementIsVisible(ref.current)) {
91
+ clearPoll();
92
+ handleVisible();
93
+ }
94
+ }, VISIBILITY_POLL_INTERVAL);
55
95
  }
56
96
  }
57
97
  }
58
98
  else {
59
- // Element is no longer visible
60
- if (visibleTimerRef.current) {
61
- // Clear the timer if element goes out of view before minimum time
62
- window.clearTimeout(visibleTimerRef.current);
63
- }
64
- visibleTimerRef.current = null;
65
- visibleStartRef.current = null;
66
- setIntersecting(false);
99
+ // Element is no longer intersecting
100
+ clearPoll();
101
+ handleHidden();
67
102
  }
68
103
  }, {
69
104
  rootMargin: rootMargin,
@@ -74,6 +109,7 @@ var useIntersectionAdvanced = function (ref, options) {
74
109
  }
75
110
  return function () {
76
111
  setIntersecting(false);
112
+ clearPoll();
77
113
  if (visibleTimerRef.current) {
78
114
  window.clearTimeout(visibleTimerRef.current);
79
115
  }
@@ -86,3 +122,9 @@ var useIntersectionAdvanced = function (ref, options) {
86
122
  return isIntersecting;
87
123
  };
88
124
  exports.useIntersectionAdvanced = useIntersectionAdvanced;
125
+ function elementIsVisible(el) {
126
+ if (el && 'checkVisibility' in el) {
127
+ return el.checkVisibility({ contentVisibilityAuto: true, opacityProperty: true, visibilityProperty: true });
128
+ }
129
+ return true;
130
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"withTracking.d.ts","sourceRoot":"","sources":["../../../src/providers/withTracking.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAK,aAAa,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAIxH,UAAU,iBAAiB;IAC1B,UAAU,CAAC,EAAE,gBAAgB,GAAG,sBAAsB,GAAG,wBAAwB,CAAC;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACnB;AAED,wBAAgB,YAAY,CAAC,KAAK,SAAS,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,CAAC,KAAK,CAAC,4BA8DnG"}
1
+ {"version":3,"file":"withTracking.d.ts","sourceRoot":"","sources":["../../../src/providers/withTracking.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAK,aAAa,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAIxH,UAAU,iBAAiB;IAC1B,UAAU,CAAC,EAAE,gBAAgB,GAAG,sBAAsB,GAAG,wBAAwB,CAAC;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACnB;AAED,wBAAgB,YAAY,CAAC,KAAK,SAAS,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,CAAC,KAAK,CAAC,4BAqEnG"}
@@ -65,15 +65,19 @@ function withTracking(WrappedComponent) {
65
65
  // track banner in future
66
66
  }
67
67
  }
68
- var currentRef = ref.current;
69
- if (currentRef) {
70
- var handleClick = (0, hooks_1.useCallback)(function (e) {
71
- controller === null || controller === void 0 ? void 0 : controller.track.product.click(e, result);
72
- }, []);
73
- currentRef.setAttribute('sstracking', 'true');
74
- currentRef.removeEventListener('click', handleClick);
75
- currentRef.addEventListener('click', handleClick);
76
- }
68
+ var handleClick = (0, hooks_1.useCallback)(function (e) {
69
+ controller === null || controller === void 0 ? void 0 : controller.track.product.click(e, result);
70
+ }, [controller, result]);
71
+ (0, hooks_1.useEffect)(function () {
72
+ var currentRef = ref.current;
73
+ if (currentRef) {
74
+ currentRef.setAttribute('sstracking', 'true');
75
+ currentRef.addEventListener('click', handleClick, true); // Use capture phase
76
+ return function () {
77
+ currentRef.removeEventListener('click', handleClick, true);
78
+ };
79
+ }
80
+ }, [ref, handleClick]);
77
81
  var trackingProps = __assign(__assign({}, restProps), { controller: controller, result: result, trackingRef: ref });
78
82
  return (0, preact_1.h)(WrappedComponent, __assign({}, trackingProps));
79
83
  };
@@ -10,7 +10,7 @@ const CSS = {
10
10
  justifyContent: 'center',
11
11
  height: 'auto',
12
12
  '& img': {
13
- visibility: visibility,
13
+ visibility: visibility === 'hidden' ? 'hidden' : undefined,
14
14
  flexShrink: '0',
15
15
  objectFit: 'contain',
16
16
  maxWidth: '100%',
@@ -12,6 +12,7 @@ export interface FacetSliderProps extends ComponentProps {
12
12
  tickSize?: number;
13
13
  tickTextColor?: string;
14
14
  stickyHandleLabel?: boolean;
15
+ separateHandles?: boolean;
15
16
  facet: RangeFacet;
16
17
  onChange?: (values: number[]) => void;
17
18
  onDrag?: (values: number[]) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"FacetSlider.d.ts","sourceRoot":"","sources":["../../../../../src/components/Molecules/FacetSlider/FacetSlider.tsx"],"names":[],"mappings":";AAUA,OAAO,EAAE,cAAc,EAAc,MAAM,gBAAgB,CAAC;AAE5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAyIhE,eAAO,MAAM,WAAW,eAAyB,gBAAgB,KAAG,WAyJlE,CAAC;AAEH,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACpC"}
1
+ {"version":3,"file":"FacetSlider.d.ts","sourceRoot":"","sources":["../../../../../src/components/Molecules/FacetSlider/FacetSlider.tsx"],"names":[],"mappings":";AAUA,OAAO,EAAE,cAAc,EAAc,MAAM,gBAAgB,CAAC;AAE5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAyIhE,eAAO,MAAM,WAAW,eAAyB,gBAAgB,KAAG,WA4LlE,CAAC;AAEH,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACpC"}
@@ -134,7 +134,7 @@ export const FacetSlider = observer((properties) => {
134
134
  ...properties,
135
135
  ...properties.theme?.components?.facetSlider,
136
136
  };
137
- const { tickTextColor, trackColor, valueTextColor, railColor, handleColor, handleDraggingColor, showTicks, facet, stickyHandleLabel, onChange, onDrag, disableStyles, className, style, } = props;
137
+ const { tickTextColor, trackColor, valueTextColor, railColor, handleColor, handleDraggingColor, showTicks, facet, stickyHandleLabel, separateHandles, onChange, onDrag, disableStyles, className, style, } = props;
138
138
  let { tickSize } = props;
139
139
  if (isNaN(Number(tickSize)) || Number(tickSize) <= 0) {
140
140
  // fallback to default (causes chrome to crash)
@@ -145,6 +145,33 @@ export const FacetSlider = observer((properties) => {
145
145
  }
146
146
  const [values, setValues] = useState([facet.active?.low, facet.active?.high]);
147
147
  const [active, setActive] = useState([facet.active?.low, facet.active?.high]);
148
+ // Helper function to ensure min and max values are not equal
149
+ const ensureValueSeparation = (val) => {
150
+ if (!separateHandles || !facet.step) {
151
+ return val;
152
+ }
153
+ const [minVal, maxVal] = val;
154
+ const rangeMin = facet.range?.low;
155
+ const rangeMax = facet.range?.high;
156
+ const step = facet.step;
157
+ // If values are equal, separate them by one step
158
+ if (minVal === maxVal) {
159
+ // Determine which value to adjust based on range boundaries
160
+ if (maxVal + step <= rangeMax) {
161
+ // Prefer to increase max if possible
162
+ return [minVal, maxVal + step];
163
+ }
164
+ else if (minVal - step >= rangeMin) {
165
+ // Otherwise, decrease min if possible
166
+ return [minVal - step, maxVal];
167
+ }
168
+ else {
169
+ // If neither is possible (range too small), return original
170
+ return val;
171
+ }
172
+ }
173
+ return val;
174
+ };
148
175
  if (((facet.active?.low || facet.active?.low === 0) && facet.active?.high && values[0] != facet.active?.low) || values[1] != facet.active?.high) {
149
176
  setActive([facet.active?.low, facet.active?.high]);
150
177
  setValues([facet.active?.low, facet.active?.high]);
@@ -152,20 +179,23 @@ export const FacetSlider = observer((properties) => {
152
179
  const { getTrackProps, ticks, segments, handles } = useRanger({
153
180
  values: active,
154
181
  onChange: (val) => {
155
- setActive(val);
182
+ const adjustedVal = ensureValueSeparation(val);
183
+ setActive(adjustedVal);
184
+ // onChange called prior to urlManager changes to allow for mutation in function
185
+ onChange && onChange(adjustedVal);
156
186
  if (facet?.services?.urlManager) {
157
- if (val[0] == facet.range.low && val[1] == facet.range.high) {
187
+ if (adjustedVal[0] == facet.range.low && adjustedVal[1] == facet.range.high) {
158
188
  facet.services.urlManager.remove('page').remove(`filter.${facet.field}`).go();
159
189
  }
160
190
  else {
161
- facet.services.urlManager.remove('page').set(`filter.${facet.field}`, { low: val[0], high: val[1] }).go();
191
+ facet.services.urlManager.remove('page').set(`filter.${facet.field}`, { low: adjustedVal[0], high: adjustedVal[1] }).go();
162
192
  }
163
193
  }
164
- onChange && onChange(val);
165
194
  },
166
195
  onDrag: (val) => {
167
- setActive(val);
168
- onDrag && onDrag(val);
196
+ const adjustedVal = ensureValueSeparation(val);
197
+ setActive(adjustedVal);
198
+ onDrag && onDrag(adjustedVal);
169
199
  },
170
200
  min: facet.range?.low,
171
201
  max: facet.range?.high,
@@ -1 +1 @@
1
- {"version":3,"file":"useIntersectionAdvanced.d.ts","sourceRoot":"","sources":["../../../src/hooks/useIntersectionAdvanced.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,UAAU,EAAE,MAAM,cAAc,CAAC;AAEvE,MAAM,WAAW,sBAAsB;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,uBAAuB,QAAS,WAAW,WAAW,GAAG,IAAI,CAAC,YAAW,sBAAsB,KAAQ,OA+FnH,CAAC"}
1
+ {"version":3,"file":"useIntersectionAdvanced.d.ts","sourceRoot":"","sources":["../../../src/hooks/useIntersectionAdvanced.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,UAAU,EAAE,MAAM,cAAc,CAAC;AAEvE,MAAM,WAAW,sBAAsB;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAGD,eAAO,MAAM,uBAAuB,QAAS,WAAW,WAAW,GAAG,IAAI,CAAC,YAAW,sBAAsB,KAAQ,OAqInH,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { useState, useEffect, useRef } from 'preact/hooks';
2
+ const VISIBILITY_POLL_INTERVAL = 250;
2
3
  export const useIntersectionAdvanced = (ref, options = {}) => {
3
4
  const { rootMargin = '0px', fireOnce = false, threshold = 0, minVisibleTime = 0, resetKey } = options;
4
5
  // State and setter for storing whether element is visible
@@ -22,43 +23,77 @@ export const useIntersectionAdvanced = (ref, options = {}) => {
22
23
  useEffect(() => {
23
24
  setIntersecting(false);
24
25
  let observer = null;
26
+ let pollInterval = null;
25
27
  if (!ref.current)
26
28
  return;
27
- observer = new IntersectionObserver(([entry]) => {
28
- // If element becomes visible
29
- if (entry.isIntersecting) {
30
- // Start tracking visibility time if minVisibleTime is set
31
- if (minVisibleTime > 0) {
32
- visibleStartRef.current = Date.now();
33
- // Clear any existing timer
34
- if (visibleTimerRef.current) {
35
- window.clearTimeout(visibleTimerRef.current);
36
- }
37
- // Set up a timer for the minimum visibility duration
38
- visibleTimerRef.current = window.setTimeout(() => {
39
- setIntersecting(true);
40
- if (fireOnce && ref.current && observer) {
41
- observer.unobserve(ref.current);
42
- }
43
- }, minVisibleTime);
29
+ const clearPoll = () => {
30
+ if (pollInterval) {
31
+ window.clearInterval(pollInterval);
32
+ pollInterval = null;
33
+ }
34
+ };
35
+ const handleVisible = () => {
36
+ // Start tracking visibility time if minVisibleTime is set
37
+ if (minVisibleTime > 0) {
38
+ visibleStartRef.current = Date.now();
39
+ // Clear any existing timer
40
+ if (visibleTimerRef.current) {
41
+ window.clearTimeout(visibleTimerRef.current);
44
42
  }
45
- else {
46
- // If no minimum time required, update state immediately
43
+ // Set up a timer for the minimum visibility duration
44
+ visibleTimerRef.current = window.setTimeout(() => {
47
45
  setIntersecting(true);
48
46
  if (fireOnce && ref.current && observer) {
49
47
  observer.unobserve(ref.current);
50
48
  }
51
- }
49
+ }, minVisibleTime);
52
50
  }
53
51
  else {
54
- // Element is no longer visible
55
- if (visibleTimerRef.current) {
56
- // Clear the timer if element goes out of view before minimum time
57
- window.clearTimeout(visibleTimerRef.current);
52
+ // If no minimum time required, update state immediately
53
+ setIntersecting(true);
54
+ if (fireOnce && ref.current && observer) {
55
+ observer.unobserve(ref.current);
56
+ }
57
+ }
58
+ };
59
+ const handleHidden = () => {
60
+ // Element is no longer visible
61
+ if (visibleTimerRef.current) {
62
+ // Clear the timer if element goes out of view before minimum time
63
+ window.clearTimeout(visibleTimerRef.current);
64
+ }
65
+ visibleTimerRef.current = null;
66
+ visibleStartRef.current = null;
67
+ setIntersecting(false);
68
+ };
69
+ observer = new IntersectionObserver(([entry]) => {
70
+ if (entry.isIntersecting) {
71
+ if (ref.current && elementIsVisible(ref.current)) {
72
+ clearPoll();
73
+ handleVisible();
74
+ }
75
+ else {
76
+ // Element is intersecting but not visible (e.g. opacity 0)
77
+ // Treat as hidden but start polling for visibility
78
+ handleHidden();
79
+ if (!pollInterval) {
80
+ pollInterval = window.setInterval(() => {
81
+ if (!ref.current) {
82
+ clearPoll();
83
+ return;
84
+ }
85
+ if (elementIsVisible(ref.current)) {
86
+ clearPoll();
87
+ handleVisible();
88
+ }
89
+ }, VISIBILITY_POLL_INTERVAL);
90
+ }
58
91
  }
59
- visibleTimerRef.current = null;
60
- visibleStartRef.current = null;
61
- setIntersecting(false);
92
+ }
93
+ else {
94
+ // Element is no longer intersecting
95
+ clearPoll();
96
+ handleHidden();
62
97
  }
63
98
  }, {
64
99
  rootMargin,
@@ -69,6 +104,7 @@ export const useIntersectionAdvanced = (ref, options = {}) => {
69
104
  }
70
105
  return () => {
71
106
  setIntersecting(false);
107
+ clearPoll();
72
108
  if (visibleTimerRef.current) {
73
109
  window.clearTimeout(visibleTimerRef.current);
74
110
  }
@@ -80,3 +116,9 @@ export const useIntersectionAdvanced = (ref, options = {}) => {
80
116
  }, [ref, resetKey]);
81
117
  return isIntersecting;
82
118
  };
119
+ function elementIsVisible(el) {
120
+ if (el && 'checkVisibility' in el) {
121
+ return el.checkVisibility({ contentVisibilityAuto: true, opacityProperty: true, visibilityProperty: true });
122
+ }
123
+ return true;
124
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"withTracking.d.ts","sourceRoot":"","sources":["../../../src/providers/withTracking.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAK,aAAa,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAIxH,UAAU,iBAAiB;IAC1B,UAAU,CAAC,EAAE,gBAAgB,GAAG,sBAAsB,GAAG,wBAAwB,CAAC;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACnB;AAED,wBAAgB,YAAY,CAAC,KAAK,SAAS,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,CAAC,KAAK,CAAC,4BA8DnG"}
1
+ {"version":3,"file":"withTracking.d.ts","sourceRoot":"","sources":["../../../src/providers/withTracking.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAK,aAAa,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAIxH,UAAU,iBAAiB;IAC1B,UAAU,CAAC,EAAE,gBAAgB,GAAG,sBAAsB,GAAG,wBAAwB,CAAC;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACnB;AAED,wBAAgB,YAAY,CAAC,KAAK,SAAS,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,CAAC,KAAK,CAAC,4BAqEnG"}
@@ -1,6 +1,6 @@
1
1
  import { h } from 'preact';
2
2
  import { createImpressionObserver } from '../utilities';
3
- import { useCallback } from 'preact/hooks';
3
+ import { useEffect, useCallback } from 'preact/hooks';
4
4
  export function withTracking(WrappedComponent) {
5
5
  const WithTracking = (props) => {
6
6
  const { controller, result, ...restProps } = props;
@@ -39,15 +39,19 @@ export function withTracking(WrappedComponent) {
39
39
  // track banner in future
40
40
  }
41
41
  }
42
- const currentRef = ref.current;
43
- if (currentRef) {
44
- const handleClick = useCallback((e) => {
45
- controller?.track.product.click(e, result);
46
- }, []);
47
- currentRef.setAttribute('sstracking', 'true');
48
- currentRef.removeEventListener('click', handleClick);
49
- currentRef.addEventListener('click', handleClick);
50
- }
42
+ const handleClick = useCallback((e) => {
43
+ controller?.track.product.click(e, result);
44
+ }, [controller, result]);
45
+ useEffect(() => {
46
+ const currentRef = ref.current;
47
+ if (currentRef) {
48
+ currentRef.setAttribute('sstracking', 'true');
49
+ currentRef.addEventListener('click', handleClick, true); // Use capture phase
50
+ return () => {
51
+ currentRef.removeEventListener('click', handleClick, true);
52
+ };
53
+ }
54
+ }, [ref, handleClick]);
51
55
  const trackingProps = {
52
56
  ...restProps,
53
57
  controller,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@searchspring/snap-preact-components",
3
- "version": "0.70.1",
3
+ "version": "0.72.0",
4
4
  "description": "Snap Preact Component Library",
5
5
  "author": "Searchspring",
6
6
  "license": "MIT",
@@ -29,7 +29,7 @@
29
29
  "dependencies": {
30
30
  "@cypress/react": "^8.0.0",
31
31
  "@emotion/react": "11.9.0",
32
- "@searchspring/snap-toolbox": "0.70.1",
32
+ "@searchspring/snap-toolbox": "0.72.0",
33
33
  "classnames": "^2.3.2",
34
34
  "cypress": "^13.7.1",
35
35
  "cypress-wait-until": "^1.7.2",
@@ -52,14 +52,14 @@
52
52
  "@babel/preset-env": "^7.21.4",
53
53
  "@babel/preset-react": "^7.18.6",
54
54
  "@babel/runtime": "^7.21.0",
55
- "@searchspring/snap-client": "0.70.1",
56
- "@searchspring/snap-controller": "0.70.1",
57
- "@searchspring/snap-event-manager": "0.70.1",
58
- "@searchspring/snap-logger": "0.70.1",
59
- "@searchspring/snap-profiler": "0.70.1",
60
- "@searchspring/snap-store-mobx": "0.70.1",
61
- "@searchspring/snap-tracker": "0.70.1",
62
- "@searchspring/snap-url-manager": "0.70.1",
55
+ "@searchspring/snap-client": "0.72.0",
56
+ "@searchspring/snap-controller": "0.72.0",
57
+ "@searchspring/snap-event-manager": "0.72.0",
58
+ "@searchspring/snap-logger": "0.72.0",
59
+ "@searchspring/snap-profiler": "0.72.0",
60
+ "@searchspring/snap-store-mobx": "0.72.0",
61
+ "@searchspring/snap-tracker": "0.72.0",
62
+ "@searchspring/snap-url-manager": "0.72.0",
63
63
  "@storybook/addon-actions": "6.4.22",
64
64
  "@storybook/addon-controls": "6.4.22",
65
65
  "@storybook/addon-docs": "6.4.22",
@@ -84,5 +84,5 @@
84
84
  "webpack-merge": "^5.8.0"
85
85
  },
86
86
  "sideEffects": false,
87
- "gitHead": "91ca0ec407be1edb997da1256242d167aa7da79f"
87
+ "gitHead": "737d4bd308ebbb852fe3f09b2b90f4af87bbe264"
88
88
  }