@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.
Files changed (53) hide show
  1. package/esm/_internal/Avatar2.js +2 -1
  2. package/esm/_internal/Avatar2.js.map +1 -1
  3. package/esm/_internal/Slider2.js +21 -2
  4. package/esm/_internal/Slider2.js.map +1 -1
  5. package/esm/_internal/Thumbnail2.js +179 -782
  6. package/esm/_internal/Thumbnail2.js.map +1 -1
  7. package/esm/_internal/avatar.js +0 -3
  8. package/esm/_internal/avatar.js.map +1 -1
  9. package/esm/_internal/comment-block.js +0 -3
  10. package/esm/_internal/comment-block.js.map +1 -1
  11. package/esm/_internal/image-block.js +0 -3
  12. package/esm/_internal/image-block.js.map +1 -1
  13. package/esm/_internal/link-preview.js +0 -3
  14. package/esm/_internal/link-preview.js.map +1 -1
  15. package/esm/_internal/mdi.js +2 -2
  16. package/esm/_internal/mdi.js.map +1 -1
  17. package/esm/_internal/mosaic.js +0 -3
  18. package/esm/_internal/mosaic.js.map +1 -1
  19. package/esm/_internal/post-block.js +0 -3
  20. package/esm/_internal/post-block.js.map +1 -1
  21. package/esm/_internal/slider.js +1 -2
  22. package/esm/_internal/slider.js.map +1 -1
  23. package/esm/_internal/thumbnail.js +1 -4
  24. package/esm/_internal/thumbnail.js.map +1 -1
  25. package/esm/_internal/types.js +1 -0
  26. package/esm/_internal/types.js.map +1 -1
  27. package/esm/_internal/user-block.js +0 -3
  28. package/esm/_internal/user-block.js.map +1 -1
  29. package/esm/index.js +2 -4
  30. package/esm/index.js.map +1 -1
  31. package/package.json +4 -4
  32. package/src/components/avatar/Avatar.tsx +1 -0
  33. package/src/components/avatar/__snapshots__/Avatar.test.tsx.snap +30 -30
  34. package/src/components/image-block/__snapshots__/ImageBlock.test.tsx.snap +1 -1
  35. package/src/components/mosaic/__snapshots__/Mosaic.test.tsx.snap +30 -30
  36. package/src/components/post-block/__snapshots__/PostBlock.test.tsx.snap +1 -1
  37. package/src/components/slideshow/__snapshots__/Slideshow.test.tsx.snap +10 -10
  38. package/src/components/table/__snapshots__/Table.test.tsx.snap +3 -3
  39. package/src/components/thumbnail/Thumbnail.stories.tsx +427 -52
  40. package/src/components/thumbnail/Thumbnail.test.tsx +14 -2
  41. package/src/components/thumbnail/Thumbnail.tsx +82 -47
  42. package/src/components/thumbnail/__snapshots__/Thumbnail.test.tsx.snap +26 -81
  43. package/src/components/thumbnail/index.ts +1 -0
  44. package/src/components/thumbnail/useFocusPointStyle.tsx +89 -0
  45. package/src/components/thumbnail/useImageLoad.ts +24 -23
  46. package/src/stories/generated/List/Demos.stories.tsx +2 -0
  47. package/src/stories/knobs/focusKnob.ts +1 -1
  48. package/src/stories/knobs/image.ts +35 -3
  49. package/types.d.ts +7 -0
  50. package/esm/_internal/clamp.js +0 -22
  51. package/esm/_internal/clamp.js.map +0 -1
  52. package/src/components/thumbnail/useClickable.ts +0 -26
  53. 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
- };