@wavv/ui 2.4.6 → 2.4.8-alpha.1

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.
@@ -1,110 +1,152 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { keyframes } from "@emotion/react";
2
3
  import styled from "@emotion/styled";
3
- import { useEffect, useState } from "react";
4
- import { useOnClickOutside } from "../hooks/index.js";
4
+ import { useCallback, useEffect, useState } from "react";
5
+ import { Dialog, Modal, ModalOverlay } from "react-aria-components";
6
+ import useEventListener from "../hooks/useEventListener.js";
5
7
  import Icon from "./Icon/index.js";
6
8
  const ImageViewer = ({ visible, close, images, startIndex, alt, maxWidth, maxHeight, opacity, ...rest })=>{
7
9
  const [currentIndex, setCurrentIndex] = useState(startIndex);
8
- const exclusions = [
9
- '#viewer-prev',
10
- '#viewer-next',
11
- '#viewer-prev *',
12
- '#viewer-next *'
13
- ];
14
- const imageRef = useOnClickOutside(close, visible, exclusions);
10
+ const handleOpenChange = (open)=>{
11
+ if (!open) close();
12
+ };
15
13
  useEffect(()=>{
16
- setCurrentIndex(startIndex);
14
+ if (visible) setCurrentIndex(startIndex);
17
15
  }, [
16
+ visible,
18
17
  startIndex
19
18
  ]);
20
- const handlePrev = ()=>currentIndex > 0 ? setCurrentIndex(currentIndex - 1) : setCurrentIndex(images.length - 1);
21
- const handleNext = ()=>currentIndex < images.length - 1 ? setCurrentIndex(currentIndex + 1) : setCurrentIndex(0);
22
- const handleKeyPress = (event)=>{
23
- const { key } = event;
24
- switch(key){
19
+ const handlePrev = ()=>setCurrentIndex((prev)=>prev > 0 ? prev - 1 : images.length - 1);
20
+ const handleNext = ()=>setCurrentIndex((prev)=>prev < images.length - 1 ? prev + 1 : 0);
21
+ const handleKeyDown = useCallback((e)=>{
22
+ switch(e.key){
23
+ case 'Escape':
24
+ e.preventDefault();
25
+ close();
26
+ break;
25
27
  case 'ArrowLeft':
26
28
  case 'ArrowDown':
27
- event.preventDefault();
28
- handlePrev();
29
+ e.preventDefault();
30
+ setCurrentIndex((prev)=>prev > 0 ? prev - 1 : images.length - 1);
29
31
  break;
30
32
  case 'ArrowRight':
31
33
  case 'ArrowUp':
32
- event.preventDefault();
33
- handleNext();
34
- break;
35
- case 'Escape':
36
- event.preventDefault();
37
- close();
34
+ e.preventDefault();
35
+ setCurrentIndex((prev)=>prev < images.length - 1 ? prev + 1 : 0);
38
36
  break;
39
37
  default:
40
38
  break;
41
39
  }
42
- };
43
- useEffect(()=>{
44
- const ssr = "u" < typeof window;
45
- const cleanup = ssr ? ()=>null : ()=>window.removeEventListener('keydown', handleKeyPress);
46
- if (visible && !ssr) window.addEventListener('keydown', handleKeyPress);
47
- return cleanup;
48
40
  }, [
49
- visible,
50
- currentIndex
41
+ close,
42
+ images.length
43
+ ]);
44
+ useEventListener("u" > typeof window ? window : {
45
+ current: null
46
+ }, 'keydown', handleKeyDown, visible, [
47
+ handleKeyDown
51
48
  ]);
52
- return visible ? /*#__PURE__*/ jsxs(Viewer, {
49
+ if (0 === images.length) return null;
50
+ return /*#__PURE__*/ jsx(Overlay, {
51
+ isOpen: visible,
52
+ onOpenChange: handleOpenChange,
53
+ isDismissable: true,
53
54
  opacity: opacity,
54
55
  prevNext: images.length > 1,
55
56
  ...rest,
56
- children: [
57
- /*#__PURE__*/ jsx(Close, {
58
- onClick: close,
59
- children: /*#__PURE__*/ jsx(Icon, {
60
- name: "close",
61
- color: "white"
62
- })
63
- }),
64
- images.length > 1 && /*#__PURE__*/ jsx(PrevNext, {
65
- id: "viewer-prev",
66
- onClick: handlePrev,
67
- children: /*#__PURE__*/ jsx(Icon, {
68
- name: "chevron-left",
69
- size: 35,
70
- color: "white"
71
- })
72
- }),
73
- /*#__PURE__*/ jsx(Media, {
74
- ref: imageRef,
75
- src: images[currentIndex],
76
- alt: alt,
77
- maxWidth: maxWidth,
78
- maxHeight: maxHeight
79
- }),
80
- images.length > 1 && /*#__PURE__*/ jsx(PrevNext, {
81
- next: true,
82
- id: "viewer-next",
83
- onClick: handleNext,
84
- children: /*#__PURE__*/ jsx(Icon, {
85
- name: "chevron-right",
86
- size: 35,
87
- color: "white"
57
+ children: /*#__PURE__*/ jsx(StyledModal, {
58
+ children: /*#__PURE__*/ jsx(StyledDialog, {
59
+ "aria-label": alt || 'Image viewer',
60
+ children: /*#__PURE__*/ jsxs(ViewerContent, {
61
+ children: [
62
+ /*#__PURE__*/ jsx(Close, {
63
+ onClick: close,
64
+ children: /*#__PURE__*/ jsx(Icon, {
65
+ name: "close",
66
+ color: "white"
67
+ })
68
+ }),
69
+ images.length > 1 && /*#__PURE__*/ jsx(PrevNext, {
70
+ id: "viewer-prev",
71
+ onClick: handlePrev,
72
+ children: /*#__PURE__*/ jsx(Icon, {
73
+ name: "chevron-left",
74
+ size: 35,
75
+ color: "white"
76
+ })
77
+ }),
78
+ /*#__PURE__*/ jsx(Media, {
79
+ src: images[currentIndex],
80
+ alt: alt,
81
+ maxWidth: maxWidth,
82
+ maxHeight: maxHeight
83
+ }),
84
+ images.length > 1 && /*#__PURE__*/ jsx(PrevNext, {
85
+ $next: true,
86
+ id: "viewer-next",
87
+ onClick: handleNext,
88
+ children: /*#__PURE__*/ jsx(Icon, {
89
+ name: "chevron-right",
90
+ size: 35,
91
+ color: "white"
92
+ })
93
+ })
94
+ ]
88
95
  })
89
96
  })
90
- ]
91
- }) : null;
97
+ })
98
+ });
92
99
  };
93
- const Viewer = styled.div(({ prevNext, opacity })=>({
94
- width: '100%',
95
- height: '100%',
96
- backgroundColor: `rgba(0, 0, 0, ${opacity ?? 0.3})`,
100
+ const fadeIn = keyframes({
101
+ from: {
102
+ opacity: 0
103
+ },
104
+ to: {
105
+ opacity: 1
106
+ }
107
+ });
108
+ const fadeOut = keyframes({
109
+ from: {
110
+ opacity: 1
111
+ },
112
+ to: {
113
+ opacity: 0
114
+ }
115
+ });
116
+ const Overlay = styled(ModalOverlay)(({ prevNext, opacity })=>({
97
117
  position: 'fixed',
98
118
  top: 0,
99
119
  left: 0,
120
+ width: '100%',
121
+ height: '100%',
122
+ backgroundColor: `rgba(0, 0, 0, ${opacity ?? 0.3})`,
100
123
  display: 'grid',
101
124
  gridTemplateColumns: prevNext ? 'auto 1fr auto' : '1fr',
102
125
  justifyContent: 'space-between',
103
126
  alignItems: 'center',
104
127
  zIndex: 10000,
105
128
  userSelect: 'none',
106
- borderRadius: 5
129
+ borderRadius: 5,
130
+ '&[data-entering]': {
131
+ animation: `${fadeIn} 200ms ease-out`
132
+ },
133
+ '&[data-exiting]': {
134
+ animation: `${fadeOut} 200ms ease-in forwards`
135
+ }
107
136
  }));
137
+ const StyledModal = styled(Modal)({
138
+ outline: 'none',
139
+ pointerEvents: 'none',
140
+ display: 'contents'
141
+ });
142
+ const StyledDialog = styled(Dialog)({
143
+ outline: 'none',
144
+ pointerEvents: 'none',
145
+ display: 'contents'
146
+ });
147
+ const ViewerContent = styled.div({
148
+ display: 'contents'
149
+ });
108
150
  const Close = styled.div({
109
151
  position: 'absolute',
110
152
  top: 0,
@@ -115,9 +157,10 @@ const Close = styled.div({
115
157
  cursor: 'pointer',
116
158
  width: 50,
117
159
  height: 50,
118
- zIndex: 1
160
+ zIndex: 1,
161
+ pointerEvents: 'auto'
119
162
  });
120
- const PrevNext = styled.div(({ next })=>({
163
+ const PrevNext = styled.div(({ $next })=>({
121
164
  display: 'flex',
122
165
  justifyContent: 'center',
123
166
  alignItems: 'center',
@@ -125,18 +168,20 @@ const PrevNext = styled.div(({ next })=>({
125
168
  width: 50,
126
169
  height: '100%',
127
170
  backgroundColor: 'transparent',
128
- left: next ? void 0 : 0,
129
- right: next ? 0 : void 0,
171
+ left: $next ? void 0 : 0,
172
+ right: $next ? 0 : void 0,
130
173
  zIndex: 0,
174
+ pointerEvents: 'auto',
131
175
  '&:hover': {
132
- background: `linear-gradient(90deg, rgba(0,0,0,${next ? 0 : 0.4}) 0%, rgba(0,0,0,${next ? 0.4 : 0}) 100%)`
176
+ background: `linear-gradient(90deg, rgba(0,0,0,${$next ? 0 : 0.4}) 0%, rgba(0,0,0,${$next ? 0.4 : 0}) 100%)`
133
177
  }
134
178
  }));
135
179
  const Media = styled.img(({ maxWidth, maxHeight })=>({
136
180
  display: 'block',
137
181
  justifySelf: 'center',
138
182
  maxWidth: maxWidth || '100%',
139
- maxHeight: maxHeight || '100%'
183
+ maxHeight: maxHeight || '100%',
184
+ pointerEvents: 'auto'
140
185
  }));
141
186
  const components_ImageViewer = ImageViewer;
142
187
  export { components_ImageViewer as default };
@@ -1,8 +1,10 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { TagItem } from './typeDefs/tagTypes';
3
- import type { MarginPadding } from './types';
3
+ import type { MarginPadding, WidthHeight } from './types';
4
4
  type WithRemove = {
5
+ /** The function to call when the remove icon is clicked */
5
6
  onRemove: (key: string) => void;
7
+ /** The unique identifier for the tag. Required if `onRemove` is provided */
6
8
  id: string;
7
9
  };
8
10
  type WithoutRemove = {
@@ -16,7 +18,11 @@ type Props = {
16
18
  type?: 'error' | 'success' | 'warning';
17
19
  /** Sets the size of the Tag (top/bottom padding) */
18
20
  size?: 'small' | 'medium';
21
+ /** Sets the text color of the Tag */
22
+ textColor?: string;
23
+ /** Sets the gap between Tag children */
24
+ gap?: number;
19
25
  className?: string;
20
- } & RemoveProps & MarginPadding & Partial<TagItem>;
21
- declare const Tag: ({ children, id, value, iconLeft, iconRight, tooltip, removable, disabled, size, onRemove, ...props }: Props) => import("react/jsx-runtime").JSX.Element;
26
+ } & RemoveProps & MarginPadding & WidthHeight & Partial<TagItem>;
27
+ declare const Tag: ({ children, id, value, iconLeft, iconRight, tooltip, removable, disabled, size, textColor, gap, onRemove, ...props }: Props) => import("react/jsx-runtime").JSX.Element;
22
28
  export default Tag;
@@ -2,10 +2,10 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useTheme } from "@emotion/react";
3
3
  import styled from "@emotion/styled";
4
4
  import getIcon from "./helpers/getIcon.js";
5
- import { marginProps, paddingProps } from "./helpers/styledProps.js";
5
+ import { marginProps, paddingProps, widthHeightProps } from "./helpers/styledProps.js";
6
6
  import Icon from "./Icon/index.js";
7
7
  import Tooltip from "./Tooltip.js";
8
- const Tag = ({ children, id, value, iconLeft, iconRight, tooltip, removable, disabled, size = 'small', onRemove, ...props })=>{
8
+ const Tag = ({ children, id, value, iconLeft, iconRight, tooltip, removable, disabled, size = 'small', textColor, gap = 4, onRemove, ...props })=>{
9
9
  const { scale10 } = useTheme();
10
10
  const handleRemove = (key)=>{
11
11
  if (onRemove) onRemove(key);
@@ -17,10 +17,11 @@ const Tag = ({ children, id, value, iconLeft, iconRight, tooltip, removable, dis
17
17
  rightIcon: !!(iconRight || removable && !disabled),
18
18
  removable: removable,
19
19
  size: size,
20
+ textColor: textColor,
21
+ gap: gap,
20
22
  ...props,
21
23
  children: [
22
24
  iconLeft && getIcon(iconLeft, {
23
- marginRight: 4,
24
25
  size: 'small'
25
26
  }),
26
27
  tooltip ? /*#__PURE__*/ jsx(Tooltip, {
@@ -37,19 +38,19 @@ const Tag = ({ children, id, value, iconLeft, iconRight, tooltip, removable, dis
37
38
  color: disabled ? void 0 : scale10
38
39
  }),
39
40
  iconRight && getIcon(iconRight, {
40
- marginLeft: 4,
41
41
  size: 'small'
42
42
  })
43
43
  ]
44
44
  });
45
45
  };
46
- const Item = styled.div(({ theme, disabled, invalid, rightIcon, type, removable, size })=>{
46
+ const Item = styled.div(({ theme, disabled, invalid, rightIcon, type, removable, size, textColor, gap })=>{
47
47
  const tag = theme.tag[type || 'default'];
48
48
  return {
49
49
  display: 'flex',
50
50
  alignItems: 'center',
51
51
  width: 'max-content',
52
- color: disabled || invalid ? theme.scale4 : theme.scale10,
52
+ gap,
53
+ color: disabled || invalid ? theme.scale4 : textColor || theme.scale10,
53
54
  cursor: 'default',
54
55
  fontSize: theme.font.size.sm,
55
56
  fontWeight: theme.font.weight.medium,
@@ -66,6 +67,6 @@ const Item = styled.div(({ theme, disabled, invalid, rightIcon, type, removable,
66
67
  outlineOffset: -1
67
68
  }
68
69
  };
69
- }, marginProps, paddingProps);
70
+ }, marginProps, paddingProps, widthHeightProps);
70
71
  const components_Tag = Tag;
71
72
  export { components_Tag as default };
@@ -2,14 +2,19 @@ import type { IconType } from '../helpers/getIcon';
2
2
  import type { Placement } from '../types';
3
3
  export type TagItem = {
4
4
  id: string;
5
+ /** The content of the tag if no children are provided */
5
6
  value: string;
7
+ /** Adds disabled styles and disables removal */
6
8
  disabled?: boolean;
9
+ /** Adds disabled styles but allows removal */
7
10
  invalid?: boolean;
11
+ /** Determines if the input can be removed */
8
12
  removable?: boolean;
9
13
  /** The name of the icon that should appear on the left side of the input */
10
14
  iconLeft?: IconType;
11
15
  /** The name of the icon that should appear on the right side of the input */
12
16
  iconRight?: IconType;
17
+ /** Tooltip configuration for the input */
13
18
  tooltip?: {
14
19
  content: string;
15
20
  position?: Placement;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wavv/ui",
3
- "version": "2.4.6",
3
+ "version": "2.4.8-alpha.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -31,22 +31,22 @@
31
31
  "@emotion/styled": "11.14.1",
32
32
  "@internationalized/date": "3.11.0",
33
33
  "@react-hook/resize-observer": "2.0.2",
34
- "@tiptap/core": "3.19.0",
35
- "@tiptap/extension-character-count": "3.19.0",
36
- "@tiptap/extension-highlight": "3.19.0",
37
- "@tiptap/extension-placeholder": "3.19.0",
38
- "@tiptap/extension-task-item": "3.19.0",
39
- "@tiptap/extension-task-list": "3.19.0",
40
- "@tiptap/markdown": "3.19.0",
41
- "@tiptap/pm": "3.19.0",
42
- "@tiptap/react": "3.19.0",
43
- "@tiptap/starter-kit": "3.19.0",
34
+ "@tiptap/core": "3.20.0",
35
+ "@tiptap/extension-character-count": "3.20.0",
36
+ "@tiptap/extension-highlight": "3.20.0",
37
+ "@tiptap/extension-placeholder": "3.20.0",
38
+ "@tiptap/extension-task-item": "3.20.0",
39
+ "@tiptap/extension-task-list": "3.20.0",
40
+ "@tiptap/markdown": "3.20.0",
41
+ "@tiptap/pm": "3.20.0",
42
+ "@tiptap/react": "3.20.0",
43
+ "@tiptap/starter-kit": "3.20.0",
44
44
  "cmdk": "1.1.1",
45
45
  "date-fns": "4.1.0",
46
46
  "draft-js": "0.11.7",
47
47
  "es-toolkit": "1.44.0",
48
- "libphonenumber-js": "1.12.36",
49
- "lucide-react": "0.563.0",
48
+ "libphonenumber-js": "1.12.37",
49
+ "lucide-react": "0.575.0",
50
50
  "polished": "4.3.1",
51
51
  "prism-react-renderer": "2.4.1",
52
52
  "react-aria": "3.46.0",
@@ -61,17 +61,17 @@
61
61
  "@babel/core": "^7.29.0",
62
62
  "@babel/preset-env": "^7.29.0",
63
63
  "@babel/preset-typescript": "^7.28.5",
64
- "@biomejs/biome": "2.3.14",
64
+ "@biomejs/biome": "2.4.2",
65
65
  "@chromatic-com/storybook": "^4.1.3",
66
66
  "@emotion/babel-plugin": "^11.13.5",
67
67
  "@emotion/react": "^11.14.0",
68
68
  "@rsbuild/core": "1.7.3",
69
69
  "@rsbuild/plugin-react": "^1.4.5",
70
70
  "@rsbuild/plugin-svgr": "^1.3.0",
71
- "@rslib/core": "^0.19.5",
72
- "@storybook/addon-docs": "^10.2.8",
73
- "@storybook/addon-links": "^10.2.8",
74
- "@storybook/addon-themes": "^10.2.8",
71
+ "@rslib/core": "^0.19.6",
72
+ "@storybook/addon-docs": "^10.2.10",
73
+ "@storybook/addon-links": "^10.2.10",
74
+ "@storybook/addon-themes": "^10.2.10",
75
75
  "@storybook/test-runner": "^0.24.2",
76
76
  "@svgr/core": "^8.1.0",
77
77
  "@svgr/plugin-jsx": "^8.1.0",
@@ -80,10 +80,10 @@
80
80
  "@types/draft-js": "^0.11.20",
81
81
  "@types/jest": "^30.0.0",
82
82
  "@types/ncp": "^2.0.8",
83
- "@types/node": "^25.2.2",
83
+ "@types/node": "^25.2.3",
84
84
  "@types/prompts": "^2.4.9",
85
85
  "@types/randomcolor": "^0.5.9",
86
- "@types/react": "^19.2.13",
86
+ "@types/react": "^19.2.14",
87
87
  "@types/react-dom": "^19.2.3",
88
88
  "@types/signale": "^1.4.7",
89
89
  "@types/webfontloader": "^1.6.38",
@@ -91,7 +91,7 @@
91
91
  "change-case": "^5.4.4",
92
92
  "chromatic": "^13.3.5",
93
93
  "jest": "^30.2.0",
94
- "lefthook": "^2.1.0",
94
+ "lefthook": "^2.1.1",
95
95
  "ncp": "^2.0.0",
96
96
  "path": "^0.12.7",
97
97
  "phone": "^3.1.70",
@@ -104,8 +104,8 @@
104
104
  "react-dom": "^19.2.4",
105
105
  "replace": "^1.2.2",
106
106
  "signale": "^1.4.0",
107
- "storybook": "^10.2.8",
108
- "storybook-react-rsbuild": "^3.2.2",
107
+ "storybook": "^10.2.10",
108
+ "storybook-react-rsbuild": "^3.2.3",
109
109
  "tsc-files": "^1.1.4",
110
110
  "tslib": "^2.8.1",
111
111
  "tsx": "^4.21.0",