@graphprotocol/gds-react 0.2.2 → 0.2.4

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 (66) hide show
  1. package/dist/components/Address.d.ts +6 -4
  2. package/dist/components/Address.d.ts.map +1 -1
  3. package/dist/components/Address.js.map +1 -1
  4. package/dist/components/Avatar.d.ts +6 -4
  5. package/dist/components/Avatar.d.ts.map +1 -1
  6. package/dist/components/Avatar.js.map +1 -1
  7. package/dist/components/Breadcrumbs.parts.d.ts +6 -4
  8. package/dist/components/Breadcrumbs.parts.d.ts.map +1 -1
  9. package/dist/components/Breadcrumbs.parts.js.map +1 -1
  10. package/dist/components/Button.d.ts +6 -4
  11. package/dist/components/Button.d.ts.map +1 -1
  12. package/dist/components/Button.js.map +1 -1
  13. package/dist/components/Card.d.ts +3 -5
  14. package/dist/components/Card.d.ts.map +1 -1
  15. package/dist/components/Chip.parts.d.ts +10 -6
  16. package/dist/components/Chip.parts.d.ts.map +1 -1
  17. package/dist/components/Chip.parts.js.map +1 -1
  18. package/dist/components/Cluster.d.ts +3 -5
  19. package/dist/components/Cluster.d.ts.map +1 -1
  20. package/dist/components/Cluster.js.map +1 -1
  21. package/dist/components/Icon.d.ts +2 -1
  22. package/dist/components/Icon.d.ts.map +1 -1
  23. package/dist/components/Icon.js.map +1 -1
  24. package/dist/components/Link.d.ts +7 -5
  25. package/dist/components/Link.d.ts.map +1 -1
  26. package/dist/components/Link.js.map +1 -1
  27. package/dist/components/Menu.parts.d.ts +23 -15
  28. package/dist/components/Menu.parts.d.ts.map +1 -1
  29. package/dist/components/Menu.parts.js +112 -106
  30. package/dist/components/Menu.parts.js.map +1 -1
  31. package/dist/components/Modal.parts.d.ts.map +1 -1
  32. package/dist/components/Modal.parts.js +2 -2
  33. package/dist/components/Modal.parts.js.map +1 -1
  34. package/dist/components/Pane.parts.d.ts +3 -3
  35. package/dist/components/Pane.parts.d.ts.map +1 -1
  36. package/dist/components/Pane.parts.js +8 -5
  37. package/dist/components/Pane.parts.js.map +1 -1
  38. package/dist/components/Tooltip.parts.d.ts.map +1 -1
  39. package/dist/components/Tooltip.parts.js +7 -6
  40. package/dist/components/Tooltip.parts.js.map +1 -1
  41. package/dist/components/base/ButtonOrLink.parts.d.ts +3 -3
  42. package/dist/components/base/ButtonOrLink.parts.d.ts.map +1 -1
  43. package/dist/components/base/ButtonOrLink.parts.js +3 -3
  44. package/dist/components/base/ButtonOrLink.parts.js.map +1 -1
  45. package/dist/components/base/MaybeButtonOrLink.d.ts +2 -2
  46. package/dist/components/base/MaybeButtonOrLink.d.ts.map +1 -1
  47. package/dist/components/index.d.ts +1 -1
  48. package/dist/components/index.d.ts.map +1 -1
  49. package/dist/components/index.js.map +1 -1
  50. package/package.json +17 -17
  51. package/src/components/Address.tsx +8 -4
  52. package/src/components/Avatar.tsx +5 -4
  53. package/src/components/Breadcrumbs.parts.tsx +5 -4
  54. package/src/components/Button.tsx +5 -4
  55. package/src/components/Card.tsx +4 -4
  56. package/src/components/Chip.parts.tsx +8 -7
  57. package/src/components/Cluster.tsx +6 -4
  58. package/src/components/Icon.tsx +3 -1
  59. package/src/components/Link.tsx +5 -5
  60. package/src/components/Menu.parts.tsx +59 -27
  61. package/src/components/Modal.parts.tsx +3 -2
  62. package/src/components/Pane.parts.tsx +16 -8
  63. package/src/components/Tooltip.parts.tsx +18 -15
  64. package/src/components/base/ButtonOrLink.parts.tsx +9 -12
  65. package/src/components/base/MaybeButtonOrLink.tsx +2 -2
  66. package/src/components/index.ts +1 -1
@@ -17,7 +17,7 @@ import { ChipGroupMeta, ChipMeta } from './Chip.meta.ts'
17
17
  type ExtractFn<T> = T extends (...args: infer A) => infer R ? (...args: A) => R : never
