@graphprotocol/gds-react 0.2.0 → 0.2.2

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 (129) hide show
  1. package/dist/GDSContext.d.ts +13 -0
  2. package/dist/GDSContext.d.ts.map +1 -0
  3. package/dist/GDSContext.js +4 -0
  4. package/dist/GDSContext.js.map +1 -0
  5. package/dist/GDSProvider.d.ts +1 -9
  6. package/dist/GDSProvider.d.ts.map +1 -1
  7. package/dist/GDSProvider.js +4 -3
  8. package/dist/GDSProvider.js.map +1 -1
  9. package/dist/components/Avatar.d.ts.map +1 -1
  10. package/dist/components/Avatar.js +2 -2
  11. package/dist/components/Avatar.js.map +1 -1
  12. package/dist/components/Breadcrumbs.parts.js +1 -1
  13. package/dist/components/Breadcrumbs.parts.js.map +1 -1
  14. package/dist/components/Button.d.ts.map +1 -1
  15. package/dist/components/Button.js +69 -69
  16. package/dist/components/Button.js.map +1 -1
  17. package/dist/components/Card.js +2 -2
  18. package/dist/components/Card.js.map +1 -1
  19. package/dist/components/CodeBlock.d.ts +1 -1
  20. package/dist/components/CodeBlock.parts.d.ts +1 -1
  21. package/dist/components/CopyButton.d.ts +1 -1
  22. package/dist/components/CopyButton.d.ts.map +1 -1
  23. package/dist/components/CopyButton.js +46 -19
  24. package/dist/components/CopyButton.js.map +1 -1
  25. package/dist/components/Input.js +2 -2
  26. package/dist/components/Input.js.map +1 -1
  27. package/dist/components/Link.js +2 -2
  28. package/dist/components/Link.js.map +1 -1
  29. package/dist/components/Menu.parts.d.ts +4 -5
  30. package/dist/components/Menu.parts.d.ts.map +1 -1
  31. package/dist/components/Menu.parts.js +52 -45
  32. package/dist/components/Menu.parts.js.map +1 -1
  33. package/dist/components/Modal.parts.d.ts.map +1 -1
  34. package/dist/components/Modal.parts.js +17 -21
  35. package/dist/components/Modal.parts.js.map +1 -1
  36. package/dist/components/Pane.d.ts +9 -0
  37. package/dist/components/Pane.d.ts.map +1 -0
  38. package/dist/components/Pane.js +8 -0
  39. package/dist/components/Pane.js.map +1 -0
  40. package/dist/components/Pane.meta.d.ts +20 -0
  41. package/dist/components/Pane.meta.d.ts.map +1 -0
  42. package/dist/components/Pane.meta.js +30 -0
  43. package/dist/components/Pane.meta.js.map +1 -0
  44. package/dist/components/Pane.parts.d.ts +77 -0
  45. package/dist/components/Pane.parts.d.ts.map +1 -0
  46. package/dist/components/Pane.parts.js +412 -0
  47. package/dist/components/Pane.parts.js.map +1 -0
  48. package/dist/components/Search.js +1 -1
  49. package/dist/components/Tooltip.parts.d.ts +13 -4
  50. package/dist/components/Tooltip.parts.d.ts.map +1 -1
  51. package/dist/components/Tooltip.parts.js +51 -63
  52. package/dist/components/Tooltip.parts.js.map +1 -1
  53. package/dist/components/base/ButtonOrLink.d.ts +1 -1
  54. package/dist/components/base/ButtonOrLink.d.ts.map +1 -1
  55. package/dist/components/base/ButtonOrLink.parts.d.ts +10 -3
  56. package/dist/components/base/ButtonOrLink.parts.d.ts.map +1 -1
  57. package/dist/components/base/ButtonOrLink.parts.js +27 -35
  58. package/dist/components/base/ButtonOrLink.parts.js.map +1 -1
  59. package/dist/components/base/MaybeButtonOrLink.d.ts +19 -2
  60. package/dist/components/base/MaybeButtonOrLink.d.ts.map +1 -1
  61. package/dist/components/base/MaybeButtonOrLink.js +5 -3
  62. package/dist/components/base/MaybeButtonOrLink.js.map +1 -1
  63. package/dist/components/base/Presence.d.ts +157 -0
  64. package/dist/components/base/Presence.d.ts.map +1 -0
  65. package/dist/components/base/Presence.js +808 -0
  66. package/dist/components/base/Presence.js.map +1 -0
  67. package/dist/components/base/index.d.ts +1 -0
  68. package/dist/components/base/index.d.ts.map +1 -1
  69. package/dist/components/base/index.js +1 -0
  70. package/dist/components/base/index.js.map +1 -1
  71. package/dist/components/index.d.ts +2 -0
  72. package/dist/components/index.d.ts.map +1 -1
  73. package/dist/components/index.js +2 -0
  74. package/dist/components/index.js.map +1 -1
  75. package/dist/hooks/useCSSProp.js +1 -1
  76. package/dist/hooks/useCSSProp.js.map +1 -1
  77. package/dist/hooks/useControlled.d.ts.map +1 -1
  78. package/dist/hooks/useControlled.js +6 -4
  79. package/dist/hooks/useControlled.js.map +1 -1
  80. package/dist/hooks/useGDS.js +1 -1
  81. package/dist/hooks/useGDS.js.map +1 -1
  82. package/dist/hooks/useStyleObserver.js +1 -1
  83. package/dist/hooks/useStyleObserver.js.map +1 -1
  84. package/dist/tailwind-plugin.d.ts.map +1 -1
  85. package/dist/tailwind-plugin.js +3 -0
  86. package/dist/tailwind-plugin.js.map +1 -1
  87. package/dist/utils/InlineCounter.d.ts +3 -0
  88. package/dist/utils/InlineCounter.d.ts.map +1 -0
  89. package/dist/utils/InlineCounter.js +7 -0
  90. package/dist/utils/InlineCounter.js.map +1 -0
  91. package/dist/utils/RenderCount.d.ts +3 -0
  92. package/dist/utils/RenderCount.d.ts.map +1 -0
  93. package/dist/utils/RenderCount.js +7 -0
  94. package/dist/utils/RenderCount.js.map +1 -0
  95. package/dist/utils/index.d.ts +2 -0
  96. package/dist/utils/index.d.ts.map +1 -1
  97. package/dist/utils/index.js +2 -0
  98. package/dist/utils/index.js.map +1 -1
  99. package/package.json +14 -14
  100. package/src/GDSContext.ts +16 -0
  101. package/src/GDSProvider.tsx +20 -31
  102. package/src/components/Avatar.tsx +3 -2
  103. package/src/components/Breadcrumbs.parts.tsx +1 -1
  104. package/src/components/Button.tsx +113 -107
  105. package/src/components/Card.tsx +2 -2
  106. package/src/components/CopyButton.tsx +49 -25
  107. package/src/components/Input.tsx +1 -1
  108. package/src/components/Link.tsx +2 -2
  109. package/src/components/Menu.parts.tsx +78 -73
  110. package/src/components/Modal.parts.tsx +26 -31
  111. package/src/components/Pane.meta.ts +31 -0
  112. package/src/components/Pane.parts.tsx +713 -0
  113. package/src/components/Pane.tsx +17 -0
  114. package/src/components/Search.tsx +1 -1
  115. package/src/components/Tooltip.parts.tsx +95 -80
  116. package/src/components/base/ButtonOrLink.parts.tsx +71 -51
  117. package/src/components/base/ButtonOrLink.tsx +1 -0
  118. package/src/components/base/MaybeButtonOrLink.tsx +26 -5
  119. package/src/components/base/Presence.tsx +1375 -0
  120. package/src/components/base/index.ts +1 -0
  121. package/src/components/index.ts +10 -0
  122. package/src/hooks/useCSSProp.ts +1 -1
  123. package/src/hooks/useControlled.ts +16 -8
  124. package/src/hooks/useGDS.ts +1 -1
  125. package/src/hooks/useStyleObserver.ts +1 -1
  126. package/src/tailwind-plugin.ts +3 -0
  127. package/src/utils/InlineCounter.tsx +17 -0
  128. package/src/utils/RenderCount.tsx +7 -0
  129. package/src/utils/index.ts +2 -0
