@reykjavik/hanna-react 0.10.60 → 0.10.61
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/Alert.js +3 -3
- package/CHANGELOG.md +10 -0
- package/_abstract/_AbstractCarousel.js +38 -63
- package/assets.d.ts +1 -1
- package/package.json +1 -1
- package/utils/seenEffect.d.ts +3 -4
- package/utils/seenEffect.js +20 -19
package/Alert.js
CHANGED
|
@@ -8,7 +8,7 @@ const getBemClass_1 = tslib_1.__importDefault(require("@hugsmidjan/react/utils/g
|
|
|
8
8
|
const i18n_1 = require("@reykjavik/hanna-utils/i18n");
|
|
9
9
|
const _Button_1 = tslib_1.__importDefault(require("./_abstract/_Button"));
|
|
10
10
|
const env_1 = require("./utils/env");
|
|
11
|
-
// FIXME: Eventually import from @reykjavik/hanna-
|
|
11
|
+
// FIXME: Eventually import from @reykjavik/hanna-css
|
|
12
12
|
const AlertCloseTransitionDuration = 400;
|
|
13
13
|
const useAutoClosing = (autoClose) => {
|
|
14
14
|
const [temp, setTemp] = (0, react_1.useState)(0);
|
|
@@ -40,7 +40,7 @@ exports.alertTypes = {
|
|
|
40
40
|
};
|
|
41
41
|
const Alert = (props) => {
|
|
42
42
|
const { type, childrenHTML, children, onClose, closeUrl, closable = !!(onClose || closeUrl != null), ssr, onClosed, } = props;
|
|
43
|
-
const autoClose =
|
|
43
|
+
const autoClose = Math.max(props.autoClose || 0, 0);
|
|
44
44
|
const closing = (0, react_1.useRef)();
|
|
45
45
|
const [open, setOpen] = (0, react_1.useState)(!!ssr);
|
|
46
46
|
const isBrowser = (0, hooks_1.useIsBrowserSide)(ssr);
|
|
@@ -51,7 +51,7 @@ const Alert = (props) => {
|
|
|
51
51
|
});
|
|
52
52
|
const closeAlert = (0, react_1.useCallback)((event) => {
|
|
53
53
|
const ret = onClose &&
|
|
54
|
-
// @ts-expect-error (@deprecated `event` parameter will be removed in v0.
|
|
54
|
+
// @ts-expect-error (@deprecated `event` parameter will be removed in v0.11)
|
|
55
55
|
onClose(event);
|
|
56
56
|
if (ret !== false) {
|
|
57
57
|
setOpen(false);
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
- ... <!-- Add new lines here. -->
|
|
6
6
|
|
|
7
|
+
## 0.10.61
|
|
8
|
+
|
|
9
|
+
_2022-08-11_
|
|
10
|
+
|
|
11
|
+
- feat: Add mouse-cursor scroll controls for `Carousel`-related components —
|
|
12
|
+
Remove mousewheel hijacking behavior.
|
|
13
|
+
- fix: `startSeen` hiding components with `html.before-sprinkling` present
|
|
14
|
+
|
|
7
15
|
## 0.10.60
|
|
8
16
|
|
|
9
17
|
_2022-06-24_
|
|
@@ -22,6 +30,7 @@ _2022-06-20_
|
|
|
22
30
|
- feat: Add prop `small` to `ArticleMeta`
|
|
23
31
|
- fix: Suppress redundant className `.Heading--normal`
|
|
24
32
|
- feat: Add prop `focalPoint` to all `ImageProps`
|
|
33
|
+
- feat: Add `EffectProp.effectType` prop value `none` for opting out
|
|
25
34
|
|
|
26
35
|
## 0.10.56 – 0.10.58
|
|
27
36
|
|
|
@@ -213,6 +222,7 @@ _2022-03-09_
|
|
|
213
222
|
|
|
214
223
|
- feat: Add props `autoClose` and `onClosed` to `Alert` — use `onClosed` when
|
|
215
224
|
removing the Alert from the DOM
|
|
225
|
+
- feat: Deprecate the `event` argument for `Alert`'s `onClose` callback
|
|
216
226
|
- feat: Add prop `align?: 'right'` to `ButtonBar`
|
|
217
227
|
- fix: Make `FileInput` image previews (thumbnails) more resilient overall
|
|
218
228
|
- fix: Plug memory-leaks caused by `FileInput` image thumbnails.
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const react_1 = tslib_1.__importStar(require("react"));
|
|
5
5
|
const A_1 = tslib_1.__importDefault(require("@hugsmidjan/qj/A"));
|
|
6
|
+
const debounce_1 = tslib_1.__importDefault(require("@hugsmidjan/qj/debounce"));
|
|
6
7
|
const focusElm_1 = tslib_1.__importDefault(require("@hugsmidjan/qj/focusElm"));
|
|
7
8
|
const throttle_1 = tslib_1.__importDefault(require("@hugsmidjan/qj/throttle"));
|
|
8
9
|
const hooks_1 = require("@hugsmidjan/react/hooks");
|
|
@@ -10,8 +11,7 @@ const getBemClass_1 = tslib_1.__importDefault(require("@hugsmidjan/react/utils/g
|
|
|
10
11
|
const hanna_utils_1 = require("@reykjavik/hanna-utils");
|
|
11
12
|
const CarouselStepper_1 = tslib_1.__importDefault(require("../CarouselStepper"));
|
|
12
13
|
const seenEffect_1 = require("../utils/seenEffect");
|
|
13
|
-
|
|
14
|
-
const WHEEL_HIJACK_TIMEOUT_MS = 667;
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
15
|
const scrollXBy = (elm, deltaX) => {
|
|
16
16
|
const left = elm.scrollLeft + deltaX;
|
|
17
17
|
elm.scrollTo(left, elm.scrollTop);
|
|
@@ -22,54 +22,6 @@ const scrollXBy = (elm, deltaX) => {
|
|
|
22
22
|
// NOTE 2: Both Chrome and Safari tend to snap hard to the nearest list item
|
|
23
23
|
// while Firefox is more smooth. Haven't found a way around that.
|
|
24
24
|
};
|
|
25
|
-
const exitWheelHijack = (elm) => () => {
|
|
26
|
-
elm.style.scrollSnapType = '';
|
|
27
|
-
const lastElmOffset = elm.lastElementChild
|
|
28
|
-
? elm.lastElementChild.offsetLeft
|
|
29
|
-
: 0;
|
|
30
|
-
// trigger one last scroll event, to make sure the element's
|
|
31
|
-
// `style.scrollSnapType`'s behavior kicks in.
|
|
32
|
-
// Otherwise the list may stay stuck in an over-scrolled state —
|
|
33
|
-
// off to the right.
|
|
34
|
-
scrollXBy(elm, Math.min(0, lastElmOffset - elm.scrollLeft));
|
|
35
|
-
};
|
|
36
|
-
const handleMouseWheel = (e) => {
|
|
37
|
-
const elm = e.currentTarget;
|
|
38
|
-
if (e.deltaX || elm.$hasXDeltaed) {
|
|
39
|
-
elm.$hasXDeltaed = true;
|
|
40
|
-
exitWheelHijack(elm);
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
if (e.deltaY && !e.deltaX) {
|
|
44
|
-
if (e.deltaY < 0) {
|
|
45
|
-
// Stop scroll-capture when list is at left-edge
|
|
46
|
-
if (elm.scrollLeft < 50) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
// Stop scroll-capture when list is beyond left edge of last list item
|
|
52
|
-
if (elm.scrollLeft >
|
|
53
|
-
(elm.lastElementChild ? elm.lastElementChild.offsetLeft : 0)) {
|
|
54
|
-
exitWheelHijack(elm);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
e.preventDefault();
|
|
59
|
-
e.stopImmediatePropagation();
|
|
60
|
-
// Disable `scroll-snap-style` because otherwise
|
|
61
|
-
// small-scale elm.scrollTo() calls have no effect
|
|
62
|
-
elm.style.scrollSnapType = 'initial';
|
|
63
|
-
scrollXBy(elm, e.deltaY * WHEEL_AMPLIFIER);
|
|
64
|
-
clearTimeout(elm.$timeout);
|
|
65
|
-
elm.$timeout = setTimeout(exitWheelHijack(elm), WHEEL_HIJACK_TIMEOUT_MS);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
const handleMouseLeave = (e) => {
|
|
69
|
-
const elm = e.currentTarget;
|
|
70
|
-
exitWheelHijack(elm);
|
|
71
|
-
elm.$hasXDeltaed = undefined;
|
|
72
|
-
};
|
|
73
25
|
const AbstractCarousel = (props) => {
|
|
74
26
|
const { title, items = [], Component, ComponentProps, bem = 'Carousel', modifier, ssr, startSeen, } = props;
|
|
75
27
|
const children = props.children && props.children.filter(hanna_utils_1.notNully);
|
|
@@ -78,9 +30,10 @@ const AbstractCarousel = (props) => {
|
|
|
78
30
|
const listRef = (0, react_1.useRef)(null);
|
|
79
31
|
const [activeItem, setActiveItem] = (0, react_1.useState)(0);
|
|
80
32
|
const isBrowser = (0, hooks_1.useIsBrowserSide)(ssr);
|
|
81
|
-
//
|
|
33
|
+
// Since listElm gets unmounted and remounted based on isBrowser, we
|
|
34
|
+
// wait for isBrowser is true before setting scroll and resize events.
|
|
35
|
+
const listElm = isBrowser && listRef.current;
|
|
82
36
|
(0, react_1.useEffect)(() => {
|
|
83
|
-
const listElm = listRef.current;
|
|
84
37
|
if (!listElm) {
|
|
85
38
|
return;
|
|
86
39
|
}
|
|
@@ -100,34 +53,56 @@ const AbstractCarousel = (props) => {
|
|
|
100
53
|
});
|
|
101
54
|
}, 300, true);
|
|
102
55
|
calcLeftOffset();
|
|
103
|
-
listElm.addEventListener('wheel', handleMouseWheel);
|
|
104
56
|
listElm.addEventListener('scroll', calcActiveItem, { passive: true });
|
|
105
57
|
window.addEventListener('resize', calcLeftOffset, { passive: true });
|
|
106
58
|
return () => {
|
|
107
|
-
listElm.removeEventListener('wheel', handleMouseWheel);
|
|
108
59
|
listElm.removeEventListener('scroll', calcActiveItem);
|
|
109
60
|
window.removeEventListener('resize', calcLeftOffset);
|
|
110
61
|
};
|
|
111
|
-
}, []);
|
|
62
|
+
}, [listElm]);
|
|
112
63
|
const scrollToItem = (newActive) => {
|
|
113
|
-
setActiveItem(newActive);
|
|
114
64
|
const listElm = listRef.current;
|
|
115
65
|
const newItem = listElm.children[newActive];
|
|
116
|
-
|
|
66
|
+
if (!newItem) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
setActiveItem(newActive);
|
|
70
|
+
listElm.scrollLeft = newItem.offsetLeft || 1;
|
|
117
71
|
setTimeout(() => (0, focusElm_1.default)(newItem), 500);
|
|
118
72
|
};
|
|
73
|
+
const { delayedScrollLeft, delayedScrollRight } = (0, react_1.useMemo)(() => {
|
|
74
|
+
const delayedScrollLeft = (0, debounce_1.default)((currentActive) => {
|
|
75
|
+
scrollToItem(currentActive - 1);
|
|
76
|
+
setTimeout(() => delayedScrollLeft(currentActive - 1), 0);
|
|
77
|
+
}, 1000);
|
|
78
|
+
const delayedScrollRight = (0, debounce_1.default)((currentActive) => {
|
|
79
|
+
scrollToItem(currentActive + 1);
|
|
80
|
+
setTimeout(() => delayedScrollRight(currentActive + 1), 0);
|
|
81
|
+
}, 1000);
|
|
82
|
+
return { delayedScrollLeft, delayedScrollRight };
|
|
83
|
+
}, []);
|
|
119
84
|
const [outerRef] = (0, seenEffect_1.useSeenEffect)(startSeen);
|
|
120
85
|
if (!itemCount) {
|
|
121
86
|
return null;
|
|
122
87
|
}
|
|
88
|
+
const itemList = (react_1.default.createElement("div", { className: bem + '__itemlist', style: leftOffset
|
|
89
|
+
? { '--Carousel--leftOffset': `${leftOffset}px` }
|
|
90
|
+
: undefined, "data-scroll-snapping": leftOffset ? 'true' : undefined, ref: listRef }, children ||
|
|
91
|
+
items.map((item, i) => (
|
|
92
|
+
// @ts-expect-error (Can't be arsed...)
|
|
93
|
+
react_1.default.createElement(Component, Object.assign({ key: i }, ComponentProps, item))))));
|
|
123
94
|
return (react_1.default.createElement("div", { className: (0, getBemClass_1.default)(bem, modifier, props.className), ref: outerRef, "data-sprinkled": isBrowser },
|
|
124
95
|
title && react_1.default.createElement("h2", { className: bem + '__title' }, title),
|
|
125
|
-
react_1.default.createElement("div", { className: bem + '__itemlist',
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
96
|
+
isBrowser ? (react_1.default.createElement("div", { className: bem + '__itemlist-wrapper' },
|
|
97
|
+
itemList,
|
|
98
|
+
react_1.default.createElement("div", { className: bem + '__itemlist-goLeft', onClick: () => {
|
|
99
|
+
delayedScrollLeft.cancel();
|
|
100
|
+
scrollToItem(activeItem - 1);
|
|
101
|
+
}, onMouseOver: () => delayedScrollLeft(activeItem), onMouseOut: () => delayedScrollLeft.cancel() }),
|
|
102
|
+
react_1.default.createElement("div", { className: bem + '__itemlist-goRight', onClick: () => {
|
|
103
|
+
delayedScrollRight.cancel();
|
|
104
|
+
scrollToItem(activeItem + 1);
|
|
105
|
+
}, onMouseOver: () => delayedScrollRight(activeItem), onMouseOut: () => delayedScrollRight.cancel() }))) : (itemList),
|
|
131
106
|
isBrowser && (react_1.default.createElement(CarouselStepper_1.default, { itemCount: itemCount, setCurrent: scrollToItem, current: activeItem }))));
|
|
132
107
|
};
|
|
133
108
|
exports.default = AbstractCarousel;
|
package/assets.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ getIllustrationUrl,
|
|
|
27
27
|
/** @deprecated Instead `import type { illustrations } from '@reykjavik/hanna-utils/assets';` (Will be removed in v0.11) */
|
|
28
28
|
illustrations, };
|
|
29
29
|
/** @deprecated Use `getCssBundleUrl` from '@reykjavik/hanna-css' instead (Will be reomved in v0.11) */
|
|
30
|
-
export declare const getCssBundleUrl: (cssTokens: string | Array<string>, version?: string
|
|
30
|
+
export declare const getCssBundleUrl: (cssTokens: string | Array<string>, version?: string) => string;
|
|
31
31
|
/** @deprecated (Will be removed in v0.11) */
|
|
32
32
|
export declare const efnistakn_menu: readonly ["menu/borgarstjori", "menu/borgarstjorn", "menu/bygg_framkv", "menu/fjarmal", "menu/fundargerdir", "menu/itrottir_aftreying", "menu/log_reglugerdir", "menu/mannaudur", "menu/menning", "menu/rad_nefndir", "menu/skipulag", "menu/skolar_fristund", "menu/svid_deildir", "menu/umhverfi_samgongur", "menu/velferd_fjolskylda"];
|
|
33
33
|
/** @deprecated (Will be removed in v0.11) */
|
package/package.json
CHANGED
package/utils/seenEffect.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ export declare const getObserver: {
|
|
|
4
4
|
(target: Element, callback?: ((target: Element) => void) | undefined): (() => void) | undefined;
|
|
5
5
|
DATA_ATTR_NAME: string;
|
|
6
6
|
};
|
|
7
|
-
export declare const seenEffectOptOut: (target: Element, setFlag?: boolean) => void;
|
|
8
7
|
declare const effects: {
|
|
9
8
|
readonly fadein: 1;
|
|
10
9
|
readonly fadeup: 1;
|
|
@@ -16,13 +15,13 @@ export declare type EffectProp = {
|
|
|
16
15
|
effectType?: SeenEffectType | 'none';
|
|
17
16
|
};
|
|
18
17
|
/** Asserts that a prop value is a SeenEffectType and returns undefined otherwise */
|
|
19
|
-
export declare const
|
|
20
|
-
export declare const getEffectAttr: (maybeType?: string
|
|
18
|
+
export declare const ensureEffectType: (maybeType?: string) => SeenEffectType | undefined;
|
|
19
|
+
export declare const getEffectAttr: (maybeType?: string) => {
|
|
21
20
|
'data-seen-effect': string | undefined;
|
|
22
21
|
};
|
|
23
22
|
export declare type SeenProp = {
|
|
24
23
|
/** Should the component appear instantly, and not transition-in once seen */
|
|
25
24
|
startSeen?: boolean;
|
|
26
25
|
};
|
|
27
|
-
export declare const useSeenEffect: <E extends Element = HTMLDivElement>(startSeen?: boolean
|
|
26
|
+
export declare const useSeenEffect: <E extends Element = HTMLDivElement>(startSeen?: boolean, customRef?: RefObject<E> | undefined) => [RefObject<E> | undefined, true | undefined];
|
|
28
27
|
export {};
|
package/utils/seenEffect.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.useSeenEffect = exports.getEffectAttr = exports.
|
|
3
|
+
exports.useSeenEffect = exports.getEffectAttr = exports.ensureEffectType = exports.getObserver = exports.DATA_ATTR_NAME = void 0;
|
|
4
4
|
const react_1 = require("react");
|
|
5
5
|
exports.DATA_ATTR_NAME = 'is-seen';
|
|
6
6
|
const STACKING_DELAY = 400; // ms
|
|
@@ -36,13 +36,10 @@ const getObserver = (target, callback) => {
|
|
|
36
36
|
}
|
|
37
37
|
target.setAttribute(dataAttr, 'false');
|
|
38
38
|
observer.observe(target);
|
|
39
|
+
// return teardown/unmount effect
|
|
39
40
|
return () => observer.unobserve(target);
|
|
40
41
|
};
|
|
41
42
|
exports.getObserver = getObserver;
|
|
42
|
-
const seenEffectOptOut = (target, setFlag = true) => {
|
|
43
|
-
setFlag ? target.setAttribute(dataAttr, '') : target.removeAttribute(dataAttr);
|
|
44
|
-
};
|
|
45
|
-
exports.seenEffectOptOut = seenEffectOptOut;
|
|
46
43
|
exports.getObserver.DATA_ATTR_NAME = exports.DATA_ATTR_NAME;
|
|
47
44
|
// ---------------------------------------------------------------------------
|
|
48
45
|
// ---------------------------------------------------------------------------
|
|
@@ -52,10 +49,10 @@ const effects = {
|
|
|
52
49
|
fadeleft: 1,
|
|
53
50
|
};
|
|
54
51
|
/** Asserts that a prop value is a SeenEffectType and returns undefined otherwise */
|
|
55
|
-
const
|
|
56
|
-
exports.
|
|
52
|
+
const ensureEffectType = (maybeType) => maybeType && maybeType in effects ? maybeType : undefined;
|
|
53
|
+
exports.ensureEffectType = ensureEffectType;
|
|
57
54
|
const getEffectAttr = (maybeType) => ({
|
|
58
|
-
'data-seen-effect': maybeType === 'none' ? undefined : (0, exports.
|
|
55
|
+
'data-seen-effect': maybeType === 'none' ? undefined : (0, exports.ensureEffectType)(maybeType) || '',
|
|
59
56
|
});
|
|
60
57
|
exports.getEffectAttr = getEffectAttr;
|
|
61
58
|
const useSeenEffect = (
|
|
@@ -63,19 +60,23 @@ const useSeenEffect = (
|
|
|
63
60
|
startSeen,
|
|
64
61
|
/** Bring Your Own RefObject */
|
|
65
62
|
customRef) => {
|
|
63
|
+
const _startSeen = startSeen || undefined; // normalize
|
|
64
|
+
const [isSeen, setSeen] = (0, react_1.useState)(_startSeen);
|
|
66
65
|
const localRef = (0, react_1.useRef)(null);
|
|
67
|
-
const
|
|
68
|
-
const ref = !startSeen && (customRef || localRef);
|
|
66
|
+
const ref = customRef || localRef;
|
|
69
67
|
(0, react_1.useEffect)(() => {
|
|
70
|
-
setSeen(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
setSeen(_startSeen);
|
|
69
|
+
const refElm = ref.current;
|
|
70
|
+
if (refElm) {
|
|
71
|
+
if (_startSeen) {
|
|
72
|
+
refElm.setAttribute(dataAttr, '');
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
refElm.removeAttribute(dataAttr);
|
|
76
|
+
return (0, exports.getObserver)(refElm, () => setSeen(true));
|
|
77
|
+
}
|
|
77
78
|
}
|
|
78
|
-
}, [
|
|
79
|
-
return [ref
|
|
79
|
+
}, [_startSeen, ref]);
|
|
80
|
+
return [ref, isSeen];
|
|
80
81
|
};
|
|
81
82
|
exports.useSeenEffect = useSeenEffect;
|