18
18
 
19
19
  export declare namespace ChipProps {
20
- interface BaseProps extends GDSComponentProps<typeof ChipMeta> {
20
+ interface OwnProps {
21
21
  /** Name used to submit the chip's value when inside a `<Chip.Group type="checkbox">` in a form. */
22
22
  name?: string | undefined
23
23
  /** Value used to identify this chip when inside a `<Chip.Group>`. */
@@ -26,13 +26,14 @@ export declare namespace ChipProps {
26
26
  addonAfter?: AddonValue | undefined
27
27
  count?: ReactNode | undefined
28
28
  }
29
+ interface CommonProps extends OwnProps, GDSComponentProps<typeof ChipMeta> {}
29
30
  interface ButtonProps
30
31
  extends
31
- BaseProps,
32
+ CommonProps,
32
33
  Omit<ButtonOrLinkProps.ButtonProps, 'value'>,
33
34
  ButtonOrLinkProps.DisableableProps {}
34
35
  interface LinkProps
35
- extends BaseProps, ButtonOrLinkProps.LinkProps, ButtonOrLinkProps.DisableableProps {}
36
+ extends CommonProps, ButtonOrLinkProps.LinkProps, ButtonOrLinkProps.DisableableProps {}
36
37
  }
37
38
 
38
39
  export type ChipProps = ChipProps.ButtonProps | ChipProps.LinkProps
@@ -222,9 +223,9 @@ function ChipAddon({
222
223
  }
223
224
 
224
225
  export declare namespace ChipGroupProps {
225
- interface BaseProps
226
- extends Omit<ComponentProps<'div'>, 'onChange'>, GDSComponentProps<typeof ChipGroupMeta> {}
227
- interface CheckboxProps extends BaseProps {
226
+ interface HTMLProps extends Omit<ComponentProps<'div'>, 'onChange'> {}
227
+ interface CommonProps extends HTMLProps, GDSComponentProps<typeof ChipGroupMeta> {}
228
+ interface CheckboxProps extends CommonProps {
228
229
  type: 'checkbox'
229
230
  name?: undefined
230
231
  /** Array of checked/selected chip values. */
@@ -232,7 +233,7 @@ export declare namespace ChipGroupProps {
232
233
  defaultValue?: string[] | undefined
233
234
  onChange?: ((value: string[]) => void) | undefined
234
235
  }
235
- interface RadioProps extends BaseProps {
236
+ interface RadioProps extends CommonProps {
236
237
  type: 'radio'
237
238
  /** Name used to submit the checked/selected chip value when inside a form. */
238
239
  name?: string | undefined
@@ -8,10 +8,12 @@ import { MaybeButtonOrLink, type MaybeButtonOrLinkProps } from './base/MaybeButt
8
8
  import { ClusterMeta } from './Cluster.meta.ts'
9
9
 
10
10
  export declare namespace ClusterProps {
11
- interface BaseProps extends GDSComponentProps<typeof ClusterMeta> {}
12
- interface ButtonProps extends BaseProps, MaybeButtonOrLinkProps.ButtonProps {}
13
- interface LinkProps extends BaseProps, MaybeButtonOrLinkProps.LinkProps {}
14
- interface OtherElementProps extends BaseProps, MaybeButtonOrLinkProps.OtherElementProps {}
11
+ interface ButtonProps
12
+ extends GDSComponentProps<typeof ClusterMeta>, MaybeButtonOrLinkProps.ButtonProps {}
13
+ interface LinkProps
14
+ extends GDSComponentProps<typeof ClusterMeta>, MaybeButtonOrLinkProps.LinkProps {}
15
+ interface OtherElementProps
16
+ extends GDSComponentProps<typeof ClusterMeta>, MaybeButtonOrLinkProps.OtherElementProps {}
15
17
  }
16
18
 
17
19
  export type ClusterProps =
@@ -27,8 +27,10 @@ export interface IconProps
27
27
  alt: string | undefined
28
28
  }
29
29
 
30
+ export type IconSrc = ReactElement | ComponentType
31
+
30
32
  export interface IconPropsWithSrc extends IconProps {
31
- src: ReactElement | ComponentType
33
+ src: IconSrc
32
34
  }
33
35
 
34
36
  export function Icon({ src, alt, size, color, className, style, ...props }: IconPropsWithSrc) {
@@ -2,6 +2,7 @@
2
2
 
3
3
  import type { ComponentProps, CSSProperties } from 'react'
4
4
 
5
+ import type { GDSComponentProps } from '@graphprotocol/gds-css'
5
6
  import { ArrowUpRightInteractiveIcon } from '@graphprotocol/gds-react/icons'
6
7
 
7
8
  import { useCSSPropsPolyfill, useCSSState, useGDS } from '../hooks/index.ts'
@@ -11,23 +12,22 @@ import { ButtonOrLink, type ButtonOrLinkProps } from './base/ButtonOrLink.tsx'
11
12
  import { LinkMeta } from './Link.meta.ts'
12
13
 
13
14
  export declare namespace LinkProps {
14
- interface BaseProps {
15
- /** @default 'primary' */
16
- variant?: 'primary' | 'secondary' | undefined
15
+ interface OwnProps {
17
16
  addonBefore?: AddonValue | undefined
18
17
  /** Defaults to `ArrowUpRightInteractiveIcon` if `href` is defined and `target` is `_blank` */
19
18
  addonAfter?: AddonValue | undefined
20
19
  }
20
+ interface CommonProps extends OwnProps, GDSComponentProps<typeof LinkMeta> {}
21
21
  interface ButtonProps
22
22
  extends
23
- BaseProps,
23
+ CommonProps,
24
24
  Omit<ButtonOrLinkProps.ButtonProps, 'href'>,
25
25
  ButtonOrLinkProps.DisableableProps {
26
26
  // We want to make `href` required, while still allowing to render this component as a button if needed
27
27
  href: undefined
28
28
  }
29
29
  interface LinkProps
30
- extends BaseProps, ButtonOrLinkProps.LinkProps, ButtonOrLinkProps.DisableableProps {}
30
+ extends CommonProps, ButtonOrLinkProps.LinkProps, ButtonOrLinkProps.DisableableProps {}
31
31
  }
32
32
 
33
33
  export type LinkProps = LinkProps.LinkProps | LinkProps.ButtonProps
@@ -1,7 +1,14 @@
1
1
  'use client'
2
2
 
3
- import { createContext, useContext, useRef, type ComponentProps, type ReactNode } from 'react'
4
- import { Menu } from '@base-ui/react/menu'
3
+ import {
4
+ createContext,
5
+ useContext,
6
+ useLayoutEffect,
7
+ useRef,
8
+ type ComponentProps,
9
+ type ReactNode,
10
+ } from 'react'
11
+ import { Menu, type MenuRootActions } from '@base-ui/react/menu'
5
12
  import { useMergedRefs } from '@base-ui/utils/useMergedRefs'
6
13
  import type { Merge } from 'type-fest'
7
14
 
@@ -36,16 +43,16 @@ const MenuTriggerContext = createContext<boolean>(false)
36
43
  type MenuItemType = 'plain' | 'checkbox' | 'radio'
37
44
 
38
45
  declare namespace GroupProps {
39
- interface BaseProps {
46
+ interface CommonProps {
40
47
  label?: ReactNode | undefined
41
48
  }
42
- interface NonRadioProps extends BaseProps {
49
+ interface NonRadioProps extends CommonProps {
43
50
  type?: Exclude<MenuItemType, 'radio'> | undefined
44
51
  value?: undefined
45
52
  defaultValue?: undefined
46
53
  onChange?: undefined
47
54
  }
48
- interface RadioProps<T extends OptionValue> extends BaseProps {
55
+ interface RadioProps<T extends OptionValue> extends CommonProps {
49
56
  type: 'radio'
50
57
  value?: T | undefined
51
58
  defaultValue?: T | undefined
@@ -53,11 +60,9 @@ declare namespace GroupProps {
53
60
  }
54
61
  }
55
62
 
56
- declare namespace MenuProps {
57
- interface BaseProps
58
- extends
59
- Omit<ComponentProps<'div'>, 'defaultValue' | 'onChange'>,
60
- GDSComponentProps<typeof MenuMeta> {
63
+ export declare namespace MenuProps {
64
+ interface HTMLProps extends Omit<ComponentProps<'div'>, 'defaultValue' | 'onChange'> {}
65
+ interface OwnProps {
61
66
  /**
62
67
  * Element that opens the menu when clicked or hovered (depending on `triggerMode`), or a
63
68
  * function that returns such an element. If not provided, the menu is rendered inline rather
@@ -83,8 +88,9 @@ declare namespace MenuProps {
83
88
  header?: ReactNode | undefined
84
89
  footer?: ReactNode | undefined
85
90
  }
86
- interface NonRadioProps extends BaseProps, GroupProps.NonRadioProps {}
87
- interface RadioProps<T extends OptionValue> extends BaseProps, GroupProps.RadioProps<T> {}
91
+ interface CommonProps extends HTMLProps, OwnProps, GDSComponentProps<typeof MenuMeta> {}
92
+ interface NonRadioProps extends CommonProps, GroupProps.NonRadioProps {}
93
+ interface RadioProps<T extends OptionValue> extends CommonProps, GroupProps.RadioProps<T> {}
88
94
  }
89
95
 
90
96
  export type MenuProps<T extends OptionValue = OptionValue> =
@@ -120,6 +126,7 @@ export function MenuRoot<T extends OptionValue>({
120
126
 
121
127
  const { rootProps, nestedProps } = splitProps(props)
122
128
 
129
+ const actionsRef = useRef<MenuRootActions>(null)
123
130
  const portalRef = useRef<HTMLDivElement>(null)
124
131
 
125
132
  const [cssPropsPolyfillRef, cssPropsPolyfillAttributes, cssProps] = useCSSPropsPolyfill(
@@ -192,7 +199,6 @@ export function MenuRoot<T extends OptionValue>({
192
199
 
193
200
  return (
194
201
  <div
195
- ref={cssPropsPolyfillRef}
196
202
  data-is-submenu={isSubmenu || undefined}
197
203
  data-has-trigger={hasTrigger || undefined}
198
204
  className={cn(
@@ -215,8 +221,15 @@ export function MenuRoot<T extends OptionValue>({
215
221
  {...cssPropsPolyfillAttributes}
216
222
  {...rootProps}
217
223
  >
224
+ {/**
225
+ * Ensure changes to CSS props are detected even when the menu is hidden (not closed, but rendered
226
+ * in a `hidden` element). This is necessary because of a `style-observer` bug (or limitation?)
227
+ * related to `display: contents` (see https://github.com/LeaVerou/style-observer/issues/140).
228
+ */}
229
+ <span ref={cssPropsPolyfillRef} className="invisible absolute" />
218
230
  <MenuRoot
219
231
  key={`${hasTrigger}-${triggerMode}`}
232
+ actionsRef={actionsRef}
220
233
  open={open}
221
234
  onOpenChange={(newOpen) => {
222
235
  // A menu with no trigger can still be opened/closed by the consumer, but it should not send events
@@ -279,7 +292,7 @@ export function MenuRoot<T extends OptionValue>({
279
292
  sideOffset={twToPx(cssProps.gap)}
280
293
  align={cssProps.align}
281
294
  alignOffset={twToPx(cssProps.alignOffset)}
282
- collisionPadding={8}
295
+ collisionPadding={4}
283
296
  collisionAvoidance={{
284
297
  side: 'flip',
285
298
  align: 'flip',
@@ -300,6 +313,7 @@ export function MenuRoot<T extends OptionValue>({
300
313
  u:rounded-12
301
314
  u:data-has-trigger:max-h-(--available-height-but-not-too-small)
302
315
  u:data-has-trigger:max-w-(--available-width)
316
+ i:data-anchor-hidden:hidden
303
317
  u:i:data-[match-trigger-height=at-least]:min-h-[min(var(--anchor-height),var(--available-height-but-not-too-small))]
304
318
  u:i:data-[match-trigger-height=at-most]:max-h-[min(var(--anchor-height),var(--available-height-but-not-too-small))]
305
319
  u:i:data-[match-trigger-height=true]:h-(--anchor-height)
@@ -309,8 +323,19 @@ export function MenuRoot<T extends OptionValue>({
309
323
  className,
310
324
  )}
311
325
  {...dirProps}
312
- render={(renderProps) => (
313
- <div {...renderProps} style={hasTrigger ? renderProps.style : undefined} />
326
+ render={(renderProps, state) => (
327
+ <>
328
+ <div {...renderProps} style={hasTrigger ? renderProps.style : undefined} />
329
+ {/**
330
+ * The `data-anchor-hidden:hidden` class above hides the menu if it's open when the trigger gets
331
+ * hidden, but this actually closes it to restore pointer interactions outside the menu (though the
332
+ * class is still necessary to prevent a flash of incorrect menu position). We're doing it in an
333
+ * effect to prevent a "Cannot update a component while rendering a different component" error.
334
+ */}
335
+ {hasTrigger && state.open && state.anchorHidden && actionsRef.current ? (
336
+ <LayoutEffect effect={actionsRef.current.close} />
337
+ ) : null}
338
+ </>
314
339
  )}
315
340
  >
316
341
  <Menu.Popup
@@ -478,6 +503,11 @@ export function MenuRoot<T extends OptionValue>({
478
503
  }
479
504
  MenuRoot.displayName = 'Menu'
480
505
 
506
+ function LayoutEffect({ effect }: { effect: () => void }) {
507
+ useLayoutEffect(effect, [effect])
508
+ return null
509
+ }
510
+
481
511
  const MenuGroupContext = createContext<
482
512
  | {
483
513
  type: MenuItemType
@@ -486,10 +516,10 @@ const MenuGroupContext = createContext<
486
516
  | null
487
517
  >(null)
488
518
 
489
- declare namespace MenuGroupProps {
490
- interface BaseProps extends Omit<ComponentProps<'div'>, 'defaultValue' | 'onChange'> {}
491
- interface NonRadioProps extends BaseProps, GroupProps.NonRadioProps {}
492
- interface RadioProps<T extends OptionValue> extends BaseProps, GroupProps.RadioProps<T> {}
519
+ export declare namespace MenuGroupProps {
520
+ interface HTMLProps extends Omit<ComponentProps<'div'>, 'defaultValue' | 'onChange'> {}
521
+ interface NonRadioProps extends HTMLProps, GroupProps.NonRadioProps {}
522
+ interface RadioProps<T extends OptionValue> extends HTMLProps, GroupProps.RadioProps<T> {}
493
523
  }
494
524
 
495
525
  export type MenuGroupProps<T extends OptionValue = OptionValue> =
@@ -545,15 +575,17 @@ export function MenuGroup<T extends OptionValue>({
545
575
  MenuGroup.displayName = 'Menu.Group'
546
576
 
547
577
  export declare namespace MenuItemProps {
548
- interface BaseProps
549
- extends
550
- Omit<ComponentProps<'div'>, 'defaultChecked' | 'defaultValue' | 'onChange'>,
551
- GDSComponentProps<typeof MenuItemMeta> {
578
+ interface HTMLProps extends Omit<
579
+ ComponentProps<'div'>,
580
+ 'defaultChecked' | 'defaultValue' | 'onChange'
581
+ > {}
582
+ interface OwnProps {
552
583
  addonBefore?: AddonValue | undefined
553
584
  addonAfter?: AddonValue | undefined
554
585
  disabled?: boolean | undefined
555
586
  }
556
- interface PlainProps extends BaseProps {
587
+ interface CommonProps extends HTMLProps, OwnProps, GDSComponentProps<typeof MenuItemMeta> {}
588
+ interface PlainProps extends CommonProps {
557
589
  type?: 'plain' | undefined
558
590
  variant?: 'default' | 'danger' | undefined
559
591
  loading?: boolean | undefined
@@ -564,7 +596,7 @@ export declare namespace MenuItemProps {
564
596
  onChange?: undefined
565
597
  value?: undefined
566
598
  }
567
- interface CheckboxProps extends BaseProps {
599
+ interface CheckboxProps extends CommonProps {
568
600
  type?: 'checkbox' | undefined
569
601
  checked?: boolean | 'indeterminate' | undefined
570
602
  defaultChecked?: boolean | 'indeterminate' | undefined
@@ -575,7 +607,7 @@ export declare namespace MenuItemProps {
575
607
  target?: undefined
576
608
  value?: undefined
577
609
  }
578
- interface RadioProps<T extends OptionValue = OptionValue> extends BaseProps {
610
+ interface RadioProps<T extends OptionValue = OptionValue> extends CommonProps {
579
611
  type?: 'radio' | undefined
580
612
  value?: T | undefined
581
613
  variant?: undefined
@@ -213,11 +213,12 @@ export const ModalRoot = <
213
213
  <Portal>
214
214
  <div
215
215
  ref={cssPropsPolyfillRef}
216
- className={cn('gds-modal i:invisible', cssPropsClassName, className)}
216
+ className={cn('gds-modal i:invisible i:block', cssPropsClassName, className)}
217
217
  {...cssPropsAttributes}
218
218
  {...cssPropsPolyfillAttributes}
219
219
  {...dirProps}
220
220
  {...props}
221
+ hidden={undefined}
221
222
  />
222
223
  </Portal>
223
224
  <Dialog.Portal>
@@ -233,7 +234,7 @@ export const ModalRoot = <
233
234
  * root, then it returns 100vw because 100cqi, even 9999 times, is 0.
234
235
  */
235
236
  className={cn(
236
- `gds-modal @container-[size] pointer-events-none absolute top-0 left-0 u:text-16 u:text-muted i:invisible i:*:visible`,
237
+ 'gds-modal @container-[size] pointer-events-none absolute top-0 left-0 u:text-16 u:text-muted i:invisible i:*:visible',
237
238
  cssPropsClassName,
238
239
  className,
239
240
  )}
@@ -87,7 +87,7 @@ function createPaneStore(storeId: string) {
87
87
  : { ...initial, id, layout: PaneMeta.cssProps.layout.defaultValue, registrants },
88
88
  )
89
89
  // Defer notification to avoid updating components during render
90
- window.queueMicrotask(notify)
90
+ if (typeof window !== 'undefined') window.queueMicrotask(notify)
91
91
  },
92
92
  unregister: (name: string, registrantId: string) => {
93
93
  const pane = panes.get(name)
@@ -316,10 +316,10 @@ export function PaneRoot({
316
316
  const duration = parseDuration(styleValues['--tw-duration']) ?? DEFAULT_DURATION
317
317
  const rootPassedRef = useMergedRefs(rootRef, passedRef, styleCallbackRef)
318
318
 
319
- const [cssPropsPolyfillRootPassedRef, cssPropsPolyfillAttributes, cssProps] = useCSSPropsPolyfill(
319
+ const [cssPropsPolyfillRef, cssPropsPolyfillAttributes, cssProps] = useCSSPropsPolyfill(
320
320
  PaneMeta,
321
321
  { layout, side },
322
- { ref: rootPassedRef, returnPropValues: { layout, side } },
322
+ { returnPropValues: { layout, side } },
323
323
  )
324
324
 
325
325
  // Sync layout to store, resetting `overlayOpen` when switching from overlay to docked
@@ -430,7 +430,7 @@ export function PaneRoot({
430
430
 
431
431
  return (
432
432
  <Element
433
- ref={cssPropsPolyfillRootPassedRef}
433
+ ref={rootPassedRef}
434
434
  id={pane.id}
435
435
  data-gds-exposed-open={open}
436
436
  className={cn(
@@ -445,6 +445,12 @@ export function PaneRoot({
445
445
  {...cssPropsPolyfillAttributes}
446
446
  {...props}
447
447
  >
448
+ {/**
449
+ * Ensure changes to CSS props are detected even when the pane is hidden (not closed, but rendered
450
+ * in a `hidden` element). This is necessary because of a `style-observer` bug (or limitation?)
451
+ * related to `display: contents` (see https://github.com/LeaVerou/style-observer/issues/140).
452
+ */}
453
+ <span ref={cssPropsPolyfillRef} className="invisible absolute" />
448
454
  {overlayModal ? (
449
455
  <div
450
456
  aria-hidden
@@ -520,7 +526,9 @@ export function PaneRoot({
520
526
  }
521
527
  data-hidden-style={hiddenStyle || undefined}
522
528
  data-hidden-if-overlay={
523
- (beforeFirstTransition.current && !pane.overlayOpen) || undefined
529
+ (!pane.overlayOpen &&
530
+ (cssProps.layout === 'docked' || beforeFirstTransition.current)) ||
531
+ undefined
524
532
  }
525
533
  data-layout={cssProps.layout}
526
534
  data-side={cssProps.side}
@@ -632,12 +640,12 @@ type PaneToggleButtonState = {
632
640
  open: boolean
633
641
  }
634
642
 
635
- interface PaneToggleButtonBaseProps extends ComponentProps<'button'> {
643
+ interface PaneToggleButtonCommonProps extends ComponentProps<'button'> {
636
644
  /** Name of the pane to toggle. */
637
645
  name: string
638
646
  }
639
647
 
640
- interface PaneToggleButtonWithRenderProps extends PaneToggleButtonBaseProps {
648
+ interface PaneToggleButtonWithRenderProps extends PaneToggleButtonCommonProps {
641
649
  /** Element or render function for the toggle button. */
642
650
  render: RenderProp<PaneToggleButtonState>
643
651
  variant?: never
@@ -650,7 +658,7 @@ interface PaneToggleButtonWithRenderProps extends PaneToggleButtonBaseProps {
650
658
 
651
659
  interface PaneToggleButtonWithoutRenderProps
652
660
  extends
653
- PaneToggleButtonBaseProps,
661
+ PaneToggleButtonCommonProps,
654
662
  Pick<
655
663
  ToggleButtonProps,
656
664
  'variant' | 'size' | 'hideLabel' | 'tooltip' | 'addonBefore' | 'addonAfter'
@@ -123,8 +123,8 @@ export function TooltipRoot({
123
123
  const triggerPassedRef = useMergedRefs(
124
124
  /**
125
125
  * Conditionally setting the ref to force the tooltip to refresh its position when `enabled`
126
- * changes, preventing it from moving to the viewport's top-left corner in some cases (e.g. when
127
- * `CopyButton` returns to its default tooltip).
126
+ * changes, preventing it from moving to the top left corner of the viewport in some cases (e.g.
127
+ * when `CopyButton` returns to its default tooltip).
128
128
  */
129
129
  enabled ? triggerRef : undefined,
130
130
  (passedTriggerProps === 'forward' ? passedRef : passedTriggerProps?.ref) as
@@ -207,10 +207,22 @@ export function TooltipRoot({
207
207
  render={children}
208
208
  {...renderProps}
209
209
  disabled={triggerDisabled}
210
- onClick={triggerProps?.onClick} // Prevent the tooltip re-opening after clicking e.g. a `Menu` trigger
210
+ onClick={triggerProps?.onClick} // Prevent re-opening the tooltip after clicking e.g. a `Menu` trigger
211
211
  />
212
212
  )}
213
213
  />
214
+ {/* Ensure changes to CSS props are detected even when the tooltip is not mounted */}
215
+ <Portal>
216
+ <span
217
+ ref={cssPropsPolyfillRef}
218
+ className={cn('gds-tooltip i:invisible i:block', className)}
219
+ {...cssPropsAttributes}
220
+ {...cssPropsPolyfillAttributes}
221
+ {...dirProps}
222
+ {...popupProps}
223
+ hidden={undefined}
224
+ />
225
+ </Portal>
214
226
  <Tooltip.Portal>
215
227
  <Tooltip.Positioner
216
228
  key={getReactNodeKey(content)} // Refresh the tooltip's position when the content changes (see `WithDynamicContent` story)
@@ -224,9 +236,10 @@ export function TooltipRoot({
224
236
  sideOffset={twToPx(cssProps.gap)}
225
237
  align={cssProps.align}
226
238
  alignOffset={twToPx(cssProps.alignOffset)}
227
- collisionPadding={8}
228
- disableAnchorTracking // Prevent the tooltip from moving to the viewport's top-left corner when the trigger is unmounted
239
+ collisionPadding={4}
240
+ disableAnchorTracking // Feels weird for a tooltip to follow e.g. a pane's X button when closing the pane
229
241
  {...dirProps}
242
+ className="i:data-anchor-hidden:hidden" // Prevent moving to the top left corner when the trigger is hidden or unmounted
230
243
  >
231
244
  <Tooltip.Popup
232
245
  ref={popupRef}
@@ -251,16 +264,6 @@ export function TooltipRoot({
251
264
  </Tooltip.Popup>
252
265
  </Tooltip.Positioner>
253
266
  </Tooltip.Portal>
254
- {/* Ensure changes to CSS props are detected even when the tooltip is not mounted */}
255
- <Portal>
256
- <span
257
- ref={cssPropsPolyfillRef}
258
- className={cn('gds-tooltip i:invisible', className)}
259
- {...cssPropsAttributes}
260
- {...cssPropsPolyfillAttributes}
261
- {...popupProps}
262
- />
263
- </Portal>
264
267
  </Tooltip.Root>
265
268
  </TooltipContext.Provider>
266
269
  )
@@ -60,7 +60,7 @@ export declare namespace InternalButtonOrLinkProps {
60
60
  interface DisableableProps {
61
61
  disabled?: boolean | 'focusable' | undefined
62
62
  }
63
- interface BaseProps {
63
+ interface CommonProps {
64
64
  /**
65
65
  * Whether to render the element inline instead of block-level. For buttons, renders a `span`
66
66
  * that behaves like an accessible button. For links, this prop has no effect as anchor elements
@@ -77,7 +77,7 @@ export declare namespace InternalButtonOrLinkProps {
77
77
  }
78
78
  interface ButtonProps
79
79
  extends
80
- BaseProps,
80
+ CommonProps,
81
81
  DisableableProps,
82
82
  Omit<ComponentProps<'button'>, 'defaultValue' | 'disabled'> {
83
83
  href?: undefined
@@ -91,7 +91,7 @@ export declare namespace InternalButtonOrLinkProps {
91
91
  defaultChecked?: boolean | undefined
92
92
  }
93
93
  interface LinkProps
94
- extends BaseProps, DisableableProps, Omit<ComponentProps<'a'>, 'defaultValue'> {
94
+ extends CommonProps, DisableableProps, Omit<ComponentProps<'a'>, 'defaultValue'> {
95
95
  href: string
96
96
  type?: undefined
97
97
  checked?: undefined
@@ -135,15 +135,12 @@ type WithContextComponent<T extends ElementType> = (
135
135
  props: Merge<ComponentProps<T>, ButtonOrLinkRenderElementProps>,
136
136
  ) => ReactElement
137
137
 
138
- const memoizedWithContextComponents = new Map<ElementType, WithContextComponent<ElementType>>()
138
+ const memoizedWithContextComponents = new Map<ElementType, unknown>()
139
139
 
140
140
  function withContext<T extends ElementType>(Element: T): WithContextComponent<T> {
141
141
  const cached = memoizedWithContextComponents.get(Element)
142
- if (cached) return cached
143
- const newWithContextComponent: WithContextComponent<ElementType> = ({
144
- buttonOrLinkState,
145
- ...props
146
- }) => (
142
+ if (cached) return cached as WithContextComponent<T>
143
+ const newWithContextComponent: WithContextComponent<T> = ({ buttonOrLinkState, ...props }) => (
147
144
  <ButtonOrLinkContext.Provider value={buttonOrLinkState}>
148
145
  <Element {...(props as ComponentProps<T>)} />
149
146
  </ButtonOrLinkContext.Provider>
@@ -202,7 +199,7 @@ export const ButtonOrLinkRoot = (passedProps: InternalButtonOrLinkProps) => {
202
199
  render: passedRender,
203
200
  className: passedClassName,
204
201
  children,
205
- ...nonBaseProps
202
+ ...specificProps
206
203
  } = props
207
204
  const manuallyDisabledEventProps = {
208
205
  onClick: undefined,
@@ -246,7 +243,7 @@ export const ButtonOrLinkRoot = (passedProps: InternalButtonOrLinkProps) => {
246
243
  href: _href,
247
244
  target: _target,
248
245
  ...remainingProps
249
- } = nonBaseProps
246
+ } = specificProps
250
247
 
251
248
  if (defaultChecked !== undefined) {
252
249
  // oxlint-disable-next-line no-console
@@ -309,7 +306,7 @@ export const ButtonOrLinkRoot = (passedProps: InternalButtonOrLinkProps) => {
309
306
  checked: _checked,
310
307
  defaultChecked: _defaultChecked,
311
308
  ...remainingProps
312
- } = nonBaseProps
309
+ } = specificProps
313
310
 
314
311
  let linkProps: ComponentProps<'a'> = {
315
312
  role: disabled || role !== 'link' ? role : undefined,
@@ -42,7 +42,7 @@ export declare namespace MaybeButtonOrLinkProps {
42
42
  render?: RenderFn<MaybeButtonOrLinkRenderState> | undefined
43
43
  }
44
44
  export type OtherElementProps =
45
- OmitInternalButtonOrLinkProps<InternalButtonOrLinkProps.BaseProps> &
45
+ OmitInternalButtonOrLinkProps<InternalButtonOrLinkProps.CommonProps> &
46
46
  ComponentProps<'span'> & {
47
47
  onClick?: InternalButtonOrLinkProps.ButtonProps['onClick'] // Not technically correct, but prevents the event from being `any` when `as` is not specified
48
48
  as?: ElementType | undefined
@@ -63,7 +63,7 @@ export type MaybeButtonOrLinkProps =
63
63
 
64
64
  type InternalMaybeButtonOrLinkProps = MaybeButtonOrLinkProps &
65
65
  InternalButtonOrLinkProps.DisableableProps &
66
- Omit<InternalButtonOrLinkProps.BaseProps, 'render'>
66
+ Omit<InternalButtonOrLinkProps.CommonProps, 'render'>
67
67
 
68
68
  /**
69
69
  * Renders a `ButtonOrLink` if one of `href` or `onClick` is passed along with no `as`. Otherwise,
@@ -34,7 +34,7 @@ export {
34
34
  export { DescriptionListItemMeta, DescriptionListMeta } from './DescriptionList.meta.ts'
35
35
  export { Divider, type DividerProps } from './Divider.tsx'
36
36
  export { DividerMeta } from './Divider.meta.ts'
37
- export { Icon, type IconProps, type IconPropsWithSrc } from './Icon.tsx'
37
+ export { Icon, type IconProps, type IconSrc, type IconPropsWithSrc } from './Icon.tsx'
38
38
  export { IconMeta } from './Icon.meta.ts'
39
39
  export { Input, type InputProps, type InputState } from './Input.tsx'
40
40
  export { InputMeta } from './Input.meta.ts'