@kushagradhawan/kookie-ui 0.1.14 → 0.1.16
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/components.css +1324 -131
- package/dist/cjs/components/dropdown-menu.js +1 -1
- package/dist/cjs/components/dropdown-menu.js.map +2 -2
- package/dist/cjs/components/icons.d.ts +2 -1
- package/dist/cjs/components/icons.d.ts.map +1 -1
- package/dist/cjs/components/icons.js +1 -1
- package/dist/cjs/components/icons.js.map +3 -3
- package/dist/cjs/components/image.d.ts +23 -2
- package/dist/cjs/components/image.d.ts.map +1 -1
- package/dist/cjs/components/image.js +1 -1
- package/dist/cjs/components/image.js.map +3 -3
- package/dist/cjs/components/sidebar.d.ts +117 -42
- package/dist/cjs/components/sidebar.d.ts.map +1 -1
- package/dist/cjs/components/sidebar.js +1 -1
- package/dist/cjs/components/sidebar.js.map +3 -3
- package/dist/cjs/components/sidebar.props.d.ts +17 -10
- package/dist/cjs/components/sidebar.props.d.ts.map +1 -1
- package/dist/cjs/components/sidebar.props.js +1 -1
- package/dist/cjs/components/sidebar.props.js.map +3 -3
- package/dist/esm/components/dropdown-menu.js +1 -1
- package/dist/esm/components/dropdown-menu.js.map +3 -3
- package/dist/esm/components/icons.d.ts +2 -1
- package/dist/esm/components/icons.d.ts.map +1 -1
- package/dist/esm/components/icons.js +1 -1
- package/dist/esm/components/icons.js.map +3 -3
- package/dist/esm/components/image.d.ts +23 -2
- package/dist/esm/components/image.d.ts.map +1 -1
- package/dist/esm/components/image.js +1 -1
- package/dist/esm/components/image.js.map +3 -3
- package/dist/esm/components/sidebar.d.ts +117 -42
- package/dist/esm/components/sidebar.d.ts.map +1 -1
- package/dist/esm/components/sidebar.js +1 -1
- package/dist/esm/components/sidebar.js.map +3 -3
- package/dist/esm/components/sidebar.props.d.ts +17 -10
- package/dist/esm/components/sidebar.props.d.ts.map +1 -1
- package/dist/esm/components/sidebar.props.js +1 -1
- package/dist/esm/components/sidebar.props.js.map +3 -3
- package/package.json +1 -1
- package/src/components/_internal/base-button.css +2 -9
- package/src/components/_internal/base-menu.css +2 -2
- package/src/components/button.css +2 -2
- package/src/components/dropdown-menu.tsx +2 -2
- package/src/components/icon-button.css +2 -2
- package/src/components/icons.tsx +18 -1
- package/src/components/image.css +5 -0
- package/src/components/image.tsx +173 -11
- package/src/components/sidebar.css +850 -54
- package/src/components/sidebar.props.tsx +15 -11
- package/src/components/sidebar.tsx +500 -367
- package/styles.css +1324 -131
package/src/components/image.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import { extractProps } from '../helpers/extract-props.js';
|
|
|
8
8
|
import { marginPropDefs } from '../props/margin.props.js';
|
|
9
9
|
import { widthPropDefs } from '../props/width.props.js';
|
|
10
10
|
import { heightPropDefs } from '../props/height.props.js';
|
|
11
|
+
import { Skeleton } from './skeleton.js';
|
|
11
12
|
|
|
12
13
|
import type { ComponentPropsWithout, RemovedProps } from '../helpers/component-props.js';
|
|
13
14
|
import type { MarginProps } from '../props/margin.props.js';
|
|
@@ -18,6 +19,27 @@ import type { GetPropDefTypes } from '../props/prop-def.js';
|
|
|
18
19
|
type ImageElement = React.ElementRef<'img'>;
|
|
19
20
|
type ImageOwnProps = GetPropDefTypes<typeof imagePropDefs> & {
|
|
20
21
|
loading?: 'eager' | 'lazy';
|
|
22
|
+
/**
|
|
23
|
+
* Placeholder image URL to show while the main image is loading.
|
|
24
|
+
* Can be a low-quality/blurred version of the main image.
|
|
25
|
+
*/
|
|
26
|
+
placeholder?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Shows a skeleton placeholder while loading instead of a blank space.
|
|
29
|
+
*/
|
|
30
|
+
showSkeleton?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Whether the image should fade in when loaded. Set to false for background images or when you want immediate visibility.
|
|
33
|
+
*/
|
|
34
|
+
fadeIn?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Callback fired when the image successfully loads.
|
|
37
|
+
*/
|
|
38
|
+
onLoad?: (event: React.SyntheticEvent<HTMLImageElement>) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Callback fired when the image fails to load.
|
|
41
|
+
*/
|
|
42
|
+
onError?: (event: React.SyntheticEvent<HTMLImageElement>) => void;
|
|
21
43
|
};
|
|
22
44
|
|
|
23
45
|
interface ImageProps
|
|
@@ -28,13 +50,13 @@ interface ImageProps
|
|
|
28
50
|
ImageOwnProps {
|
|
29
51
|
/**
|
|
30
52
|
* The alt attribute provides alternative information for an image if a user for some reason cannot view it.
|
|
31
|
-
* Required for accessibility when not using asChild.
|
|
53
|
+
* Required for accessibility when not using asChild. Use empty string for decorative images.
|
|
32
54
|
*/
|
|
33
|
-
alt
|
|
55
|
+
alt: string;
|
|
34
56
|
}
|
|
35
57
|
|
|
36
58
|
const Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) => {
|
|
37
|
-
const { variant = 'surface',
|
|
59
|
+
const { variant = 'surface', children } = props;
|
|
38
60
|
const {
|
|
39
61
|
asChild,
|
|
40
62
|
className,
|
|
@@ -43,16 +65,108 @@ const Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) =
|
|
|
43
65
|
loading = 'lazy',
|
|
44
66
|
alt,
|
|
45
67
|
src,
|
|
68
|
+
placeholder,
|
|
69
|
+
showSkeleton = false,
|
|
70
|
+
fadeIn = true,
|
|
71
|
+
onLoad: userOnLoad,
|
|
72
|
+
onError: userOnError,
|
|
46
73
|
children: _children, // Extract children to exclude from imgProps
|
|
47
74
|
...imgProps
|
|
48
75
|
} = extractProps(props, imagePropDefs, marginPropDefs, widthPropDefs, heightPropDefs);
|
|
49
76
|
|
|
77
|
+
// Loading state management
|
|
78
|
+
const [imageLoaded, setImageLoaded] = React.useState(false);
|
|
79
|
+
const [imageError, setImageError] = React.useState(false);
|
|
80
|
+
const [showPlaceholder, setShowPlaceholder] = React.useState(!!placeholder);
|
|
81
|
+
|
|
82
|
+
// Ref to check if image is already loaded (for cached images)
|
|
83
|
+
const imgRef = React.useRef<HTMLImageElement>(null);
|
|
84
|
+
|
|
85
|
+
// Handle image load - moved to top to avoid conditional hook call
|
|
86
|
+
const handleLoad = React.useCallback((event: React.SyntheticEvent<HTMLImageElement>) => {
|
|
87
|
+
setImageLoaded(true);
|
|
88
|
+
setImageError(false);
|
|
89
|
+
setShowPlaceholder(false);
|
|
90
|
+
userOnLoad?.(event);
|
|
91
|
+
}, [userOnLoad]);
|
|
92
|
+
|
|
93
|
+
// Handle image error - moved to top to avoid conditional hook call
|
|
94
|
+
const handleError = React.useCallback((event: React.SyntheticEvent<HTMLImageElement>) => {
|
|
95
|
+
setImageLoaded(false);
|
|
96
|
+
setImageError(true);
|
|
97
|
+
setShowPlaceholder(false);
|
|
98
|
+
userOnError?.(event);
|
|
99
|
+
}, [userOnError]);
|
|
100
|
+
|
|
101
|
+
// Check if image is already loaded (for cached images)
|
|
102
|
+
React.useEffect(() => {
|
|
103
|
+
const img = imgRef.current;
|
|
104
|
+
if (img && img.complete && img.naturalWidth > 0) {
|
|
105
|
+
setImageLoaded(true);
|
|
106
|
+
setImageError(false);
|
|
107
|
+
setShowPlaceholder(false);
|
|
108
|
+
}
|
|
109
|
+
}, [src]);
|
|
110
|
+
|
|
111
|
+
// Validate required props
|
|
112
|
+
if (!src) {
|
|
113
|
+
console.warn('Image component: src prop is required');
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!asChild && alt === undefined) {
|
|
118
|
+
console.warn('Image component: alt prop is required for accessibility when not using asChild');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Create skeleton placeholder
|
|
122
|
+
const skeletonElement = showSkeleton && !imageLoaded && !imageError ? (
|
|
123
|
+
<Skeleton
|
|
124
|
+
style={{
|
|
125
|
+
...style,
|
|
126
|
+
width: '100%',
|
|
127
|
+
height: '100px', // Default height, can be overridden by style
|
|
128
|
+
borderRadius: radius ? `var(--radius-${radius})` : undefined,
|
|
129
|
+
}}
|
|
130
|
+
className={className}
|
|
131
|
+
/>
|
|
132
|
+
) : null;
|
|
133
|
+
|
|
134
|
+
// Create placeholder image element
|
|
135
|
+
const placeholderElement = placeholder && showPlaceholder ? (
|
|
136
|
+
<img
|
|
137
|
+
data-radius={radius}
|
|
138
|
+
style={{
|
|
139
|
+
...style,
|
|
140
|
+
position: 'absolute',
|
|
141
|
+
top: 0,
|
|
142
|
+
left: 0,
|
|
143
|
+
width: '100%',
|
|
144
|
+
height: '100%',
|
|
145
|
+
filter: 'blur(4px)',
|
|
146
|
+
opacity: 0.7,
|
|
147
|
+
transition: 'opacity 0.3s ease-out',
|
|
148
|
+
}}
|
|
149
|
+
className={classNames(
|
|
150
|
+
'rt-reset',
|
|
151
|
+
'rt-Image',
|
|
152
|
+
'rt-Image--placeholder',
|
|
153
|
+
className,
|
|
154
|
+
)}
|
|
155
|
+
alt=""
|
|
156
|
+
src={placeholder}
|
|
157
|
+
/>
|
|
158
|
+
) : null;
|
|
159
|
+
|
|
50
160
|
// Create the standard img element
|
|
51
161
|
const imgElement = (
|
|
52
162
|
<img
|
|
53
163
|
data-radius={radius}
|
|
54
164
|
loading={loading}
|
|
55
|
-
style={
|
|
165
|
+
style={{
|
|
166
|
+
...style,
|
|
167
|
+
opacity: fadeIn ? (imageLoaded ? 1 : 0) : 1,
|
|
168
|
+
transition: fadeIn ? 'opacity 0.3s ease-out' : 'none',
|
|
169
|
+
}}
|
|
56
170
|
className={classNames(
|
|
57
171
|
'rt-reset',
|
|
58
172
|
'rt-Image',
|
|
@@ -61,11 +175,29 @@ const Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) =
|
|
|
61
175
|
)}
|
|
62
176
|
alt={alt}
|
|
63
177
|
src={src}
|
|
178
|
+
onLoad={handleLoad}
|
|
179
|
+
onError={handleError}
|
|
64
180
|
{...imgProps}
|
|
65
|
-
ref={
|
|
181
|
+
ref={(node) => {
|
|
182
|
+
imgRef.current = node;
|
|
183
|
+
if (typeof forwardedRef === 'function') {
|
|
184
|
+
forwardedRef(node);
|
|
185
|
+
} else if (forwardedRef) {
|
|
186
|
+
forwardedRef.current = node;
|
|
187
|
+
}
|
|
188
|
+
}}
|
|
66
189
|
/>
|
|
67
190
|
);
|
|
68
191
|
|
|
192
|
+
// Wrapper for images with placeholders
|
|
193
|
+
const imageWithPlaceholder = (placeholder || showSkeleton) ? (
|
|
194
|
+
<div style={{ position: 'relative', display: 'inline-block' }}>
|
|
195
|
+
{skeletonElement}
|
|
196
|
+
{placeholderElement}
|
|
197
|
+
{imgElement}
|
|
198
|
+
</div>
|
|
199
|
+
) : imgElement;
|
|
200
|
+
|
|
69
201
|
// Handle asChild - inject img into the child element
|
|
70
202
|
if (asChild && children) {
|
|
71
203
|
const child = React.Children.only(children) as React.ReactElement<any>;
|
|
@@ -113,12 +245,27 @@ const Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) =
|
|
|
113
245
|
<img
|
|
114
246
|
data-radius={radius}
|
|
115
247
|
loading={loading}
|
|
116
|
-
style={{
|
|
248
|
+
style={{
|
|
249
|
+
...style,
|
|
250
|
+
position: 'relative',
|
|
251
|
+
zIndex: 1,
|
|
252
|
+
opacity: fadeIn ? (imageLoaded ? 1 : 0) : 1,
|
|
253
|
+
transition: fadeIn ? 'opacity 0.3s ease-out' : 'none',
|
|
254
|
+
}}
|
|
117
255
|
className={classNames('rt-reset', 'rt-Image', 'rt-Image--blur', className)}
|
|
118
256
|
alt={alt}
|
|
119
257
|
src={src}
|
|
258
|
+
onLoad={handleLoad}
|
|
259
|
+
onError={handleError}
|
|
120
260
|
{...imgProps}
|
|
121
|
-
ref={
|
|
261
|
+
ref={(node) => {
|
|
262
|
+
imgRef.current = node;
|
|
263
|
+
if (typeof forwardedRef === 'function') {
|
|
264
|
+
forwardedRef(node);
|
|
265
|
+
} else if (forwardedRef) {
|
|
266
|
+
forwardedRef.current = node;
|
|
267
|
+
}
|
|
268
|
+
}}
|
|
122
269
|
/>
|
|
123
270
|
</>
|
|
124
271
|
),
|
|
@@ -137,7 +284,7 @@ const Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) =
|
|
|
137
284
|
cursor: 'pointer', // Ensure interactive cursor
|
|
138
285
|
...child.props?.style, // Allow user overrides
|
|
139
286
|
},
|
|
140
|
-
children:
|
|
287
|
+
children: imageWithPlaceholder,
|
|
141
288
|
});
|
|
142
289
|
}
|
|
143
290
|
}
|
|
@@ -171,18 +318,33 @@ const Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) =
|
|
|
171
318
|
<img
|
|
172
319
|
data-radius={radius}
|
|
173
320
|
loading={loading}
|
|
174
|
-
style={{
|
|
321
|
+
style={{
|
|
322
|
+
...style,
|
|
323
|
+
position: 'relative',
|
|
324
|
+
zIndex: 1,
|
|
325
|
+
opacity: fadeIn ? (imageLoaded ? 1 : 0) : 1,
|
|
326
|
+
transition: fadeIn ? 'opacity 0.3s ease-out' : 'none',
|
|
327
|
+
}}
|
|
175
328
|
className={classNames('rt-reset', 'rt-Image', 'rt-Image--blur', className)}
|
|
176
329
|
alt={alt}
|
|
177
330
|
src={src}
|
|
331
|
+
onLoad={handleLoad}
|
|
332
|
+
onError={handleError}
|
|
178
333
|
{...imgProps}
|
|
179
|
-
ref={
|
|
334
|
+
ref={(node) => {
|
|
335
|
+
imgRef.current = node;
|
|
336
|
+
if (typeof forwardedRef === 'function') {
|
|
337
|
+
forwardedRef(node);
|
|
338
|
+
} else if (forwardedRef) {
|
|
339
|
+
forwardedRef.current = node;
|
|
340
|
+
}
|
|
341
|
+
}}
|
|
180
342
|
/>
|
|
181
343
|
</div>
|
|
182
344
|
);
|
|
183
345
|
}
|
|
184
346
|
|
|
185
|
-
return
|
|
347
|
+
return imageWithPlaceholder;
|
|
186
348
|
});
|
|
187
349
|
|
|
188
350
|
Image.displayName = 'Image';
|