@graphprotocol/gds-react 0.1.0 → 0.1.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.
Files changed (107) hide show
  1. package/README.md +10 -11
  2. package/dist/GDSProvider.d.ts +6 -3
  3. package/dist/GDSProvider.d.ts.map +1 -1
  4. package/dist/GDSProvider.js +14 -6
  5. package/dist/GDSProvider.js.map +1 -1
  6. package/dist/components/Address.d.ts.map +1 -1
  7. package/dist/components/Address.js +8 -6
  8. package/dist/components/Address.js.map +1 -1
  9. package/dist/components/Avatar.d.ts +4 -2
  10. package/dist/components/Avatar.d.ts.map +1 -1
  11. package/dist/components/Avatar.js +8 -5
  12. package/dist/components/Avatar.js.map +1 -1
  13. package/dist/components/Breadcrumbs.parts.d.ts.map +1 -1
  14. package/dist/components/Breadcrumbs.parts.js +2 -2
  15. package/dist/components/Breadcrumbs.parts.js.map +1 -1
  16. package/dist/components/Button.js +3 -3
  17. package/dist/components/Button.js.map +1 -1
  18. package/dist/components/Card.js +2 -2
  19. package/dist/components/Card.js.map +1 -1
  20. package/dist/components/Checkbox.parts.js +1 -1
  21. package/dist/components/Checkbox.parts.js.map +1 -1
  22. package/dist/components/CodeBlock.parts.js +1 -1
  23. package/dist/components/CodeBlock.parts.js.map +1 -1
  24. package/dist/components/CopyButton.js +1 -1
  25. package/dist/components/CopyButton.js.map +1 -1
  26. package/dist/components/Keyboard.js +1 -1
  27. package/dist/components/Label.d.ts.map +1 -1
  28. package/dist/components/Label.js +1 -1
  29. package/dist/components/Label.js.map +1 -1
  30. package/dist/components/Link.d.ts.map +1 -1
  31. package/dist/components/Link.js +1 -1
  32. package/dist/components/Link.js.map +1 -1
  33. package/dist/components/Menu.parts.d.ts.map +1 -1
  34. package/dist/components/Menu.parts.js +15 -10
  35. package/dist/components/Menu.parts.js.map +1 -1
  36. package/dist/components/Modal.parts.js +1 -1
  37. package/dist/components/Modal.parts.js.map +1 -1
  38. package/dist/components/OTCInput.js +1 -1
  39. package/dist/components/OTCInput.js.map +1 -1
  40. package/dist/components/Search.d.ts +16 -0
  41. package/dist/components/Search.d.ts.map +1 -0
  42. package/dist/components/Search.js +129 -0
  43. package/dist/components/Search.js.map +1 -0
  44. package/dist/components/Search.meta.d.ts +15 -0
  45. package/dist/components/Search.meta.d.ts.map +1 -0
  46. package/dist/components/Search.meta.js +24 -0
  47. package/dist/components/Search.meta.js.map +1 -0
  48. package/dist/components/SegmentedControl.parts.d.ts.map +1 -1
  49. package/dist/components/SegmentedControl.parts.js +9 -10
  50. package/dist/components/SegmentedControl.parts.js.map +1 -1
  51. package/dist/components/TabSet.parts.js +2 -2
  52. package/dist/components/TabSet.parts.js.map +1 -1
  53. package/dist/components/Tooltip.parts.d.ts.map +1 -1
  54. package/dist/components/Tooltip.parts.js +10 -6
  55. package/dist/components/Tooltip.parts.js.map +1 -1
  56. package/dist/components/base/ButtonOrLink.parts.d.ts.map +1 -1
  57. package/dist/components/base/ButtonOrLink.parts.js +23 -12
  58. package/dist/components/base/ButtonOrLink.parts.js.map +1 -1
  59. package/dist/components/base/Render.d.ts +1 -1
  60. package/dist/components/base/Render.d.ts.map +1 -1
  61. package/dist/components/base/Render.js +1 -1
  62. package/dist/components/base/Render.js.map +1 -1
  63. package/dist/components/index.d.ts +2 -0
  64. package/dist/components/index.d.ts.map +1 -1
  65. package/dist/components/index.js +2 -0
  66. package/dist/components/index.js.map +1 -1
  67. package/dist/hooks/useCSSProp.js +1 -1
  68. package/dist/hooks/useCSSProp.js.map +1 -1
  69. package/dist/hooks/useEffectWithRefDeps.js +1 -1
  70. package/dist/hooks/useEffectWithRefDeps.js.map +1 -1
  71. package/dist/hooks/useGDS.d.ts +1 -1
  72. package/dist/hooks/useNumberInput.js +1 -1
  73. package/dist/hooks/useNumberInput.js.map +1 -1
  74. package/dist/hooks/useStyleObserver.js +1 -1
  75. package/dist/hooks/useStyleObserver.js.map +1 -1
  76. package/dist/tailwind-plugin.d.ts.map +1 -1
  77. package/dist/tailwind-plugin.js +2 -0
  78. package/dist/tailwind-plugin.js.map +1 -1
  79. package/package.json +19 -18
  80. package/src/GDSProvider.tsx +31 -14
  81. package/src/components/Address.tsx +11 -6
  82. package/src/components/Avatar.tsx +21 -13
  83. package/src/components/Breadcrumbs.parts.tsx +2 -3
  84. package/src/components/Button.tsx +3 -3
  85. package/src/components/Card.tsx +2 -2
  86. package/src/components/Checkbox.parts.tsx +1 -1
  87. package/src/components/CodeBlock.parts.tsx +3 -3
  88. package/src/components/CopyButton.tsx +1 -1
  89. package/src/components/Keyboard.tsx +1 -1
  90. package/src/components/Label.tsx +2 -1
  91. package/src/components/Link.tsx +2 -1
  92. package/src/components/Menu.parts.tsx +20 -24
  93. package/src/components/Modal.parts.tsx +1 -1
  94. package/src/components/OTCInput.tsx +1 -1
  95. package/src/components/Search.meta.ts +24 -0
  96. package/src/components/Search.tsx +238 -0
  97. package/src/components/SegmentedControl.parts.tsx +10 -11
  98. package/src/components/TabSet.parts.tsx +2 -2
  99. package/src/components/Tooltip.parts.tsx +15 -4
  100. package/src/components/base/ButtonOrLink.parts.tsx +27 -13
  101. package/src/components/base/Render.tsx +1 -1
  102. package/src/components/index.ts +2 -0
  103. package/src/hooks/useCSSProp.ts +1 -1
  104. package/src/hooks/useEffectWithRefDeps.ts +1 -1
  105. package/src/hooks/useNumberInput.ts +1 -1
  106. package/src/hooks/useStyleObserver.ts +1 -1
  107. package/src/tailwind-plugin.ts +2 -0