@@ -30,7 +30,6 @@ import { Render, type RenderFnProps, type RenderProp } from './base/Render.tsx'
30
30
  import { Button } from './Button.tsx'
31
31
  import { Checkbox } from './Checkbox.tsx'
32
32
  import { MenuMeta, type MenuItemMeta } from './Menu.meta.js'
33
- import { Tooltip } from './Tooltip.tsx'
34
33
 
35
34
  const MenuTriggerContext = createContext<boolean>(false)
36
35
 
@@ -66,17 +65,15 @@ declare namespace MenuProps {
66
65
  * `alignOffset`, `matchTriggerWidth`, and `matchTriggerHeight`).
67
66
  */
68
67
  trigger?:
69
- | RenderProp<
70
- { open: boolean; setOpen: (open: boolean) => void; openMenu: () => void },
71
- { disabled?: boolean | undefined }
72
- >
68
+ | RenderProp<{ open: boolean; setOpen: (open: boolean) => void; openMenu: () => void }>
73
69
  | undefined
74
70
  /**
75
71
  * Controls how the menu is opened when `trigger` is an element. Ignored if `trigger` is a
76
72
  * function, since you can fully customize the interaction that opens the menu (e.g.
77
- * `trigger={(props, { openMenu }) => <button {...props} onMouseEnter={openMenu} />}`).
73
+ * `trigger={(props, { openMenu }) => <button {...props} onMouseEnter={openMenu} />}`). Also
74
+ * ignored for submenus, which always open on hover.
78
75
  *
79
- * @default 'click' // or for nested menus, 'hover'
76
+ * @default 'click'
80
77
  */
