@ultraviolet/ui 1.51.0 → 1.51.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.
package/dist/index.d.ts CHANGED
@@ -1334,7 +1334,7 @@ type AvatarProps = {
1334
1334
  declare const Avatar: ({ image, size, text, textBgColor, textColor, textSize, lock, className, "data-testid": dataTestId, }: AvatarProps) => _emotion_react_jsx_runtime.JSX.Element;
1335
1335
 
1336
1336
  type IconName$1 = ComponentProps<typeof Icon>['name'];
1337
- declare const SIZES$1: {
1337
+ declare const SIZES: {
1338
1338
  large: number;
1339
1339
  medium: number;
1340
1340
  small: number;
@@ -1345,7 +1345,7 @@ declare const PROMINENCES$3: {
1345
1345
  };
1346
1346
  type BadgeProps = {
1347
1347
  sentiment?: Color;
1348
- size?: keyof typeof SIZES$1;
1348
+ size?: keyof typeof SIZES;
1349
1349
  prominence?: keyof typeof PROMINENCES$3;
1350
1350
  /**
1351
1351
  * Defines icon to display on left side of badge. **Only available on medium and large sizes**.
@@ -2034,7 +2034,7 @@ declare const arrowPlacementStyles: {
2034
2034
  readonly 'top-start': (theme: Theme) => _emotion_react.SerializedStyles;
2035
2035
  };
2036
2036
  type ArrowPlacement = keyof typeof arrowPlacementStyles;
2037
- type MenuProps$1 = {
2037
+ type MenuProps = {
2038
2038
  ariaLabel?: string;
2039
2039
  id?: string;
2040
2040
  placement?: ArrowPlacement;
@@ -2052,7 +2052,7 @@ type MenuProps$1 = {
2052
2052
  * When a user activates a choice in a menu, the menu usually closes unless the choice opened a submenu.
2053
2053
  * @deprecated use MenuV2 component instead
2054
2054
  */
2055
- declare const Menu: react.ForwardRefExoticComponent<MenuProps$1 & react.RefAttributes<HTMLButtonElement>> & {
2055
+ declare const Menu: react.ForwardRefExoticComponent<MenuProps & react.RefAttributes<HTMLButtonElement>> & {
2056
2056
  Item: react.ForwardRefExoticComponent<{
2057
2057
  href?: string | undefined;
2058
2058
  disabled?: boolean | undefined;
@@ -2358,7 +2358,8 @@ declare const Popover: react.ForwardRefExoticComponent<{
2358
2358
  maxHeight?: string | number | undefined;
2359
2359
  disableAnimation?: boolean | undefined;
2360
2360
  portalTarget?: HTMLElement | undefined;
2361
- } & react.RefAttributes<HTMLDivElement>, "placement"> & react.RefAttributes<HTMLDivElement>>;
2361
+ dynamicDomRendering?: boolean | undefined;
2362
+ } & react.RefAttributes<HTMLDivElement>, "placement" | "dynamicDomRendering"> & react.RefAttributes<HTMLDivElement>>;
2362
2363
 
2363
2364
  type PopupProps = {
2364
2365
  /**
@@ -2418,6 +2419,12 @@ type PopupProps = {
2418
2419
  * behavior by setting a portalTarget prop.
2419
2420
  */
2420
2421
  portalTarget?: HTMLElement;
2422
+ /**
2423
+ * If you set this to true, the popup will be rendered in the DOM only when it is visible.
2424
+ * When set to false, the popup will always be rendered in the DOM. By default this value is set to `true`, if for some
2425
+ * reason you need to disable it, you can set it to `false`.
2426
+ */
2427
+ dynamicDomRendering?: boolean;
2421
2428
  };
2422
2429
  /**
2423
2430
  * @experimental This component is experimental and may be subject to breaking changes in the future.
@@ -3364,11 +3371,6 @@ declare const RadioGroup: {
3364
3371
  Radio: ({ onFocus, onBlur, disabled, error, name, value, label, helper, className, autoFocus, onKeyDown, "data-testid": dataTestId, }: RadioGroupRadioProps) => _emotion_react_jsx_runtime.JSX.Element;
3365
3372
  };
3366
3373
 
3367
- declare const SIZES: {
3368
- small: string;
3369
- medium: string;
3370
- large: string;
3371
- };
3372
3374
  type DisclosureProps = {
3373
3375
  visible: boolean;
3374
3376
  };
@@ -3378,40 +3380,68 @@ type DisclosureElement = ((disclosure: DisclosureProps) => ReactElement<ButtonHT
3378
3380
  type ChildMenuProps = {
3379
3381
  toggle: () => void;
3380
3382
  };
3381
- type MenuProps = {
3382
- id?: string;
3383
- ariaLabel?: string;
3384
- placement?: ComponentProps<typeof Popup>['placement'];
3383
+ /**
3384
+ * A menu is a widget that offers a list of choices to the user, such as a set of actions or functions.
3385
+ * A menu is usually opened, or made visible, by activating a menu button, choosing an item in a menu that opens a
3386
+ * sub menu, or by invoking a command, such as `Shift + F10` on Windows, that opens a context specific menu.
3387
+ * When a user activates a choice in a menu, the menu usually closes unless the choice opened a submenu.
3388
+ */
3389
+ declare const MenuV2: react.ForwardRefExoticComponent<{
3390
+ id?: string | undefined;
3391
+ ariaLabel?: string | undefined;
3385
3392
  children?: ReactNode | (({ toggle }: ChildMenuProps) => ReactNode);
3386
- className?: string;
3393
+ className?: string | undefined;
3387
3394
  disclosure: DisclosureElement;
3388
- hasArrow?: boolean;
3389
- visible?: boolean;
3390
- 'data-testid'?: string;
3391
- maxHeight?: string;
3395
+ hasArrow?: boolean | undefined;
3396
+ visible?: boolean | undefined;
3397
+ 'data-testid'?: string | undefined;
3398
+ maxHeight?: string | undefined;
3392
3399
  /**
3393
3400
  * @deprecated: use `size` instead
3394
3401
  */
3395
- maxWidth?: string;
3402
+ maxWidth?: string | undefined;
3396
3403
  /**
3397
3404
  * By default, the portal target is children container or document.body if children is a function. You can override this
3398
3405
  * behavior by setting a portalTarget prop.
3399
3406
  */
3400
- portalTarget?: HTMLElement;
3401
- size?: keyof typeof SIZES;
3407
+ portalTarget?: HTMLElement | undefined;
3408
+ size?: "large" | "small" | "medium" | undefined;
3402
3409
  /**
3403
3410
  * The behavior of the menu when it is opened. If set to `click`, the menu will open when the user clicks on the disclosure.
3404
3411
  * If set to `hover`, the menu will open when the user hovers over the disclosure.
3405
3412
  */
3406
- triggerMethod?: 'click' | 'hover';
3407
- };
3408
- /**
3409
- * A menu is a widget that offers a list of choices to the user, such as a set of actions or functions.
3410
- * A menu is usually opened, or made visible, by activating a menu button, choosing an item in a menu that opens a
3411
- * sub menu, or by invoking a command, such as `Shift + F10` on Windows, that opens a context specific menu.
3412
- * When a user activates a choice in a menu, the menu usually closes unless the choice opened a submenu.
3413
- */
3414
- declare const MenuV2: react.ForwardRefExoticComponent<MenuProps & react.RefAttributes<HTMLButtonElement>> & {
3413
+ triggerMethod?: "click" | "hover" | undefined;
3414
+ } & Pick<{
3415
+ id?: string | undefined;
3416
+ children: ReactNode | ((renderProps: {
3417
+ className?: string | undefined;
3418
+ onBlur: () => void;
3419
+ onFocus: () => void;
3420
+ onPointerEnter: () => void;
3421
+ onPointerLeave: () => void;
3422
+ ref: react.RefObject<HTMLDivElement>;
3423
+ }) => ReactNode);
3424
+ maxWidth?: string | number | undefined;
3425
+ placement?: PopupPlacement | undefined;
3426
+ text?: ReactNode;
3427
+ className?: string | undefined;
3428
+ containerFullWidth?: boolean | undefined;
3429
+ visible?: boolean | undefined;
3430
+ innerRef?: Ref<HTMLDivElement | null> | undefined;
3431
+ role?: string | undefined;
3432
+ 'data-testid'?: string | undefined;
3433
+ hasArrow?: boolean | undefined;
3434
+ onClose?: (() => void) | undefined;
3435
+ tabIndex?: number | undefined;
3436
+ onKeyDown?: react.KeyboardEventHandler | undefined;
3437
+ 'aria-haspopup'?: boolean | "menu" | "dialog" | "grid" | "listbox" | "tree" | "false" | "true" | undefined;
3438
+ hideOnClickOutside?: boolean | undefined;
3439
+ debounceDelay?: number | undefined;
3440
+ maxHeight?: string | number | undefined;
3441
+ disableAnimation?: boolean | undefined;
3442
+ portalTarget?: HTMLElement | undefined;
3443
+ dynamicDomRendering?: boolean | undefined;
3444
+ } & react.RefAttributes<HTMLDivElement>, "placement" | "dynamicDomRendering"> & react.RefAttributes<HTMLButtonElement>> & {
3415
3445
  Item: react.ForwardRefExoticComponent<{
3416
3446
  href?: string | undefined;
3417
3447
  disabled?: boolean | undefined;
@@ -48,7 +48,8 @@ const FwdMenu = /*#__PURE__*/forwardRef(({
48
48
  maxWidth,
49
49
  portalTarget,
50
50
  size = 'small',
51
- triggerMethod = 'click'
51
+ triggerMethod = 'click',
52
+ dynamicDomRendering
52
53
  }, ref) => {
53
54
  const [isVisible, setIsVisible] = useState(visible);
54
55
  const popupRef = useRef(null);
@@ -98,6 +99,7 @@ const FwdMenu = /*#__PURE__*/forwardRef(({
98
99
  }) : children
99
100
  }),
100
101
  portalTarget: portalTarget,
102
+ dynamicDomRendering: dynamicDomRendering,
101
103
  children: finalDisclosure
102
104
  });
103
105
  });
@@ -101,7 +101,8 @@ const Popover = /*#__PURE__*/forwardRef(({
101
101
  maxWidth,
102
102
  maxHeight,
103
103
  'data-testid': dataTestId,
104
- portalTarget
104
+ portalTarget,
105
+ dynamicDomRendering
105
106
  }, ref) => {
106
107
  const innerRef = useRef(null);
107
108
  const [localVisible, setLocalVisible] = useState(visible);
@@ -137,6 +138,7 @@ const Popover = /*#__PURE__*/forwardRef(({
137
138
  maxWidth: maxWidth,
138
139
  maxHeight: maxHeight,
139
140
  portalTarget: portalTarget,
141
+ dynamicDomRendering: dynamicDomRendering,
140
142
  children: children
141
143
  });
142
144
  });
@@ -2,13 +2,13 @@ const ARROW_WIDTH = 8; // in px
2
2
  const SPACE = 4; // in px
3
3
  const TOTAL_USED_SPACE = 0; // in px
4
4
  const DEFAULT_POSITIONS = {
5
- arrowLeft: -999,
6
- arrowTop: -999,
5
+ arrowLeft: 0,
6
+ arrowTop: 0,
7
7
  arrowTransform: 'translate(-50%, -50)',
8
8
  placement: 'top',
9
9
  rotate: 135,
10
- popupInitialPosition: 'translate3d(-999px, -999px, 0)',
11
- popupPosition: 'translate3d(-999px, -999px, 0)'
10
+ popupInitialPosition: 'translate3d(0px, 0px, 0)',
11
+ popupPosition: 'translate3d(0px, 0px, 0)'
12
12
  };
13
13
  /**
14
14
  * This function will find the best placement in a window for popup based on children position and popup size
@@ -28,7 +28,7 @@ const StyledPopup = /*#__PURE__*/_styled('div', {
28
28
  maxHeight
29
29
  }) => typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight, ";overflow:", ({
30
30
  maxHeight
31
- }) => maxHeight ? 'auto' : undefined, ";overflow-wrap:break-word;font-size:0.8rem;inset:0 auto auto 0;top:0;left:0;z-index:1;transform:", ({
31
+ }) => maxHeight ? 'auto' : undefined, ";overflow-wrap:break-word;font-size:0.8rem;inset:0 auto auto 0;top:0;left:0;opacity:0;z-index:1;transform:", ({
32
32
  positions
33
33
  }) => positions.popupPosition, ";animation:", ({
34
34
  positions,
@@ -45,7 +45,7 @@ const StyledPopup = /*#__PURE__*/_styled('div', {
45
45
  positions
46
46
  }) => positions.rotate, "deg);margin-left:-", ARROW_WIDTH, "px;border-width:", ARROW_WIDTH, "px;border-style:solid;border-color:", ({
47
47
  theme
48
- }) => theme.colors.neutral.backgroundStronger, " transparent transparent transparent;pointer-events:none;}}");
48
+ }) => theme.colors.neutral.backgroundStronger, " transparent transparent transparent;pointer-events:none;}}&[data-visible-in-dom='false']{display:none;}");
49
49
  const StyledChildrenContainer = /*#__PURE__*/_styled("div", {
50
50
  target: "e4h1g860"
51
51
  })(process.env.NODE_ENV === "production" ? {
@@ -80,7 +80,8 @@ const Popup = /*#__PURE__*/forwardRef(({
80
80
  hideOnClickOutside = false,
81
81
  debounceDelay = DEFAULT_DEBOUNCE_DURATION,
82
82
  disableAnimation = false,
83
- portalTarget
83
+ portalTarget,
84
+ dynamicDomRendering = true
84
85
  }, ref) => {
85
86
  const childrenRef = useRef(null);
86
87
  useImperativeHandle(innerRef, () => childrenRef.current);
@@ -226,6 +227,15 @@ const Popup = /*#__PURE__*/forwardRef(({
226
227
  };
227
228
  }, [generatePopupPositions, onWindowChangeDetected, visibleInDom, maxWidth, popupPortalTarget]);
228
229
 
230
+ // This will be triggered when positions are computed and popup is visible in the dom.
231
+ useEffect(() => {
232
+ if (visibleInDom && innerPopupRef.current) {
233
+ innerPopupRef.current.style.opacity = '1';
234
+ }
235
+ },
236
+ // eslint-disable-next-line react-hooks/exhaustive-deps
237
+ [positions]);
238
+
229
239
  /**
230
240
  * If popup has `visible` prop it means the popup is manually controlled through this prop.
231
241
  * In this cas we don't want to display popup on hover, but only when `visible` is true.
@@ -324,6 +334,15 @@ const Popup = /*#__PURE__*/forwardRef(({
324
334
  children: children
325
335
  });
326
336
  }, [ariaHasPopup, children, containerFullWidth, generatedId, isControlled, onKeyDown, onPointerEvent, tabIndex]);
337
+ const shouldRender = useMemo(() => {
338
+ if (!dynamicDomRendering) {
339
+ return true;
340
+ }
341
+ if (dynamicDomRendering && visibleInDom) {
342
+ return true;
343
+ }
344
+ return false;
345
+ }, [dynamicDomRendering, visibleInDom]);
327
346
  if (!text) {
328
347
  if (typeof children === 'function') return null;
329
348
  return jsx(Fragment, {
@@ -338,7 +357,7 @@ const Popup = /*#__PURE__*/forwardRef(({
338
357
  event.nativeEvent.stopImmediatePropagation();
339
358
  };
340
359
  return jsxs(Fragment, {
341
- children: [renderChildren(), visibleInDom ? /*#__PURE__*/createPortal(jsx(StyledPopup, {
360
+ children: [renderChildren(), shouldRender ? /*#__PURE__*/createPortal(jsx(StyledPopup, {
342
361
  ref: innerPopupRef,
343
362
  positions: positions,
344
363
  maxWidth: maxWidth,
@@ -355,6 +374,7 @@ const Popup = /*#__PURE__*/forwardRef(({
355
374
  animationDuration: animationDuration,
356
375
  onKeyDown: role === 'dialog' ? handleFocusTrap : undefined,
357
376
  isDialog: role === 'dialog',
377
+ "data-visible-in-dom": !dynamicDomRendering ? visibleInDom : undefined,
358
378
  children: text
359
379
  }), popupPortalTarget) : null]
360
380
  });
@@ -1,6 +1,6 @@
1
1
  import _styled from '@emotion/styled/base';
2
2
  import { Icon } from '@ultraviolet/icons';
3
- import { useRef, useEffect } from 'react';
3
+ import { useRef } from 'react';
4
4
  import { TextInputV2 } from '../TextInputV2/index.js';
5
5
  import { useSelectInput } from './SelectInputProvider.js';
6
6
  import { jsx } from '@emotion/react/jsx-runtime';
@@ -99,13 +99,6 @@ const SearchBarDropdown = ({
99
99
  }
100
100
  }
101
101
  };
102
- useEffect(() => {
103
- // TODO: Remove me and use autoFocus when popup is fixed
104
- // Explanation : Actually the component render at -999px -999px then it will be placed according to child position and it broke the autoFocus (scroll -999px to top)
105
- setTimeout(() => {
106
- searchInputRef.current?.focus();
107
- }, 50);
108
- }, []);
109
102
  return jsx(StyledInput, {
110
103
  value: searchInput,
111
104
  onChange: event => handleChange(event),
@@ -120,6 +113,7 @@ const SearchBarDropdown = ({
120
113
  }),
121
114
  onKeyDown: event => handleKeyDown(event.key, searchInput),
122
115
  size: "medium",
116
+ autoFocus: true,
123
117
  "aria-label": "search-bar",
124
118
  ref: searchInputRef
125
119
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultraviolet/ui",
3
- "version": "1.51.0",
3
+ "version": "1.51.1",
4
4
  "description": "Ultraviolet UI",
5
5
  "homepage": "https://github.com/scaleway/ultraviolet#readme",
6
6
  "repository": {
@@ -35,19 +35,19 @@
35
35
  "peerDependencies": {
36
36
  "@emotion/react": "11.11.4",
37
37
  "@emotion/styled": "11.11.5",
38
- "react": "18.2.0",
39
- "react-dom": "18.2.0"
38
+ "react": "18.3.1",
39
+ "react-dom": "18.3.1"
40
40
  },
41
41
  "devDependencies": {
42
- "@babel/core": "7.24.4",
42
+ "@babel/core": "7.24.5",
43
43
  "@emotion/babel-plugin": "11.11.0",
44
44
  "@emotion/react": "11.11.4",
45
45
  "@emotion/styled": "11.11.5",
46
- "@types/react": "18.2.79",
46
+ "@types/react": "18.3.1",
47
47
  "@types/react-datepicker": "4.19.6",
48
- "@types/react-dom": "18.2.25",
49
- "react": "18.2.0",
50
- "react-dom": "18.2.0"
48
+ "@types/react-dom": "18.3.0",
49
+ "react": "18.3.1",
50
+ "react-dom": "18.3.1"
51
51
  },
52
52
  "dependencies": {
53
53
  "@emotion/serialize": "1.1.4",