@@ -0,0 +1,238 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useRef, type ComponentProps } from 'react'
4
+ import { useMergedRefs } from '@base-ui/utils/useMergedRefs'
5
+
6
+ import type { GDSComponentProps } from '@graphprotocol/gds-css'
7
+ import { MagnifyingGlassIcon, XIcon } from '@graphprotocol/gds-react/icons'
8
+
9
+ import { useControlled, useCSSPropsPolyfill, useCSSState, useGDS } from '../hooks/index.ts'
10
+ import { cn, getCSSPropsAttributes, splitProps } from '../utils/index.ts'
11
+ import { Button } from './Button.tsx'
12
+ import { Keyboard } from './Keyboard.js'
13
+ import { SearchMeta } from './Search.meta.ts'
14
+
15
+ export interface SearchProps
16
+ extends
17
+ Omit<ComponentProps<'input'>, 'disabled' | 'onChange' | 'size' | 'type'>,
18
+ GDSComponentProps<typeof SearchMeta> {
19
+ value?: string | undefined
20
+ defaultValue?: string | undefined
21
+ onChange?: ((value: string) => void) | undefined
22
+ /**
23
+ * Key that focuses the search input when pressed. Set to `false` to disable.
24
+ *
25
+ * @default '/'
26
+ */
27
+ focusKey?: string | false | undefined
28
+ }
29
+
30
+ export function Search({
31
+ ref: passedRef,
32
+ size,
33
+ layout,
34
+ placeholder = 'Search...',
35
+ value: controlledValue,
36
+ defaultValue,
37
+ onChange,
38
+ focusKey = '/',
39
+ className,
40
+ style,
41
+ ...props
42
+ }: SearchProps) {
43
+ useGDS()
44
+
45
+ const { rootProps, nestedProps } = splitProps(props)
46
+
47
+ const inputRef = useRef<HTMLInputElement>(null)
48
+ const inputPassedRef = useMergedRefs(inputRef, passedRef)
49
+ const previousActiveElementRef = useRef<HTMLElement | null>(null)
50
+ const [value, setValue] = useControlled(controlledValue, defaultValue ?? '', onChange)
51
+
52
+ const [stateRef, state] = useCSSState({
53
+ pointer: undefined,
54
+ focus: undefined,
55
+ blank: value === '',
56
+ open: value !== '',
57
+ })
58
+ const [cssPropsPolyfillStateRef, cssPropsPolyfillAttributes, cssProps] = useCSSPropsPolyfill(
59
+ SearchMeta,
60
+ { size, layout },
61
+ { ref: stateRef, returnPropValues: { size, layout } },
62
+ )
63
+
64
+ useEffect(() => {
65
+ if (!focusKey) return
66
+
67
+ function handleKeyDown(event: KeyboardEvent) {
68
+ if (event.key !== focusKey) return
69
+
70
+ const input = inputRef.current
71
+ if (!input) return
72
+
73
+ // Don't focus if already focused on an interactive element
74
+ const activeElement = document.activeElement
75
+ if (
76
+ activeElement instanceof HTMLInputElement ||
77
+ activeElement instanceof HTMLTextAreaElement ||
78
+ (activeElement instanceof HTMLElement && activeElement.isContentEditable)
79
+ ) {
80
+ return
81
+ }
82
+
83
+ // Don't focus if the input has an inert/hidden ancestor
84
+ if (input.closest('[inert], [data-base-ui-inert], [aria-hidden="true"]')) {
85
+ return
86
+ }
87
+
88
+ event.preventDefault()
89
+ // Store the currently focused element before focusing the input
90
+ previousActiveElementRef.current = activeElement instanceof HTMLElement ? activeElement : null
91
+ input.focus()
92
+ input.select()
93
+ }
94
+
95
+ document.addEventListener('keydown', handleKeyDown)
96
+ return () => document.removeEventListener('keydown', handleKeyDown)
97
+ }, [focusKey])
98
+
99
+ return (
100
+ <div
101
+ ref={cssPropsPolyfillStateRef}
102
+ data-size={cssProps.size}
103
+ data-layout={cssProps.layout}
104
+ className={cn(
105
+ `gds-search root-flex flex-col u:max-w-full
106
+ u:hover:state-hover
107
+ u:active:state-active
108
+ u:data-[layout=compact]:data-[size=medium]:min-w-10
109
+ u:data-[layout=compact]:data-[size=small]:min-w-8
110
+ u:has-nested-not-blank/search-ref:expose-open
111
+ u:has-nested-blank/search-ref:expose-blank
112
+ u:has-nested-focus-visible/search-ref:expose-focus
113
+ u:has-nested-focus-visible/search-ref:expose-open
114
+ u:has-nested-active/search-clear-button:state-focus`,
115
+ className,
116
+ )}
117
+ {...state.exposedAttributes}
118
+ {...state.polyfillAttributes}
119
+ {...getCSSPropsAttributes(SearchMeta, { size, layout }, style)}
120
+ {...cssPropsPolyfillAttributes}
121
+ {...rootProps}
122
+ >
123
+ <div
124
+ className={`
125
+ flex h-(--height) grow overflow-clip text-form-text duration-300
126
+ @prop-size-small/search:[--height:--spacing(8)]
127
+ @prop-size-small/search:[--padding:--spacing(2)]
128
+ @prop-size-medium/search:[--height:--spacing(10)]
129
+ @prop-size-medium/search:[--padding:--spacing(2.5)]
130
+ @prop-layout-compact/search:transition-[width]
131
+ @prop-layout-compact/search:@state-not-open/search:w-(--height)
132
+ @prop-layout-compact/search:@prop-size-small/search:animate-[width-1_1ms]
133
+ @prop-layout-compact/search:@prop-size-medium/search:animate-[width-2_1ms]
134
+ `}
135
+ >
136
+ <div
137
+ className={`
138
+ h-full w-(--gds-search-input-width) grow border border-form-idle transition
139
+ @state-hover/search:border-form-hover
140
+ @state-focus/search:border-focus
141
+ @state-active/search:transition-none
142
+ @prop-size-small/search:rounded-6
143
+ @prop-size-medium/search:rounded-8
144
+ `}
145
+ />
146
+ <input
147
+ ref={inputPassedRef}
148
+ type="search"
149
+ placeholder={placeholder}
150
+ aria-label={placeholder || 'Search'}
151
+ value={value}
152
+ onChange={(event) => setValue(event.target.value)}
153
+ onKeyDown={(event) => {
154
+ nestedProps.onKeyDown?.(event)
155
+ if (event.defaultPrevented) return
156
+ if (event.key === 'Escape' && value === '') {
157
+ event.preventDefault()
158
+ inputRef.current?.blur()
159
+ // Restore focus to the previous element if it still exists in the DOM
160
+ if (
161
+ previousActiveElementRef.current &&
162
+ document.contains(previousActiveElementRef.current)
163
+ ) {
164
+ previousActiveElementRef.current.focus()
165
+ previousActiveElementRef.current = null
166
+ }
167
+ }
168
+ }}
169
+ className={`
170
+ nested/search-ref absolute end-0 top-0 size-full ps-(--padding) pe-(--height) outline-0
171
+ @state-not-focus/search:@state-blank/search:ps-[calc(var(--height)+var(--padding))]
172
+ @state-focus/search:placeholder:text-transparent
173
+ @prop-size-small/search:text-12
174
+ @prop-size-medium/search:text-14
175
+ @prop-layout-compact/search:@state-not-open/search:w-screen
176
+ @prop-layout-compact/search:@state-not-open/search:opacity-0
177
+ `}
178
+ {...nestedProps}
179
+ />
180
+ <div
181
+ className={`
182
+ pointer-events-none absolute inset-y-0 start-0 flex w-(--height) items-center justify-center
183
+ border-e border-form-idle transition
184
+ @state-hover/search:border-form-hover
185
+ @state-focus/search:hidden
186
+ @prop-layout-compact/search:@state-not-open/search:border-0
187
+ @prop-layout-compact/search:@state-open/search:@state-not-blank/search:hidden
188
+ @prop-layout-full/search:@state-not-blank/search:hidden
189
+ `}
190
+ >
191
+ <MagnifyingGlassIcon
192
+ alt=""
193
+ className={`
194
+ @prop-size-small/search:prop-size-3.5
195
+ @prop-size-medium/search:prop-size-4
196
+ `}
197
+ />
198
+ </div>
199
+ <div
200
+ className={`
201
+ pointer-events-none absolute inset-y-0 end-0 grid w-(--height) place-items-center
202
+ *:col-span-full *:row-span-full
203
+ `}
204
+ >
205
+ <Button
206
+ variant="tertiary"
207
+ size="xsmall"
208
+ tooltip={null}
209
+ tabIndex={-1}
210
+ onClick={() => {
211
+ setValue('')
212
+ inputRef.current?.focus()
213
+ }}
214
+ className={`
215
+ nested/search-clear-button pointer-events-auto size-6 var-[radius=var(--radius-4)]
216
+ @state-blank/search:hidden
217
+ @prop-layout-compact/search:@state-not-open/search:hidden
218
+ `}
219
+ >
220
+ <XIcon alt="Clear" />
221
+ </Button>
222
+ {focusKey ? (
223
+ <Keyboard
224
+ size="large"
225
+ className={`
226
+ @state-not-blank/search:hidden
227
+ @state-focus/search:hidden
228
+ @prop-layout-compact/search:@state-not-open/search:hidden
229
+ `}
230
+ >
231
+ {focusKey}
232
+ </Keyboard>
233
+ ) : null}
234
+ </div>
235
+ </div>
236
+ </div>
237
+ )
238
+ }
@@ -8,9 +8,9 @@ import {
8
8
  type KeyboardEvent,
9
9
  type MouseEvent,
10
10
  } from 'react'
