@lumx/react 2.1.9 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/_internal/Avatar2.js +2 -1
- package/esm/_internal/Avatar2.js.map +1 -1
- package/esm/_internal/Slider2.js +21 -2
- package/esm/_internal/Slider2.js.map +1 -1
- package/esm/_internal/Thumbnail2.js +179 -782
- package/esm/_internal/Thumbnail2.js.map +1 -1
- package/esm/_internal/avatar.js +0 -3
- package/esm/_internal/avatar.js.map +1 -1
- package/esm/_internal/comment-block.js +0 -3
- package/esm/_internal/comment-block.js.map +1 -1
- package/esm/_internal/image-block.js +0 -3
- package/esm/_internal/image-block.js.map +1 -1
- package/esm/_internal/link-preview.js +0 -3
- package/esm/_internal/link-preview.js.map +1 -1
- package/esm/_internal/mdi.js +2 -2
- package/esm/_internal/mdi.js.map +1 -1
- package/esm/_internal/mosaic.js +0 -3
- package/esm/_internal/mosaic.js.map +1 -1
- package/esm/_internal/post-block.js +0 -3
- package/esm/_internal/post-block.js.map +1 -1
- package/esm/_internal/slider.js +1 -2
- package/esm/_internal/slider.js.map +1 -1
- package/esm/_internal/thumbnail.js +1 -4
- package/esm/_internal/thumbnail.js.map +1 -1
- package/esm/_internal/types.js +1 -0
- package/esm/_internal/types.js.map +1 -1
- package/esm/_internal/user-block.js +0 -3
- package/esm/_internal/user-block.js.map +1 -1
- package/esm/index.js +2 -4
- package/esm/index.js.map +1 -1
- package/package.json +4 -4
- package/src/components/avatar/Avatar.tsx +1 -0
- package/src/components/avatar/__snapshots__/Avatar.test.tsx.snap +30 -30
- package/src/components/image-block/__snapshots__/ImageBlock.test.tsx.snap +1 -1
- package/src/components/mosaic/__snapshots__/Mosaic.test.tsx.snap +30 -30
- package/src/components/post-block/__snapshots__/PostBlock.test.tsx.snap +1 -1
- package/src/components/slideshow/__snapshots__/Slideshow.test.tsx.snap +10 -10
- package/src/components/table/__snapshots__/Table.test.tsx.snap +3 -3
- package/src/components/thumbnail/Thumbnail.stories.tsx +427 -52
- package/src/components/thumbnail/Thumbnail.test.tsx +14 -2
- package/src/components/thumbnail/Thumbnail.tsx +82 -47
- package/src/components/thumbnail/__snapshots__/Thumbnail.test.tsx.snap +26 -81
- package/src/components/thumbnail/index.ts +1 -0
- package/src/components/thumbnail/useFocusPointStyle.tsx +89 -0
- package/src/components/thumbnail/useImageLoad.ts +24 -23
- package/src/stories/generated/List/Demos.stories.tsx +2 -0
- package/src/stories/knobs/focusKnob.ts +1 -1
- package/src/stories/knobs/image.ts +35 -3
- package/types.d.ts +7 -0
- package/esm/_internal/clamp.js +0 -22
- package/esm/_internal/clamp.js.map +0 -1
- package/src/components/thumbnail/useClickable.ts +0 -26
- package/src/components/thumbnail/useFocusPoint.ts +0 -154
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { KeyboardEventHandler, MouseEventHandler, useMemo } from 'react';
|
|
2
|
-
|
|
3
|
-
interface Props {
|
|
4
|
-
role: 'button';
|
|
5
|
-
tabIndex: 0;
|
|
6
|
-
onClick: MouseEventHandler;
|
|
7
|
-
onKeyPress: KeyboardEventHandler;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const useClickable = ({ onClick, onKeyPress }: { onClick?: any; onKeyPress?: any }): Props | undefined => {
|
|
11
|
-
return useMemo(() => {
|
|
12
|
-
if (!onClick) return undefined;
|
|
13
|
-
|
|
14
|
-
return {
|
|
15
|
-
role: 'button',
|
|
16
|
-
tabIndex: 0,
|
|
17
|
-
onClick,
|
|
18
|
-
onKeyPress(event) {
|
|
19
|
-
onKeyPress?.(event);
|
|
20
|
-
if (event.key === 'Enter' || event.key === ' ') {
|
|
21
|
-
onClick?.();
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
|
-
}, [onClick, onKeyPress]);
|
|
26
|
-
};
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { CSSProperties, MutableRefObject, RefObject, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
-
import { useOnResize } from '@lumx/react/hooks/useOnResize';
|
|
3
|
-
import { AspectRatio } from '@lumx/react';
|
|
4
|
-
import { clamp } from '@lumx/react/utils/clamp';
|
|
5
|
-
import { LoadingState } from '@lumx/react/components/thumbnail/useImageLoad';
|
|
6
|
-
import { Callback } from '@lumx/react/utils';
|
|
7
|
-
import { isEqual } from 'lodash';
|
|
8
|
-
import { FocusPoint } from './types';
|
|
9
|
-
|
|
10
|
-
const IMG_STYLES: CSSProperties = {
|
|
11
|
-
bottom: 0,
|
|
12
|
-
left: 0,
|
|
13
|
-
position: 'absolute',
|
|
14
|
-
right: 0,
|
|
15
|
-
top: 0,
|
|
16
|
-
minHeight: '100%',
|
|
17
|
-
minWidth: '100%',
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const WRAPPER_STYLES: CSSProperties = {
|
|
21
|
-
overflow: 'hidden',
|
|
22
|
-
position: 'relative',
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
type Sizes = {
|
|
26
|
-
imgWidth?: number;
|
|
27
|
-
imgHeight?: number;
|
|
28
|
-
containerWidth?: number;
|
|
29
|
-
containerHeight?: number;
|
|
30
|
-
aspectRatio?: AspectRatio;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
function calculateSizes(
|
|
34
|
-
imageElement?: HTMLImageElement | null | undefined,
|
|
35
|
-
parentElement?: HTMLElement | null,
|
|
36
|
-
aspectRatio?: AspectRatio,
|
|
37
|
-
): Sizes | undefined {
|
|
38
|
-
if (!imageElement || !parentElement || !aspectRatio || aspectRatio === AspectRatio.original) return undefined;
|
|
39
|
-
const { naturalWidth: imgWidth, naturalHeight: imgHeight } = imageElement || { naturalWidth: 0, naturalHeight: 0 };
|
|
40
|
-
const { width: containerWidth, height: containerHeight } = parentElement?.getBoundingClientRect() || {
|
|
41
|
-
width: 0,
|
|
42
|
-
height: 0,
|
|
43
|
-
};
|
|
44
|
-
return { imgWidth, imgHeight, containerWidth, containerHeight, aspectRatio };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const parseFocusPoint = (point: FocusPoint = {}): Required<FocusPoint> => {
|
|
48
|
-
return { x: point.x ? clamp(point.x, -1, 1) : 0, y: point.y ? clamp(point.y, -1, 1) : 0 };
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
function calculateImageShift(
|
|
52
|
-
containerToImageSizeRatio: number,
|
|
53
|
-
containerSize: number,
|
|
54
|
-
imageSize: number,
|
|
55
|
-
focusSize: number,
|
|
56
|
-
toMinus?: boolean,
|
|
57
|
-
) {
|
|
58
|
-
const containerCenter = Math.floor(containerSize / 2); // Container center in px
|
|
59
|
-
const focusFactor = (focusSize + 1) / 2; // Focus point of resize image in px
|
|
60
|
-
const scaledImage = Math.floor(imageSize / containerToImageSizeRatio); // Can't use width() as images may be display:none
|
|
61
|
-
let focus = Math.floor(focusFactor * scaledImage);
|
|
62
|
-
if (toMinus) {
|
|
63
|
-
focus = scaledImage - focus;
|
|
64
|
-
}
|
|
65
|
-
let focusOffset = focus - containerCenter; // Calculate difference between focus point and center
|
|
66
|
-
const remainder = scaledImage - focus; // Reduce offset if necessary so image remains filled
|
|
67
|
-
const containerRemainder = containerSize - containerCenter;
|
|
68
|
-
if (remainder < containerRemainder) {
|
|
69
|
-
focusOffset -= containerRemainder - remainder;
|
|
70
|
-
}
|
|
71
|
-
if (focusOffset < 0) {
|
|
72
|
-
focusOffset = 0;
|
|
73
|
-
}
|
|
74
|
-
return (focusOffset * -100) / containerSize;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
type Styles = { wrapper: CSSProperties; image: CSSProperties };
|
|
78
|
-
|
|
79
|
-
function calculateImageStyle(sizes: Sizes, point: Required<FocusPoint>): Styles | undefined {
|
|
80
|
-
if (!sizes.imgWidth || !sizes.imgHeight || !sizes.containerWidth || !sizes.containerHeight) {
|
|
81
|
-
return undefined;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Which is over by more?
|
|
85
|
-
const widthRatio = sizes.imgWidth / sizes.containerWidth;
|
|
86
|
-
const heightRatio = sizes.imgHeight / sizes.containerHeight;
|
|
87
|
-
|
|
88
|
-
const image: CSSProperties = { ...IMG_STYLES };
|
|
89
|
-
|
|
90
|
-
// Minimize image while still filling space
|
|
91
|
-
if (sizes.imgWidth > sizes.containerWidth && sizes.imgHeight > sizes.containerHeight) {
|
|
92
|
-
image[widthRatio > heightRatio ? 'maxHeight' : 'maxWidth'] = '100%';
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (widthRatio > heightRatio) {
|
|
96
|
-
image.left = `${calculateImageShift(heightRatio, sizes.containerWidth, sizes.imgWidth, point.x)}%`;
|
|
97
|
-
} else if (widthRatio < heightRatio) {
|
|
98
|
-
image.top = `${calculateImageShift(widthRatio, sizes.containerHeight, sizes.imgHeight, point.y, true)}%`;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
image,
|
|
103
|
-
wrapper: WRAPPER_STYLES,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Hook that calculate CSS style to shift the image in it's container according to the focus point.
|
|
109
|
-
*/
|
|
110
|
-
export const useFocusPoint = (options: {
|
|
111
|
-
image: string;
|
|
112
|
-
focusPoint?: FocusPoint;
|
|
113
|
-
aspectRatio?: AspectRatio;
|
|
114
|
-
imgRef: RefObject<HTMLImageElement>;
|
|
115
|
-
loadingState: LoadingState;
|
|
116
|
-
wrapper?: HTMLElement;
|
|
117
|
-
}): Styles | undefined => {
|
|
118
|
-
const { image, aspectRatio, focusPoint, imgRef, loadingState, wrapper } = options;
|
|
119
|
-
|
|
120
|
-
const point = parseFocusPoint(focusPoint);
|
|
121
|
-
|
|
122
|
-
const previousPoint = useRef<Required<FocusPoint>>();
|
|
123
|
-
const previousSizes = useRef<Sizes>({});
|
|
124
|
-
const [style, setStyle] = useState<Styles>();
|
|
125
|
-
|
|
126
|
-
// Update style.
|
|
127
|
-
const updateRef = useRef<Callback>(null);
|
|
128
|
-
const update = useMemo(
|
|
129
|
-
() => {
|
|
130
|
-
const updateFunction = () => {
|
|
131
|
-
const sizes = calculateSizes(imgRef?.current, wrapper, aspectRatio);
|
|
132
|
-
if (!sizes || (isEqual(sizes, previousSizes.current) && isEqual(point, previousPoint.current))) {
|
|
133
|
-
// Nothing changed.
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
setStyle(calculateImageStyle(sizes, point));
|
|
137
|
-
previousPoint.current = point;
|
|
138
|
-
previousSizes.current = sizes;
|
|
139
|
-
};
|
|
140
|
-
(updateRef as MutableRefObject<Callback>).current = updateFunction;
|
|
141
|
-
return updateFunction;
|
|
142
|
-
},
|
|
143
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
144
|
-
[...Object.values(point), imgRef, wrapper, aspectRatio],
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
// Update on image loaded.
|
|
148
|
-
useLayoutEffect(update, [image, update, loadingState]);
|
|
149
|
-
|
|
150
|
-
// Update on parent resize.
|
|
151
|
-
useOnResize(wrapper, updateRef);
|
|
152
|
-
|
|
153
|
-
return style;
|
|
154
|
-
};
|