81
78
  triggerMode?: 'click' | 'hover' | undefined
82
79
  /** Defaults to `true` if `trigger` is not provided, or `undefined` otherwise. */
@@ -97,7 +94,7 @@ export type MenuProps<T extends OptionValue = OptionValue> =
97
94
  export function MenuRoot<T extends OptionValue>({
98
95
  ref: passedRef,
99
96
  trigger,
100
- triggerMode: passedTriggerMode,
97
+ triggerMode,
101
98
  side,
102
99
  gap,
103
100
  align,
@@ -138,12 +135,50 @@ export function MenuRoot<T extends OptionValue>({
138
135
  },
139
136
  )
140
137
 
141
- const isNested = useContext(MenuGroupContext) !== null
138
+ const isSubmenu = useContext(MenuGroupContext) !== null
139
+ const MenuRoot = isSubmenu ? Menu.SubmenuRoot : Menu.Root
140
+
142
141
  const hasTrigger = trigger !== undefined
143
- const triggerIsElement = hasTrigger && typeof trigger !== 'function'
144
- const triggerMode = passedTriggerMode ?? (isNested ? 'hover' : 'click')
145
- const MenuRoot = isNested ? Menu.SubmenuRoot : Menu.Root
146
- const MenuTrigger = isNested ? Menu.SubmenuTrigger : Menu.Trigger
142
+ const triggerElement = (() => {
143
+ if (!hasTrigger) return null
144
+ const renderTrigger =
145
+ typeof trigger !== 'function'
146
+ ? trigger
147
+ : (renderProps: RenderFnProps) => {
148
+ // Don't pass event handlers and other potentially too specific props (e.g. `type`)
149
+ const filteredRenderProps = Object.fromEntries(
150
+ Object.entries(renderProps).filter(
151
+ ([prop]) =>
152
+ prop === 'ref' ||
153
+ prop === 'id' ||
154
+ prop === 'className' ||
155
+ prop.startsWith('aria-') ||
156
+ prop.startsWith('data-'),
157
+ ),
158
+ )
159
+ return trigger(filteredRenderProps, {
160
+ open,
161
+ setOpen,
162
+ openMenu: () => setOpen(true),
163
+ })
164
+ }
165
+ if (isSubmenu) {
166
+ return <Render render={renderTrigger} />
167
+ } else {
168
+ return (
169
+ <Menu.Trigger
170
+ nativeButton
171
+ openOnHover={triggerMode === 'hover'}
172
+ data-open-on-hover={triggerMode === 'hover' || undefined}
173
+ className={`
174
+ u:i:open:data-open-on-hover:state-hover
175
+ u:iii:open:not-data-open-on-hover:state-active
176
+ `}
177
+ render={renderTrigger}
178
+ />
179
+ )
180
+ }
181
+ })()
147
182
 
148
183
  const [open, setOpen] = useControlled(
149
184
  controlledOpen ?? (!hasTrigger ? true : undefined),
@@ -158,15 +193,18 @@ export function MenuRoot<T extends OptionValue>({
158
193
  return (
159
194
  <div
160
195
  ref={cssPropsPolyfillRef}
161
- data-nested={isNested || undefined}
196
+ data-is-submenu={isSubmenu || undefined}
162
197
  data-has-trigger={hasTrigger || undefined}
163
198
  className={cn(
164
199
  `gds-menu root-contents [anchor-scope:all]
165
200
  not-data-has-trigger:**:data-base-ui-focus-guard:hidden
166
201
  u:rounded-12
167
- u:data-nested:default-align-offset-[-2]
168
- u:data-nested:default-gap-3
169
- u:data-nested:default-side-end`,
202
+ u:data-is-submenu:default-align-offset-[-2]
203
+ u:data-is-submenu:default-gap-3
204
+ u:data-is-submenu:default-side-end`,
205
+ // Very imperfect workaround for https://bugs.webkit.org/show_bug.cgi?id=301871
206
+ `safari:root-block
207
+ i:safari:not-data-is-submenu:size-max`,
170
208
  className,
171
209
  )}
172
210
  {...getCSSPropsAttributes(
@@ -184,48 +222,11 @@ export function MenuRoot<T extends OptionValue>({
184
222
  // A menu with no trigger can still be opened/closed by the consumer, but it should not send events
185
223
  if (hasTrigger) setOpen(newOpen)
186
224
  }}
187
- modal={!isNested ? hasTrigger : (undefined as never)}
225
+ modal={!isSubmenu ? hasTrigger : (undefined as never)}
188
226
  closeParentOnEsc
189
227
  >
190
228
  <MenuTriggerContext.Provider value={true}>
191
- {hasTrigger ? (
192
- // Hoist any tooltip used in `trigger` for its interactions to work properly
193
- <Tooltip>
194
- <MenuTrigger
195
- nativeButton={!isNested}
196
- // Ensure submenu triggers get disabled properly (see https://github.com/mui/base-ui/issues/3850)
197
- disabled={triggerIsElement && trigger.props.disabled === true}
198
- openOnHover={triggerMode === 'hover'}
199
- data-open-on-hover={triggerMode === 'hover' || undefined}
200
- className={`
201
- u:i:open:data-open-on-hover:state-hover
202
- u:iii:open:not-data-open-on-hover:state-active
203
- `}
204
- render={
205
- triggerIsElement
206
- ? trigger
207
- : (renderProps: RenderFnProps) => {
208
- // Don't pass event handlers and other potentially too specific props (e.g. `type`)
209
- const filteredRenderProps = Object.fromEntries(
210
- Object.entries(renderProps).filter(
211
- ([prop]) =>
212
- prop === 'ref' ||
213
- prop === 'id' ||
214
- prop === 'className' ||
215
- prop.startsWith('aria-') ||
216
- prop.startsWith('data-'),
217
- ),
218
- )
219
- return trigger(filteredRenderProps, {
220
- open,
221
- setOpen,
222
- openMenu: () => setOpen(true),
223
- })
224
- }
225
- }
226
- />
227
- </Tooltip>
228
- ) : (
229
+ {triggerElement ?? (
229
230
  /**
230
231
  * Base UI doesn't explicitly support menus with no `Menu.Trigger`, `Menu.Portal`, or
231
232
  * `Menu.Positioner`, so we work around it. We render an invisible trigger to satisfy
@@ -258,11 +259,13 @@ export function MenuRoot<T extends OptionValue>({
258
259
  !hasTrigger
259
260
  ? portalRef
260
261
  : /**
261
- * Specifying `document.body` because while it is the default for top-level menus, a nested menu
262
+ * Specifying `document.body` because while it is the default for top-level menus, a submenu
262
263
  * defaults to using the same portal as its parent, which is not desirable when the parent is
263
- * rendered inline (it can cause the nested menu to appear behind other elements).
264
+ * rendered inline (it can cause the submenu to appear behind other elements).
264
265
  */
265
- document?.body
266
+ typeof document !== 'undefined'
267
+ ? document.body
268
+ : undefined
266
269
  }
267
270
  >
268
271
  <Menu.Positioner
@@ -631,7 +634,7 @@ export function MenuItem<T extends OptionValue>({
631
634
  (groupContext !== null && groupContext !== 'root' ? groupContext.type : undefined) ??
632
635
  'plain')
633
636
 
634
- const renderContent = (renderProps: RenderFnProps) => {
637
+ const renderItem = (renderProps: RenderFnProps) => {
635
638
  let addonBefore: AddonValue = passedAddonBefore
636
639
  let addonInside: AddonValue = undefined
637
640
  let addonAfter: AddonValue = passedAddonAfter
@@ -677,17 +680,10 @@ export function MenuItem<T extends OptionValue>({
677
680
  }
678
681
  return (
679
682
  <Render
680
- render={
681
- href !== undefined ? (
682
- <ButtonOrLink href={href} target={target} disabled={disabled} />
683
- ) : (
684
- <div />
685
- )
686
- }
683
+ render={<ButtonOrLink href={href} target={target as undefined} />}
687
684
  data-type={type}
688
685
  data-variant={variant}
689
686
  className={cn(
690
- // TODO: Replace `active:` with `data-pressed:` when https://github.com/mui/base-ui/issues/1726 is added, to style Space presses as well
691
687
  `gds-menu-item root-flex w-full items-center outline-0 select-none u:rounded-6 u:p-2 u:text-16
692
688
  u:state-idle
693
689
  u:checked:state-checked
@@ -696,7 +692,7 @@ export function MenuItem<T extends OptionValue>({
696
692
  u:disabled:state-disabled
697
693
  u:data-highlighted:state-hover
698
694
  u:data-[variant=danger]:text-status-error-default
699
- u:ii:active:state-active
695
+ u:ii:not-disabled:active:state-active
700
696
  u:ii:data-[type=submenu-trigger]:not-[a]:cursor-default
701
697
  u:ii:data-[type=submenu-trigger]:not-[a]:active:state-hover
702
698
  ${/* Disabled styles */ ''}
@@ -749,9 +745,10 @@ export function MenuItem<T extends OptionValue>({
749
745
  }
750
746
 
751
747
  if (type === 'submenu-trigger') {
752
- return renderContent(baseProps)
748
+ return <Menu.SubmenuTrigger nativeButton disabled={disabled} render={renderItem(baseProps)} />
753
749
  }
754
750
 
751
+ const nativeButton = href === undefined
755
752
  const onKeyDown: ComponentProps<typeof Menu.Item>['onKeyDown'] = (event) => {
756
753
  props.onKeyDown?.(event)
757
754
  /**
@@ -768,13 +765,14 @@ export function MenuItem<T extends OptionValue>({
768
765
  return (
769
766
  <CheckboxItem
770
767
  {...baseProps}
768
+ nativeButton={nativeButton}
771
769
  closeOnClick={false}
772
770
  checked={checked}
773
771
  defaultChecked={defaultChecked}
774
772
  onCheckedChange={onChange}
775
773
  onKeyDown={onKeyDown}
776
774
  disabled={disabled}
777
- render={renderContent}
775
+ render={renderItem}
778
776
  />
779
777
  )
780
778
  }
@@ -783,17 +781,24 @@ export function MenuItem<T extends OptionValue>({
783
781
  return (
784
782
  <Menu.RadioItem
785
783
  {...baseProps}
784
+ nativeButton={nativeButton}
786
785
  closeOnClick={true}
787
786
  value={passedValue !== undefined ? passedValue : autoValue}
788
787
  onKeyDown={onKeyDown}
789
788
  disabled={disabled}
790
- render={renderContent}
789
+ render={renderItem}
791
790
  />
792
791
  )
793
792
  }
794
793
 
795
794
  return (
796
- <Menu.Item {...baseProps} onKeyDown={onKeyDown} disabled={disabled} render={renderContent} />
795
+ <Menu.Item
796
+ {...baseProps}
797
+ nativeButton={nativeButton}
798
+ onKeyDown={onKeyDown}
799
+ disabled={disabled}
800
+ render={renderItem}
801
+ />
797
802
  )
798
803
  }
799
804
  MenuItem.displayName = 'Menu.Item'
@@ -24,7 +24,6 @@ import { ButtonGroup } from './ButtonGroup.tsx'
24
24
  import { Divider } from './Divider.tsx'
25
25
  import { ModalMeta } from './Modal.meta.ts'
26
26
  import { Stepper, type StepperProps } from './Stepper.tsx'
27
- import { Tooltip } from './Tooltip.tsx'
28
27
 
29
28
  const ModalContext = createContext<{
30
29
  open: boolean
@@ -125,7 +124,6 @@ export const ModalRoot = <
125
124
  `)
126
125
 
127
126
  const hasTrigger = trigger !== undefined
128
- const triggerIsElement = hasTrigger && typeof trigger !== 'function'
129
127
 
130
128
  const [open, setOpen] = useControlled(controlledOpen, defaultOpen ?? !hasTrigger, onOpenChange)
131
129
 
@@ -184,35 +182,32 @@ export const ModalRoot = <
184
182
  disablePointerDismissal={!dismissible}
185
183
  >
186
184
  {hasTrigger ? (
187
- // Hoist any tooltip used in `trigger` for its interactions to work properly
188
- <Tooltip>
189
- <Dialog.Trigger
190
- render={
191
- triggerIsElement
192
- ? trigger
193
- : (renderProps) => {
194
- // Don't pass event handlers and other potentially too specific props (e.g. `type`)
195
- const filteredRenderProps = Object.fromEntries(
196
- Object.entries(renderProps).filter(
197
- ([prop]) =>
198
- prop === 'ref' ||
199
- prop === 'id' ||
200
- prop === 'className' ||
201
- prop.startsWith('aria-') ||
202
- prop.startsWith('data-'),
203
- ),
204
- )
205
- return trigger(filteredRenderProps, {
206
- open,
207
- setOpen,
208
- openModal: () => setOpen(true),
209
- currentStep,
210
- setCurrentStep,
211
- })
212
- }
213
- }
214
- />
215
- </Tooltip>
185
+ <Dialog.Trigger
186
+ render={
187
+ typeof trigger !== 'function'
188
+ ? trigger
189
+ : (renderProps) => {
190
+ // Don't pass event handlers and other potentially too specific props (e.g. `type`)
191
+ const filteredRenderProps = Object.fromEntries(
192
+ Object.entries(renderProps).filter(
193
+ ([prop]) =>
194
+ prop === 'ref' ||
195
+ prop === 'id' ||
196
+ prop === 'className' ||
197
+ prop.startsWith('aria-') ||
198
+ prop.startsWith('data-'),
199
+ ),
200
+ )
201
+ return trigger(filteredRenderProps, {
202
+ open,
203
+ setOpen,
204
+ openModal: () => setOpen(true),
205
+ currentStep,
206
+ setCurrentStep,
207
+ })
208
+ }
209
+ }
210
+ />
216
211
  ) : null}
217
212
  {/* Ensure the CSS props polyfill works even when the modal is not mounted */}
218
213
  <Portal>
@@ -0,0 +1,31 @@
1
+ import { createComponent } from '@graphprotocol/gds-css'
2
+
3
+ export const PaneContainerMeta = createComponent('PaneContainer', {
4
+ isolate: 'allow-inheritance',
5
+ cssProps: {
6
+ /** @default 'horizontal' */
7
+ orientation: {
8
+ type: 'values',
9
+ values: ['horizontal', 'vertical'],
10
+ defaultValue: 'horizontal',
11
+ },
12
+ },
13
+ })
14
+
15
+ export const PaneMeta = createComponent('Pane', {
16
+ isolate: 'allow-inheritance',
17
+ cssProps: {
18
+ /** @default 'docked' */
19
+ layout: {
20
+ type: 'values',
21
+ values: ['docked', 'overlay'],
22
+ defaultValue: 'docked',
23
+ },
24
+ /** @default // dynamic based on position and container orientation */
25
+ side: {
26
+ type: 'values',
27
+ values: ['top', 'bottom', 'start', 'end'],
28
+ defaultValue: 'start',
29
+ },
30
+ },
31
+ })