11
- import { Radio } from '@base-ui-components/react/radio'
12
- import { RadioGroup } from '@base-ui-components/react/radio-group'
13
- import { useMergedRefs } from '@base-ui-components/utils/useMergedRefs'
11
+ import { Radio } from '@base-ui/react/radio'
12
+ import { RadioGroup } from '@base-ui/react/radio-group'
13
+ import { useMergedRefs } from '@base-ui/utils/useMergedRefs'
14
14
 
15
15
  import type { GDSComponentProps } from '@graphprotocol/gds-css'
16
16
 
@@ -142,6 +142,8 @@ export function SegmentedControlOption<T extends OptionValue>({
142
142
  }: SegmentedControlOptionProps<T>) {
143
143
  useGDS()
144
144
 
145
+ const { rootProps, nestedProps } = splitProps(props)
146
+
145
147
  const buttonRef = useRef<HTMLSpanElement>(null)
146
148
  const buttonPassedRef = useMergedRefs(buttonRef, passedRef)
147
149
 
@@ -154,8 +156,6 @@ export function SegmentedControlOption<T extends OptionValue>({
154
156
  const autoValue = useAutoValue(children)
155
157
  const value = passedValue !== undefined ? passedValue : autoValue
156
158
 
157
- const { rootProps, nestedProps } = splitProps(props)
158
-
159
159
  const { onCollect, collectedContent } = useCollectedTooltip()
160
160
  let tooltipProps: Omit<TooltipProps, 'children'> = { content: collectedContent }
161
161
  if (tooltip !== undefined) {
@@ -190,12 +190,11 @@ export function SegmentedControlOption<T extends OptionValue>({
190
190
  /**
191
191
  * This wrapper is necessary to ensure this component returns a single element (which some
192
192
  * Tailwind classes might assume) because `Radio.Root` renders a `<input type="radio">` as a
193
- * sibling of the button (which is a `<span>`, see https://github.com/mui/base-ui/pull/3205).
194
- * Ideally, we would make `Radio.Root` the root and it would render an internal component (e.g.
195
- * `SegmentedControlOptionButton`) that would itself render a wrapper `<div>` around the button
196
- * and the input (as well as calling `useCSSState` with a proper initial `checked` value, from
197
- * the `render` state), but that is not possible currently (see
198
- * https://github.com/mui/base-ui/issues/3143).
193
+ * sibling of the button (actually a `<span>`). Ideally, we would make `Radio.Root` the root and
194
+ * it would render an internal component (e.g. `SegmentedControlOptionButton`) that would itself
195
+ * render a wrapper `<div>` around the button and the input (as well as calling `useCSSState`
196
+ * with a proper initial `checked` value, from the `render` state), but that is not possible
197
+ * currently (see https://github.com/mui/base-ui/issues/3143).
199
198
  */
200
199
  <div
201
200
  ref={stateRef}
@@ -1,7 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import { createContext, useContext, type ComponentProps } from 'react'
4
- import { Tabs } from '@base-ui-components/react/tabs'
4
+ import { Tabs } from '@base-ui/react/tabs'
5
5
  import flattenChildren from 'react-keyed-flatten-children'
6
6
 
7
7
  import type { GDSComponentProps } from '@graphprotocol/gds-css'
@@ -265,7 +265,7 @@ export function TabSetPanels({
265
265
  u:group-data-[activation-direction=right]/tab-set:var-[exit-translate-x=-32px]
266
266
  u:has-nested-focus-visible/tab-set-panel:animate
267
267
  u:has-nested-focus-visible/tab-set-panel:outline
268
- u:has-nested-focus-visible/tab-set-panel:animate-outline-from-(--border-color-focus)`,
268
+ u:has-nested-focus-visible/tab-set-panel:animate-outline-from-focus`,
269
269
  transition && typeof transition === 'object' ? transition.className : undefined,
270
270
  className,
271
271
  )}
@@ -2,15 +2,17 @@
2
2
 
3
3
  import {
4
4
  createContext,
5
+ useCallback,
5
6
  useContext,
6
7
  useEffect,
8
+ useMemo,
7
9
  useRef,
8
10
  useState,
9
11
  type ComponentProps,
10
12
  type ReactElement,
11
13
  type ReactNode,
12
14
  } from 'react'
13
- import { Tooltip } from '@base-ui-components/react/tooltip'
15
+ import { Tooltip } from '@base-ui/react/tooltip'
14
16
 
15
17
  import { twToPx, type GDSComponentProps } from '@graphprotocol/gds-css'
16
18
 
@@ -83,9 +85,9 @@ export function TooltipRoot({
83
85
  const enabled = !cssProps.disabled && !hasEnabledTooltipAncestor && Boolean(content)
84
86
  const [open, ownSetOpen] = useControlled(controlledOpen, false, onOpenChange)
85
87
  const nestedSetOpen = useRef<(newOpen: boolean) => void>(null)
86
- const registerNestedSetOpen = (newNestedSetOpen: (newOpen: boolean) => void) => {
88
+ const registerNestedSetOpen = useCallback((newNestedSetOpen: (newOpen: boolean) => void) => {
87
89
  nestedSetOpen.current = newNestedSetOpen
88
- }
90
+ }, [])
89
91
  const setOpen = (newOpen: boolean) => {
90
92
  ownSetOpen(newOpen)
91
93
  /**
@@ -112,8 +114,16 @@ export function TooltipRoot({
112
114
  clearOpenTimeout()
113
115
  }
114
116
 
117
+ const tooltipContextValue = useMemo(
118
+ () => ({
119
+ enabled,
120
+ registerNestedSetOpen,
121
+ }),
122
+ [enabled, registerNestedSetOpen],
123
+ )
124
+
115
125
  return (
116
- <TooltipContext.Provider value={{ enabled, registerNestedSetOpen }}>
126
+ <TooltipContext.Provider value={tooltipContextValue}>
117
127
  <TooltipCollector onCollect={onCollect}>
118
128
  <Tooltip.Root
119
129
  disabled={!enabled}
@@ -145,6 +155,7 @@ export function TooltipRoot({
145
155
  side={cssProps.position}
146
156
  sideOffset={twToPx(cssProps.gap)}
147
157
  align={cssProps.align}
158
+ disableAnchorTracking
148
159
  {...dirProps}
149
160
  >
150
161
  <Tooltip.Popup
@@ -4,6 +4,7 @@ import {
4
4
  createContext,
5
5
  isValidElement,
6
6
  useContext,
7
+ useMemo,
7
8
  useRef,
8
9
  useState,
9
10
  type ComponentProps,
@@ -13,7 +14,7 @@ import {
13
14
  type ReactElement,
14
15
  type ReactNode,
15
16
  } from 'react'
16
- import { useMergedRefs } from '@base-ui-components/utils/useMergedRefs'
17
+ import { useMergedRefs } from '@base-ui/utils/useMergedRefs'
17
18
  import { mergeProps } from '@react-aria/utils'
18
19
  import { useButton } from 'react-aria'
19
20
 
@@ -337,25 +338,38 @@ export const ButtonOrLinkConfig = ({
337
338
  children,
338
339
  }: ButtonOrLinkConfigProps) => {
339
340
  const ancestorConfig = useContext(ButtonOrLinkConfigContext)
341
+
340
342
  const onClick =
341
343
  passedOnClick && ancestorConfig?.onClick
342
- ? (
343
- event: MouseEvent<HTMLButtonElement & HTMLAnchorElement>,
344
- props: InternalButtonOrLinkProps,
345
- ) => {
346
- passedOnClick(event, props)
347
- if (event.isPropagationStopped()) return
348
- ancestorConfig.onClick!(event, props)
349
- }
344
+ ? (() => {
345
+ const ancestorOnClick = ancestorConfig.onClick
346
+ return (
347
+ event: MouseEvent<HTMLButtonElement & HTMLAnchorElement>,
348
+ props: InternalButtonOrLinkProps,
349
+ ) => {
350
+ passedOnClick(event, props)
351
+ if (event.isPropagationStopped()) return
352
+ ancestorOnClick(event, props)
353
+ }
354
+ })()
350
355
  : (passedOnClick ?? ancestorConfig?.onClick)
351
356
  const transformProps =
352
357
  passedTransformProps && ancestorConfig?.transformProps
353
- ? (props: InternalButtonOrLinkProps) => {
354
- return ancestorConfig.transformProps!(passedTransformProps(props))
355
- }
358
+ ? (() => {
359
+ const ancestorTransformProps = ancestorConfig.transformProps
360
+ return (props: InternalButtonOrLinkProps) => {
361
+ return ancestorTransformProps(passedTransformProps(props))
362
+ }
363
+ })()
356
364
  : (passedTransformProps ?? ancestorConfig?.transformProps)
365
+
366
+ const buttonOrLinkConfigContextValue = useMemo(() => {
367
+ if (!onClick && !transformProps) return null
368
+ return { onClick, transformProps }
369
+ }, [onClick, transformProps])
370
+
357
371
  return (
358
- <ButtonOrLinkConfigContext.Provider value={{ onClick, transformProps }}>
372
+ <ButtonOrLinkConfigContext.Provider value={buttonOrLinkConfigContextValue}>
359
373
  {children}
360
374
  </ButtonOrLinkConfigContext.Provider>
361
375
  )
@@ -1,7 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import type { ComponentProps, ElementType } from 'react'
4
- import { useRender } from '@base-ui-components/react/use-render'
4
+ import { useRender } from '@base-ui/react/use-render'
5
5
 
6
6
  export type RenderProps<
7
7
  T extends ElementType = 'span',
@@ -63,6 +63,8 @@ export { OTCInput, type OTCInputProps } from './OTCInput.tsx'
63
63
  export { OTCInputMeta } from './OTCInput.meta.ts'
64
64
  export { Radio, type RadioAreaProps, type RadioGroupProps, type RadioProps } from './Radio.tsx'
65
65
  export { RadioMeta } from './Radio.meta.ts'
66
+ export { Search, type SearchProps } from './Search.tsx'
67
+ export { SearchMeta } from './Search.meta.ts'
66
68
  export {
67
69
  SegmentedControl,
68
70
  type SegmentedControlOptionProps,
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useRef, type Ref, type RefCallback } from 'react'
2
- import { useMergedRefs } from '@base-ui-components/utils/useMergedRefs'
2
+ import { useMergedRefs } from '@base-ui/utils/useMergedRefs'
3
3
 
4
4
  import {
5
5
  parseCSSPropValue,
@@ -6,7 +6,7 @@ import {
6
6
  type Ref,
7
7
  type RefCallback,
8
8
  } from 'react'
9
- import { useMergedRefs } from '@base-ui-components/utils/useMergedRefs'
9
+ import { useMergedRefs } from '@base-ui/utils/useMergedRefs'
10
10
  import { useCustomCompareEffect, useCustomCompareMemo, type EffectHook } from '@react-hookz/web'
11
11
  import { isEqual } from '@ver0/deep-equal'
12
12
 
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useMemo, useRef, useState, type KeyboardEvent, type Ref } from 'react'
2
- import { useMergedRefs } from '@base-ui-components/utils/useMergedRefs'
2
+ import { useMergedRefs } from '@base-ui/utils/useMergedRefs'
3
3
 
4
4
  import {
5
5
  bigIntToNumber,
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useLayoutEffect, useRef, type Ref } from 'react'
2
- import { useMergedRefs } from '@base-ui-components/utils/useMergedRefs'
2
+ import { useMergedRefs } from '@base-ui/utils/useMergedRefs'
3
3
  import StyleObserver from 'style-observer'
4
4
 
5
5
  import { styleScheduler } from '../utils/styleScheduler.ts'
@@ -31,6 +31,7 @@ import { MenuItemMeta, MenuMeta } from './components/Menu.meta.ts'
31
31
  import { ModalMeta } from './components/Modal.meta.ts'
32
32
  import { OTCInputMeta } from './components/OTCInput.meta.ts'
33
33
  import { RadioMeta } from './components/Radio.meta.ts'
34
+ import { SearchMeta } from './components/Search.meta.ts'
34
35
  import {
35
36
  SegmentedControlMeta,
36
37
  SegmentedControlOptionMeta,
@@ -101,6 +102,7 @@ const gdsTailwindPluginWithComponents: ReturnType<typeof createPlugin> = createP
101
102
  ModalMeta,
102
103
  OTCInputMeta,
103
104
  RadioMeta,
105
+ SearchMeta,
104
106
  SegmentedControlMeta,
105
107
  SegmentedControlOptionMeta,
106
108
  StatusMeta,