funda-ui 4.5.657 → 4.5.676

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.
@@ -11,10 +11,12 @@ export type MasonryLayoutProps = {
11
11
  columns?: number;
12
12
  gap?: number;
13
13
  breakPoints?: Record<number, number>;
14
+ balanceColumnHeights?: boolean;
14
15
  /** -- */
15
16
  id?: string;
16
17
  tabIndex?: number;
17
18
  children: React.ReactNode;
19
+ onResize?: (wrapperWidth: number) => void;
18
20
  };
19
21
 
20
22
 
@@ -23,8 +25,10 @@ const MasonryLayout = (props: MasonryLayoutProps) => {
23
25
  columns,
24
26
  gap,
25
27
  breakPoints,
28
+ balanceColumnHeights = true,
26
29
  id,
27
- children
30
+ children,
31
+ onResize
28
32
  } = props;
29
33
 
30
34
 
@@ -33,13 +37,15 @@ const MasonryLayout = (props: MasonryLayoutProps) => {
33
37
  const rootRef = useRef<any>(null);
34
38
  const itemWrapperKey = 'column-';
35
39
  const [items, setItems] = useState<React.ReactNode[]>([]);
40
+ const [orginalItems, setOrginalItems] = useState<React.ReactNode[]>([]);
36
41
  const COLS = typeof columns !== 'undefined' ? parseFloat(columns as any) : 2;
37
42
  const GAP = typeof gap !== 'undefined' ? parseFloat(gap as any) : 15;
43
+ const colsRef = useRef<Record<string, any>>(new Map());
38
44
 
39
45
  const windowResizeUpdate = debounce(handleWindowUpdate, 50);
40
46
  let windowWidth = typeof window !== 'undefined' ? window.innerWidth : 0;
41
47
 
42
- const calcInit = useCallback((w: number) => {
48
+ const calcInit = useCallback((w: number, minColIndex: number | undefined = undefined, maxColIndex: number | undefined = undefined) => {
43
49
 
44
50
  let colCount = COLS;
45
51
  const columnWrapper: any = {};
@@ -100,27 +106,52 @@ const MasonryLayout = (props: MasonryLayoutProps) => {
100
106
  items = children;
101
107
  }
102
108
 
109
+
103
110
  // get wrapper width
104
111
  const wrapperWidth = rootRef.current?.offsetWidth || 0;
105
- const perBrickWidth = wrapperWidth/colCount; // Prevent the width of the internal elements from overflowing
112
+ let perBrickWidth = wrapperWidth/colCount; // Prevent the width of the internal elements from overflowing
113
+
114
+
115
+ // return wrapper width
116
+ onResize?.(wrapperWidth);
106
117
 
107
118
 
108
- items.forEach((el: React.ReactNode, i: number) => {
119
+ //
120
+ React.Children.forEach(children, (child: any, i: number) => {
121
+ if (!child) return;
122
+
109
123
  const columnIndex = i % colCount;
110
-
124
+ const itemRow = Math.floor(i / colCount);
125
+ const itemIndex = itemRow * colCount + columnIndex;
126
+
127
+ //
111
128
  columnWrapper[`${itemWrapperKey}${columnIndex}`].push(
112
129
  <div
113
130
  key={i}
131
+ data-row={itemRow}
132
+ data-col={columnIndex}
133
+ data-index={itemIndex}
114
134
  style={{
115
135
  marginBottom: `${GAP}px`
116
136
  }}
117
137
  >
118
- <div style={perBrickWidth > 0 ? {width: perBrickWidth + 'px'} : undefined}>{el}</div>
138
+ <div style={perBrickWidth > 0 ? {width: perBrickWidth + 'px'} : undefined}>{child}</div>
119
139
  </div>
120
140
  );
121
141
  });
122
142
 
123
143
 
144
+ // Add the item to the shortest column
145
+ if (
146
+ balanceColumnHeights &&
147
+ (typeof minColIndex !== 'undefined' && typeof maxColIndex !== 'undefined') &&
148
+ items.length > COLS
149
+ ) {
150
+ const maxColLastElement = columnWrapper[`${itemWrapperKey}${maxColIndex}`].pop();
151
+ columnWrapper[`${itemWrapperKey}${minColIndex}`].push(maxColLastElement);
152
+ }
153
+
154
+
124
155
  // STEP 5:
125
156
  //=================
126
157
  // Wrapping the items in each column with a div and pushing it into the result array
@@ -133,15 +164,35 @@ const MasonryLayout = (props: MasonryLayoutProps) => {
133
164
  marginLeft: `${i > 0 ? GAP : 0}px`,
134
165
  flex: '0 1 auto'
135
166
  }}>
136
- {columnWrapper[`${itemWrapperKey}${i}`]}
167
+ <div
168
+ className="masonry-item-inner"
169
+ data-inner-col={i}
170
+ ref={el => {
171
+ if (el) {
172
+ colsRef.current.set(`col-${i}`, el);
173
+ } else {
174
+ colsRef.current.delete(`col-${i}`);
175
+ }
176
+ }}
177
+ >
178
+ {columnWrapper[`${itemWrapperKey}${i}`]}
179
+ </div>
137
180
  </div>
138
181
  );
139
182
  }
140
183
 
141
184
  // STEP 6:
142
185
  //=================
186
+ // update items
143
187
  setItems(result);
188
+
189
+ // update orginal items
190
+ if (typeof minColIndex === 'undefined' && typeof maxColIndex === 'undefined') {
191
+ setOrginalItems(result);
192
+ }
193
+
144
194
 
195
+
145
196
  }, [children]);
146
197
 
147
198
 
@@ -160,6 +211,73 @@ const MasonryLayout = (props: MasonryLayoutProps) => {
160
211
  }
161
212
  }
162
213
 
214
+ function adjustPosition() {
215
+ if (rootRef.current === null) return;
216
+
217
+ // Adjust the position of the element
218
+ const initCols = () => {
219
+
220
+ const columnHeights = new Array(COLS).fill(0);
221
+ React.Children.forEach(items, (child: any, i: number) => {
222
+ if (!child) return;
223
+
224
+ const columnIndex = i % COLS;
225
+
226
+ // update column height
227
+ const columnInner = colsRef.current.get(`col-${columnIndex}`);
228
+ if (columnInner) {
229
+ const height = columnInner.offsetHeight;
230
+ columnHeights[columnIndex] = height;
231
+ }
232
+
233
+ });
234
+
235
+ // Find the shortest height column
236
+ const minHeight = Math.min(...columnHeights);
237
+ const maxHeight = Math.max(...columnHeights);
238
+ if (minHeight > 0 && maxHeight > 0 && maxHeight/2 > minHeight) {
239
+ const columnMinHeightIndex = columnHeights.indexOf(minHeight);
240
+ const columnMaxHeightIndex = columnHeights.indexOf(maxHeight);
241
+ calcInit(windowWidth, columnMinHeightIndex, columnMaxHeightIndex);
242
+ }
243
+
244
+
245
+ };
246
+
247
+ const images: NodeListOf<HTMLImageElement> = rootRef.current.querySelectorAll('img');
248
+ const imagePromises: Promise<void>[] = [];
249
+ images.forEach((img: HTMLImageElement) => {
250
+ const imgPromise: Promise<void> = new Promise((resolve, reject) => {
251
+ if (img.complete) {
252
+ resolve();
253
+ } else {
254
+ img.onload = () => resolve();
255
+ img.onerror = () => resolve();
256
+ }
257
+ });
258
+ imagePromises.push(imgPromise);
259
+ });
260
+
261
+ // Wait for all images to load
262
+ if (images.length > 0) {
263
+ Promise.all(imagePromises)
264
+ .then(() => {
265
+ initCols();
266
+ })
267
+ .catch((error: Error) => {
268
+ console.error(error);
269
+ });
270
+ } else {
271
+ initCols();
272
+ }
273
+ }
274
+
275
+
276
+ useEffect(() => {
277
+ adjustPosition();
278
+ }, [orginalItems]);
279
+
280
+
163
281
  useEffect(() => {
164
282
 
165
283
  // Initialize items
@@ -50,6 +50,8 @@ export type TextareaProps = {
50
50
  onBlur?: (e: any, el: any) => void;
51
51
  onFocus?: (e: any, el: any) => void;
52
52
  onPressEnter?: (e: any, el: any) => void;
53
+ onKeyDown?: (e: any, el: any) => void;
54
+ onKeyUp?: (e: any, el: any) => void;
53
55
  onResize?: (el: any, params: number[]) => void;
54
56
 
55
57
  };
@@ -96,6 +98,8 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
96
98
  onBlur,
97
99
  onFocus,
98
100
  onPressEnter,
101
+ onKeyDown,
102
+ onKeyUp,
99
103
  onResize,
100
104
  ...attributes
101
105
  } = props;
@@ -363,6 +367,10 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
363
367
  }
364
368
 
365
369
  function handleKeyPressed(event: KeyboardEvent<HTMLTextAreaElement>) {
370
+
371
+ onKeyDown?.(event, valRef.current);
372
+
373
+
366
374
  if (typeof (onKeyPressedCallback) === 'function') {
367
375
  const newData: any = onKeyPressedCallback(event, valRef.current);
368
376
  if (newData) setChangedVal(newData); // Avoid the error "react checkbox changing an uncontrolled input to be controlled"
@@ -418,6 +426,9 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
418
426
 
419
427
  }
420
428
 
429
+ function handleKeyUp(event: KeyboardEvent<HTMLTextAreaElement>) {
430
+ onKeyUp?.(event, valRef.current);
431
+ }
421
432
 
422
433
  useEffect(() => {
423
434
 
@@ -534,6 +545,7 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
534
545
  }
535
546
  }}
536
547
  onKeyDown={handleKeyPressed}
548
+ onKeyUp={handleKeyUp}
537
549
  disabled={disabled || null}
538
550
  required={required || null}
539
551
  readOnly={readOnly || null}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "UIUX Lab",
3
3
  "email": "uiuxlab@gmail.com",
4
4
  "name": "funda-ui",
5
- "version": "4.5.657",
5
+ "version": "4.5.676",
6
6
  "description": "React components using pure Bootstrap 5+ which does not contain any external style and script libraries.",
7
7
  "repository": {
8
8
  "type": "git",