@graphprotocol/gds-react 0.1.1 → 0.1.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.
- package/dist/components/Avatar.d.ts.map +1 -1
- package/dist/components/Avatar.js +5 -1
- package/dist/components/Avatar.js.map +1 -1
- package/dist/components/Breadcrumbs.parts.js +2 -2
- package/dist/components/Button.js +8 -4
- package/dist/components/Button.js.map +1 -1
- package/dist/components/Card.js +1 -1
- package/dist/components/Card.js.map +1 -1
- package/dist/components/Chip.d.ts +6 -0
- package/dist/components/Chip.d.ts.map +1 -0
- package/dist/components/Chip.js +5 -0
- package/dist/components/Chip.js.map +1 -0
- package/dist/components/Chip.meta.d.ts +17 -0
- package/dist/components/Chip.meta.d.ts.map +1 -0
- package/dist/components/Chip.meta.js +22 -0
- package/dist/components/Chip.meta.js.map +1 -0
- package/dist/components/Chip.parts.d.ts +52 -0
- package/dist/components/Chip.parts.d.ts.map +1 -0
- package/dist/components/Chip.parts.js +122 -0
- package/dist/components/Chip.parts.js.map +1 -0
- package/dist/components/DescriptionList.parts.js +1 -1
- package/dist/components/SegmentedControl.parts.d.ts.map +1 -1
- package/dist/components/SegmentedControl.parts.js +12 -23
- package/dist/components/SegmentedControl.parts.js.map +1 -1
- package/dist/components/TabSet.parts.js +1 -1
- package/dist/components/ToggleButton.d.ts +0 -2
- package/dist/components/ToggleButton.d.ts.map +1 -1
- package/dist/components/ToggleButton.js.map +1 -1
- package/dist/components/Tooltip.parts.d.ts.map +1 -1
- package/dist/components/Tooltip.parts.js +1 -1
- package/dist/components/Tooltip.parts.js.map +1 -1
- package/dist/components/base/ButtonOrLink.parts.d.ts +8 -5
- package/dist/components/base/ButtonOrLink.parts.d.ts.map +1 -1
- package/dist/components/base/ButtonOrLink.parts.js +19 -16
- package/dist/components/base/ButtonOrLink.parts.js.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +2 -0
- package/dist/components/index.js.map +1 -1
- package/dist/tailwind-plugin.d.ts.map +1 -1
- package/dist/tailwind-plugin.js +3 -0
- package/dist/tailwind-plugin.js.map +1 -1
- package/package.json +7 -7
- package/src/components/Avatar.tsx +5 -1
- package/src/components/Breadcrumbs.parts.tsx +2 -2
- package/src/components/Button.tsx +13 -3
- package/src/components/Card.tsx +1 -1
- package/src/components/Chip.meta.ts +23 -0
- package/src/components/Chip.parts.tsx +329 -0
- package/src/components/Chip.tsx +7 -0
- package/src/components/DescriptionList.parts.tsx +1 -1
- package/src/components/SegmentedControl.parts.tsx +34 -35
- package/src/components/TabSet.parts.tsx +1 -1
- package/src/components/ToggleButton.tsx +0 -2
- package/src/components/Tooltip.parts.tsx +0 -1
- package/src/components/base/ButtonOrLink.parts.tsx +38 -21
- package/src/components/index.ts +2 -0
- package/src/tailwind-plugin.ts +3 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext, type ComponentProps, type ReactNode } from 'react'
|
|
4
|
+
import { Checkbox } from '@base-ui/react/checkbox'
|
|
5
|
+
import { CheckboxGroup } from '@base-ui/react/checkbox-group'
|
|
6
|
+
import { Radio } from '@base-ui/react/radio'
|
|
7
|
+
import { RadioGroup } from '@base-ui/react/radio-group'
|
|
8
|
+
|
|
9
|
+
import type { GDSComponentProps } from '@graphprotocol/gds-css'
|
|
10
|
+
|
|
11
|
+
import { useAutoValue, useCSSPropsPolyfill, useCSSState, useGDS } from '../hooks/index.ts'
|
|
12
|
+
import { cn, getCSSPropsAttributes, splitProps } from '../utils/index.ts'
|
|
13
|
+
import { renderAddon, type AddonValue } from './base/Addon.tsx'
|
|
14
|
+
import { ButtonOrLink, type ButtonOrLinkProps } from './base/ButtonOrLink.tsx'
|
|
15
|
+
import { ChipGroupMeta, ChipMeta } from './Chip.meta.ts'
|
|
16
|
+
|
|
17
|
+
type ExtractFn<T> = T extends (...args: infer A) => infer R ? (...args: A) => R : never
|
|
18
|
+
|
|
19
|
+
export declare namespace ChipProps {
|
|
20
|
+
interface BaseProps extends GDSComponentProps<typeof ChipMeta> {
|
|
21
|
+
/** Name used to submit the chip's value when inside a `<Chip.Group type="checkbox">` in a form. */
|
|
22
|
+
name?: string | undefined
|
|
23
|
+
/** Value used to identify this chip when inside a `<Chip.Group>`. */
|
|
24
|
+
value?: string | undefined
|
|
25
|
+
addonBefore?: AddonValue | undefined
|
|
26
|
+
addonAfter?: AddonValue | undefined
|
|
27
|
+
count?: ReactNode | undefined
|
|
28
|
+
}
|
|
29
|
+
interface ButtonProps
|
|
30
|
+
extends
|
|
31
|
+
BaseProps,
|
|
32
|
+
Omit<ButtonOrLinkProps.ButtonProps, 'value'>,
|
|
33
|
+
ButtonOrLinkProps.DisableableProps {}
|
|
34
|
+
interface LinkProps
|
|
35
|
+
extends BaseProps, ButtonOrLinkProps.LinkProps, ButtonOrLinkProps.DisableableProps {}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type ChipProps = ChipProps.ButtonProps | ChipProps.LinkProps
|
|
39
|
+
|
|
40
|
+
interface ChipGroupContextValue {
|
|
41
|
+
type: 'checkbox' | 'radio'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const ChipGroupContext = createContext<ChipGroupContextValue | null>(null)
|
|
45
|
+
|
|
46
|
+
export function ChipRoot({
|
|
47
|
+
size,
|
|
48
|
+
name,
|
|
49
|
+
value: passedValue,
|
|
50
|
+
addonBefore,
|
|
51
|
+
addonAfter,
|
|
52
|
+
count,
|
|
53
|
+
disabled = false,
|
|
54
|
+
className,
|
|
55
|
+
style,
|
|
56
|
+
children,
|
|
57
|
+
...props
|
|
58
|
+
}: ChipProps) {
|
|
59
|
+
useGDS()
|
|
60
|
+
|
|
61
|
+
const { rootProps, nestedProps } = splitProps(props)
|
|
62
|
+
|
|
63
|
+
const autoValue = useAutoValue(children)
|
|
64
|
+
const value = passedValue !== undefined ? passedValue : autoValue
|
|
65
|
+
|
|
66
|
+
const groupContext = useContext(ChipGroupContext)
|
|
67
|
+
|
|
68
|
+
const [stateRef, state] = useCSSState({
|
|
69
|
+
pointer: undefined,
|
|
70
|
+
focus: undefined,
|
|
71
|
+
checked: undefined,
|
|
72
|
+
disabled: Boolean(disabled),
|
|
73
|
+
})
|
|
74
|
+
const [cssPropsPolyfillStateRef, cssPropsPolyfillAttributes] = useCSSPropsPolyfill(
|
|
75
|
+
ChipMeta,
|
|
76
|
+
{ size },
|
|
77
|
+
{ ref: stateRef },
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div
|
|
82
|
+
ref={cssPropsPolyfillStateRef}
|
|
83
|
+
data-type={groupContext?.type ?? 'plain'}
|
|
84
|
+
className={cn(
|
|
85
|
+
`gds-chip root-flex flex-col u:size-max u:max-w-full
|
|
86
|
+
u:has-nested-checked/chip-ref:expose-checked
|
|
87
|
+
u:has-nested-hover/chip-ref:expose-hover
|
|
88
|
+
u:has-nested-focus-visible/chip-ref:expose-focus
|
|
89
|
+
u:has-nested-disabled/chip-ref:expose-disabled
|
|
90
|
+
u:i:has-nested-checked/chip-ref:data-[type=radio]:state-idle`,
|
|
91
|
+
className,
|
|
92
|
+
)}
|
|
93
|
+
{...state.exposedAttributes}
|
|
94
|
+
{...state.polyfillAttributes}
|
|
95
|
+
{...getCSSPropsAttributes(ChipMeta, { size }, style)}
|
|
96
|
+
{...cssPropsPolyfillAttributes}
|
|
97
|
+
{...rootProps}
|
|
98
|
+
>
|
|
99
|
+
{(() => {
|
|
100
|
+
if (!groupContext || props.href !== undefined) {
|
|
101
|
+
return renderButtonOrLink({})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (groupContext.type === 'checkbox') {
|
|
105
|
+
return (
|
|
106
|
+
<Checkbox.Root
|
|
107
|
+
name={name ?? (undefined as never)}
|
|
108
|
+
value={typeof value === 'string' ? value : (undefined as never)}
|
|
109
|
+
disabled={Boolean(disabled)}
|
|
110
|
+
nativeButton
|
|
111
|
+
render={renderButtonOrLink}
|
|
112
|
+
/>
|
|
113
|
+
)
|
|
114
|
+
} else {
|
|
115
|
+
return (
|
|
116
|
+
<Radio.Root
|
|
117
|
+
value={value}
|
|
118
|
+
disabled={Boolean(disabled)}
|
|
119
|
+
nativeButton
|
|
120
|
+
render={renderButtonOrLink}
|
|
121
|
+
/>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function renderButtonOrLink({
|
|
126
|
+
tabIndex: _tabIndex,
|
|
127
|
+
defaultChecked: _defaultChecked,
|
|
128
|
+
...renderProps
|
|
129
|
+
}: Parameters<
|
|
130
|
+
ExtractFn<ComponentProps<typeof Checkbox.Root | typeof Radio.Root>['render']>
|
|
131
|
+
>[0]) {
|
|
132
|
+
return (
|
|
133
|
+
<ButtonOrLink
|
|
134
|
+
{...renderProps}
|
|
135
|
+
disabled={disabled}
|
|
136
|
+
className={cn(
|
|
137
|
+
renderProps.className,
|
|
138
|
+
`nested/chip-ref flex grow items-center justify-center
|
|
139
|
+
rounded-full border border-default text-muted transition
|
|
140
|
+
@state-checked/chip:border-brand-muted
|
|
141
|
+
@state-checked/chip:bg-brand-subtlest
|
|
142
|
+
@state-checked/chip:text-default
|
|
143
|
+
@state-hover/chip:border-strong
|
|
144
|
+
@state-hover/chip:text-default
|
|
145
|
+
@state-hover/chip:@state-checked/chip:border-brand-default
|
|
146
|
+
@state-hover/chip:@state-checked/chip:bg-brand-subtler
|
|
147
|
+
@prop-size-small/chip:h-6
|
|
148
|
+
@prop-size-small/chip:px-1
|
|
149
|
+
@prop-size-small/chip:text-12
|
|
150
|
+
@prop-size-medium/chip:h-8
|
|
151
|
+
@prop-size-medium/chip:px-1.5
|
|
152
|
+
@prop-size-medium/chip:text-14
|
|
153
|
+
@prop-size-large/chip:h-10
|
|
154
|
+
@prop-size-large/chip:px-2
|
|
155
|
+
@prop-size-large/chip:text-16
|
|
156
|
+
u:*:transition-opacity
|
|
157
|
+
u:*:@state-disabled/chip:opacity-disabled
|
|
158
|
+
u:*:@state-disabled/chip:grayscale
|
|
159
|
+
u:@prop-size-small/chip:addon-small
|
|
160
|
+
u:@prop-size-medium/chip:addon-medium
|
|
161
|
+
u:@prop-size-large/chip:addon-large
|
|
162
|
+
i:@state-disabled/chip:border-muted
|
|
163
|
+
i:@state-disabled/chip:bg-transparent
|
|
164
|
+
i:@state-disabled/chip:text-muted
|
|
165
|
+
i:@state-disabled/chip:@state-checked/chip:border-transparent
|
|
166
|
+
i:@state-disabled/chip:@state-checked/chip:bg-brand-subtlest`,
|
|
167
|
+
)}
|
|
168
|
+
{...nestedProps}
|
|
169
|
+
>
|
|
170
|
+
{/* Ensure rounded corners are clickable */}
|
|
171
|
+
<span className="absolute inset-0 not-in-clickable:hidden" />
|
|
172
|
+
{addonBefore ? <ChipAddon side="before">{renderAddon(addonBefore)}</ChipAddon> : null}
|
|
173
|
+
<span
|
|
174
|
+
className={`
|
|
175
|
+
flex items-baseline
|
|
176
|
+
@prop-size-small/chip:gap-0.5
|
|
177
|
+
@prop-size-small/chip:px-0.5
|
|
178
|
+
@prop-size-medium/chip:gap-1
|
|
179
|
+
@prop-size-medium/chip:px-1
|
|
180
|
+
@prop-size-large/chip:gap-1
|
|
181
|
+
@prop-size-large/chip:px-1
|
|
182
|
+
`}
|
|
183
|
+
>
|
|
184
|
+
<span className="truncate">{children}</span>
|
|
185
|
+
{count ? (
|
|
186
|
+
<span
|
|
187
|
+
className={`
|
|
188
|
+
max-w-full shrink-0 truncate text-subtle transition
|
|
189
|
+
@state-enabled/chip:@state-checked/chip:text-current
|
|
190
|
+
@state-enabled/chip:@state-hover/chip:text-current
|
|
191
|
+
@prop-size-small/chip:text-10
|
|
192
|
+
@prop-size-medium/chip:text-12
|
|
193
|
+
@prop-size-large/chip:text-14
|
|
194
|
+
`}
|
|
195
|
+
>
|
|
196
|
+
{count}
|
|
197
|
+
</span>
|
|
198
|
+
) : null}
|
|
199
|
+
</span>
|
|
200
|
+
{addonAfter ? <ChipAddon side="after">{renderAddon(addonAfter)}</ChipAddon> : null}
|
|
201
|
+
</ButtonOrLink>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
})()}
|
|
205
|
+
</div>
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
ChipRoot.displayName = 'Chip'
|
|
209
|
+
|
|
210
|
+
function ChipAddon({
|
|
211
|
+
side,
|
|
212
|
+
className,
|
|
213
|
+
children,
|
|
214
|
+
...props
|
|
215
|
+
}: ComponentProps<'span'> & { side: 'before' | 'after' }) {
|
|
216
|
+
return (
|
|
217
|
+
<span
|
|
218
|
+
data-side={side}
|
|
219
|
+
className={cn(
|
|
220
|
+
`gds-addon root-flex items-center justify-center u:h-(--gds-addon-size) u:min-w-(--gds-addon-size) u:shrink-0
|
|
221
|
+
u:has-avatar-group:data-[side=after]:ps-0.5
|
|
222
|
+
u:has-tag:data-[side=after]:ps-0.5
|
|
223
|
+
u:has-avatar-group:data-[side=before]:pe-0.5
|
|
224
|
+
u:has-tag:data-[side=before]:pe-0.5`,
|
|
225
|
+
className,
|
|
226
|
+
)}
|
|
227
|
+
{...props}
|
|
228
|
+
>
|
|
229
|
+
{children}
|
|
230
|
+
</span>
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export declare namespace ChipGroupProps {
|
|
235
|
+
interface BaseProps
|
|
236
|
+
extends Omit<ComponentProps<'div'>, 'onChange'>, GDSComponentProps<typeof ChipGroupMeta> {}
|
|
237
|
+
interface CheckboxProps extends BaseProps {
|
|
238
|
+
type: 'checkbox'
|
|
239
|
+
name?: undefined
|
|
240
|
+
/** Array of checked/selected chip values. */
|
|
241
|
+
value?: string[] | undefined
|
|
242
|
+
defaultValue?: string[] | undefined
|
|
243
|
+
onChange?: ((value: string[]) => void) | undefined
|
|
244
|
+
}
|
|
245
|
+
interface RadioProps extends BaseProps {
|
|
246
|
+
type: 'radio'
|
|
247
|
+
/** Name used to submit the checked/selected chip value when inside a form. */
|
|
248
|
+
name?: string | undefined
|
|
249
|
+
/** Checked/selected chip value. */
|
|
250
|
+
value?: string | undefined
|
|
251
|
+
defaultValue?: string | undefined
|
|
252
|
+
onChange?: ((value: string) => void) | undefined
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export type ChipGroupProps = ChipGroupProps.CheckboxProps | ChipGroupProps.RadioProps
|
|
257
|
+
|
|
258
|
+
export function ChipGroup({
|
|
259
|
+
ref: passedRef,
|
|
260
|
+
type,
|
|
261
|
+
name,
|
|
262
|
+
value,
|
|
263
|
+
defaultValue,
|
|
264
|
+
onChange,
|
|
265
|
+
size,
|
|
266
|
+
className,
|
|
267
|
+
style,
|
|
268
|
+
children,
|
|
269
|
+
...props
|
|
270
|
+
}: ChipGroupProps) {
|
|
271
|
+
useGDS()
|
|
272
|
+
|
|
273
|
+
if (props['aria-label'] === undefined && props['aria-labelledby'] === undefined) {
|
|
274
|
+
// oxlint-disable-next-line no-console
|
|
275
|
+
console.warn(
|
|
276
|
+
'[Chip.Group] One of `aria-label` or `aria-labelledby` should be set for accessibility',
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const [cssPropsPolyfillPassedRef, cssPropsPolyfillAttributes, cssProps] = useCSSPropsPolyfill(
|
|
281
|
+
ChipGroupMeta,
|
|
282
|
+
{ size },
|
|
283
|
+
{ ref: passedRef, returnPropValues: { size } },
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
const sharedProps = {
|
|
287
|
+
ref: cssPropsPolyfillPassedRef,
|
|
288
|
+
'data-size': cssProps.size,
|
|
289
|
+
className: cn(
|
|
290
|
+
`gds-chip-group root-flex u:flex-wrap
|
|
291
|
+
u:data-[size=large]:gap-2
|
|
292
|
+
u:data-[size=medium]:gap-1.5
|
|
293
|
+
u:data-[size=small]:gap-1
|
|
294
|
+
u:**:chip:@prop-size-small/chip-group:prop-size-small
|
|
295
|
+
u:**:chip:@prop-size-medium/chip-group:prop-size-medium
|
|
296
|
+
u:**:chip:@prop-size-large/chip-group:prop-size-large`,
|
|
297
|
+
className,
|
|
298
|
+
),
|
|
299
|
+
...getCSSPropsAttributes(ChipGroupMeta, { size }, style),
|
|
300
|
+
...cssPropsPolyfillAttributes,
|
|
301
|
+
...props,
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return (
|
|
305
|
+
<ChipGroupContext.Provider value={{ type }}>
|
|
306
|
+
{type === 'checkbox' ? (
|
|
307
|
+
<CheckboxGroup
|
|
308
|
+
{...sharedProps}
|
|
309
|
+
{...(value !== undefined && { value })}
|
|
310
|
+
{...(defaultValue !== undefined && { defaultValue })}
|
|
311
|
+
{...(onChange !== undefined && { onValueChange: onChange })}
|
|
312
|
+
>
|
|
313
|
+
{children}
|
|
314
|
+
</CheckboxGroup>
|
|
315
|
+
) : (
|
|
316
|
+
<RadioGroup
|
|
317
|
+
name={name ?? (undefined as never)}
|
|
318
|
+
{...sharedProps}
|
|
319
|
+
{...(value !== undefined && { value })}
|
|
320
|
+
{...(defaultValue !== undefined && { defaultValue })}
|
|
321
|
+
{...(onChange !== undefined && { onValueChange: onChange as (value: unknown) => void })}
|
|
322
|
+
>
|
|
323
|
+
{children}
|
|
324
|
+
</RadioGroup>
|
|
325
|
+
)}
|
|
326
|
+
</ChipGroupContext.Provider>
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
ChipGroup.displayName = 'Chip.Group'
|
|
@@ -189,7 +189,7 @@ export function DescriptionListItem({
|
|
|
189
189
|
<span
|
|
190
190
|
className={`
|
|
191
191
|
absolute inset-y-0 flex items-center justify-center
|
|
192
|
-
not-
|
|
192
|
+
group-not-data-loading/description-list-item:hidden
|
|
193
193
|
@prop-orientation-horizontal/description-list:start-0
|
|
194
194
|
@prop-orientation-vertical/description-list:end-0
|
|
195
195
|
@prop-orientation-vertical/description-list:w-(--gds-addon-size)
|
|
@@ -53,6 +53,8 @@ export function SegmentedControlRoot<T extends OptionValue>({
|
|
|
53
53
|
children,
|
|
54
54
|
...props
|
|
55
55
|
}: SegmentedControlProps<T>) {
|
|
56
|
+
useGDS()
|
|
57
|
+
|
|
56
58
|
if (props['aria-label'] === undefined && props['aria-labelledby'] === undefined) {
|
|
57
59
|
// oxlint-disable-next-line no-console
|
|
58
60
|
console.warn(
|
|
@@ -140,22 +142,20 @@ export function SegmentedControlOption<T extends OptionValue>({
|
|
|
140
142
|
children,
|
|
141
143
|
...props
|
|
142
144
|
}: SegmentedControlOptionProps<T>) {
|
|
143
|
-
useGDS()
|
|
144
|
-
|
|
145
145
|
const { rootProps, nestedProps } = splitProps(props)
|
|
146
146
|
|
|
147
147
|
const buttonRef = useRef<HTMLSpanElement>(null)
|
|
148
148
|
const buttonPassedRef = useMergedRefs(buttonRef, passedRef)
|
|
149
149
|
|
|
150
|
+
const autoValue = useAutoValue(children)
|
|
151
|
+
const value = passedValue !== undefined ? passedValue : autoValue
|
|
152
|
+
|
|
150
153
|
const [stateRef, state] = useCSSState({
|
|
151
154
|
pointer: undefined,
|
|
152
155
|
checked: undefined,
|
|
153
156
|
'is-toggle': undefined,
|
|
154
157
|
})
|
|
155
158
|
|
|
156
|
-
const autoValue = useAutoValue(children)
|
|
157
|
-
const value = passedValue !== undefined ? passedValue : autoValue
|
|
158
|
-
|
|
159
159
|
const { onCollect, collectedContent } = useCollectedTooltip()
|
|
160
160
|
let tooltipProps: Omit<TooltipProps, 'children'> = { content: collectedContent }
|
|
161
161
|
if (tooltip !== undefined) {
|
|
@@ -187,32 +187,32 @@ export function SegmentedControlOption<T extends OptionValue>({
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
return (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
190
|
+
<Tooltip {...tooltipProps}>
|
|
191
|
+
{/**
|
|
192
|
+
* This wrapper is necessary to ensure this component returns a single element (which some Tailwind
|
|
193
|
+
* classes might assume) because `Radio.Root` renders a `<input type="radio">` as a sibling of the
|
|
194
|
+
* button (actually a `<span>`). Ideally, we would make `Radio.Root` the root and it would render an
|
|
195
|
+
* internal component (e.g. `SegmentedControlOptionButton`) that would itself render a wrapper
|
|
196
|
+
* `<div>` around the button and the input (as well as calling `useCSSState` with a proper initial
|
|
197
|
+
* `checked` value, from the `render` state), but that is not possible currently (see
|
|
198
|
+
* https://github.com/mui/base-ui/issues/3143).
|
|
199
|
+
*/}
|
|
200
|
+
<div
|
|
201
|
+
ref={stateRef}
|
|
202
|
+
className={cn(
|
|
203
|
+
`gds-segmented-control-option root-block
|
|
204
|
+
state-[is-toggle=false]
|
|
205
|
+
group-[:has([data-checked]):has(>:nth-child(2)):not(:has(>:nth-child(3)))]/segmented-control-options:state-[is-toggle]
|
|
206
|
+
u:group-has-nested-hover/segmented-control--segmented-control-toggle:state-hover
|
|
207
|
+
u:group-has-nested-active/segmented-control--segmented-control-toggle:state-active
|
|
208
|
+
u:has-nested-hover/segmented-control-option-ref:state-hover
|
|
209
|
+
u:has-nested-active/segmented-control-option-ref:state-active
|
|
210
|
+
u:has-nested-[[data-checked]]/segmented-control-option-ref:state-checked`,
|
|
211
|
+
className,
|
|
212
|
+
)}
|
|
213
|
+
{...state.polyfillAttributes}
|
|
214
|
+
{...rootProps}
|
|
215
|
+
>
|
|
216
216
|
<Radio.Root
|
|
217
217
|
ref={buttonPassedRef}
|
|
218
218
|
value={value}
|
|
@@ -223,8 +223,7 @@ export function SegmentedControlOption<T extends OptionValue>({
|
|
|
223
223
|
}
|
|
224
224
|
}}
|
|
225
225
|
className={`
|
|
226
|
-
nested/segmented-control-option-ref block w-full
|
|
227
|
-
@state-checked/segmented-control-option:cursor-default
|
|
226
|
+
nested/segmented-control-option-ref block w-full outline-0
|
|
228
227
|
@state-checked/segmented-control-option:[anchor-name:--gds-segmented-control-checked]
|
|
229
228
|
`}
|
|
230
229
|
{...nestedProps}
|
|
@@ -270,8 +269,8 @@ export function SegmentedControlOption<T extends OptionValue>({
|
|
|
270
269
|
`}
|
|
271
270
|
/>
|
|
272
271
|
</Radio.Root>
|
|
273
|
-
</
|
|
274
|
-
</
|
|
272
|
+
</div>
|
|
273
|
+
</Tooltip>
|
|
275
274
|
)
|
|
276
275
|
}
|
|
277
276
|
SegmentedControlOption.displayName = 'SegmentedControl.Option'
|
|
@@ -118,8 +118,8 @@ export function TabSetTabs({
|
|
|
118
118
|
className={`
|
|
119
119
|
absolute inset-x-[anchor(inside)] bottom-0 h-px bg-(--border-color-brand-default) transition-[inset] duration-300
|
|
120
120
|
[position-anchor:--gds-tab-set-underline]
|
|
121
|
-
not-group-hover/tab-set-tabs:duration-0
|
|
122
121
|
not-supports-[position-anchor:--foo]:hidden
|
|
122
|
+
group-not-hover/tab-set-tabs:duration-0
|
|
123
123
|
group-has-focus-visible/tab-set-tabs:hidden
|
|
124
124
|
`}
|
|
125
125
|
/>
|
|
@@ -17,8 +17,6 @@ export interface ToggleButtonProps
|
|
|
17
17
|
extends
|
|
18
18
|
Omit<ButtonProps.ToggleButtonProps, 'onChange' | 'variant'>,
|
|
19
19
|
GDSComponentProps<typeof ToggleButtonMeta> {
|
|
20
|
-
checked?: boolean | undefined
|
|
21
|
-
defaultChecked?: boolean | undefined
|
|
22
20
|
onChange?: ((checked: boolean) => void) | undefined
|
|
23
21
|
}
|
|
24
22
|
|
|
@@ -68,19 +68,26 @@ export declare namespace InternalButtonOrLinkProps {
|
|
|
68
68
|
linkComponent?: LinkComponent | undefined
|
|
69
69
|
}
|
|
70
70
|
interface ButtonProps
|
|
71
|
-
extends
|
|
71
|
+
extends
|
|
72
|
+
BaseProps,
|
|
73
|
+
DisableableProps,
|
|
74
|
+
Omit<ComponentProps<'button'>, 'defaultValue' | 'disabled'> {
|
|
72
75
|
href?: undefined
|
|
73
76
|
target?: undefined
|
|
74
77
|
checked?: undefined
|
|
78
|
+
defaultChecked?: undefined
|
|
75
79
|
}
|
|
76
|
-
interface ToggleButtonProps extends Omit<ButtonProps, 'checked'> {
|
|
77
|
-
/** Whether the button is in a
|
|
78
|
-
checked?: boolean |
|
|
80
|
+
interface ToggleButtonProps extends Omit<ButtonProps, 'checked' | 'defaultChecked'> {
|
|
81
|
+
/** Whether the button is in a pressed/selected state. */
|
|
82
|
+
checked?: boolean | undefined
|
|
83
|
+
defaultChecked?: boolean | undefined
|
|
79
84
|
}
|
|
80
|
-
interface LinkProps
|
|
85
|
+
interface LinkProps
|
|
86
|
+
extends BaseProps, DisableableProps, Omit<ComponentProps<'a'>, 'defaultValue'> {
|
|
81
87
|
href: string
|
|
82
88
|
type?: undefined
|
|
83
89
|
checked?: undefined
|
|
90
|
+
defaultChecked?: undefined
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
93
|
|
|
@@ -192,11 +199,19 @@ export const ButtonOrLinkRoot = (passedProps: InternalButtonOrLinkProps) => {
|
|
|
192
199
|
role = 'button',
|
|
193
200
|
type = 'button',
|
|
194
201
|
checked,
|
|
202
|
+
defaultChecked,
|
|
195
203
|
href: _href,
|
|
196
204
|
target: _target,
|
|
197
205
|
...remainingProps
|
|
198
206
|
} = nonBaseProps
|
|
199
207
|
|
|
208
|
+
if (defaultChecked !== undefined) {
|
|
209
|
+
// oxlint-disable-next-line no-console
|
|
210
|
+
console.warn(
|
|
211
|
+
'[ButtonOrLink] `defaultChecked` is not supported; use a controlled `checked` prop instead, or wrap `ButtonOrLink` in a component that manages the uncontrolled state',
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
200
215
|
const Element = inline ? SpanButtonWithContext : ButtonWithContext
|
|
201
216
|
|
|
202
217
|
const state: ButtonOrLinkState = {
|
|
@@ -208,22 +223,23 @@ export const ButtonOrLinkRoot = (passedProps: InternalButtonOrLinkProps) => {
|
|
|
208
223
|
checked,
|
|
209
224
|
}
|
|
210
225
|
|
|
211
|
-
const checkedAttribute =
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
226
|
+
const checkedAttribute =
|
|
227
|
+
checked === undefined
|
|
228
|
+
? {}
|
|
229
|
+
: ((checked: unknown) => {
|
|
230
|
+
if (
|
|
231
|
+
(role === 'option' || role === 'tab' || role === 'row' || role === 'gridcell') &&
|
|
232
|
+
checked !== 'indeterminate'
|
|
233
|
+
) {
|
|
234
|
+
return { 'aria-selected': Boolean(checked) }
|
|
235
|
+
}
|
|
236
|
+
const checkedOrMixed =
|
|
237
|
+
checked === 'indeterminate' ? ('mixed' as const) : Boolean(checked)
|
|
238
|
+
if (role === 'button') {
|
|
239
|
+
return { 'aria-pressed': checkedOrMixed }
|
|
240
|
+
}
|
|
241
|
+
return { 'aria-checked': checkedOrMixed }
|
|
242
|
+
})(checked)
|
|
227
243
|
|
|
228
244
|
let buttonProps: ComponentProps<typeof Element> = {
|
|
229
245
|
role,
|
|
@@ -251,6 +267,7 @@ export const ButtonOrLinkRoot = (passedProps: InternalButtonOrLinkProps) => {
|
|
|
251
267
|
rel = target === '_blank' ? 'noopener noreferrer' : undefined,
|
|
252
268
|
type: _type,
|
|
253
269
|
checked: _checked,
|
|
270
|
+
defaultChecked: _defaultChecked,
|
|
254
271
|
...remainingProps
|
|
255
272
|
} = nonBaseProps
|
|
256
273
|
|
package/src/components/index.ts
CHANGED
|
@@ -14,6 +14,8 @@ export { ButtonGroup, type ButtonGroupProps } from './ButtonGroup.tsx'
|
|
|
14
14
|
export { ButtonGroupMeta } from './ButtonGroup.meta.ts'
|
|
15
15
|
export { Card, type CardProps } from './Card.tsx'
|
|
16
16
|
export { CardMeta } from './Card.meta.ts'
|
|
17
|
+
export { Chip, type ChipProps, type ChipGroupProps } from './Chip.tsx'
|
|
18
|
+
export { ChipMeta, ChipGroupMeta } from './Chip.meta.ts'
|
|
17
19
|
export { Cluster, type ClusterProps } from './Cluster.tsx'
|
|
18
20
|
export { ClusterMeta } from './Cluster.meta.ts'
|
|
19
21
|
export { CodeBlock, type CodeBlockProps, type CodeBlockTabsProps } from './CodeBlock.tsx'
|
package/src/tailwind-plugin.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { ButtonMeta } from './components/Button.meta.ts'
|
|
|
16
16
|
import { ButtonGroupMeta } from './components/ButtonGroup.meta.ts'
|
|
17
17
|
import { CardMeta } from './components/Card.meta.ts'
|
|
18
18
|
import { CheckboxMeta } from './components/Checkbox.meta.ts'
|
|
19
|
+
import { ChipGroupMeta, ChipMeta } from './components/Chip.meta.ts'
|
|
19
20
|
import { ClusterMeta } from './components/Cluster.meta.ts'
|
|
20
21
|
import { CodeBlockMeta, CodeBlockTabsMeta } from './components/CodeBlock.meta.ts'
|
|
21
22
|
import { CopyButtonMeta } from './components/CopyButton.meta.ts'
|
|
@@ -85,6 +86,8 @@ const gdsTailwindPluginWithComponents: ReturnType<typeof createPlugin> = createP
|
|
|
85
86
|
CodeBlockTabsMeta,
|
|
86
87
|
CheckableMeta,
|
|
87
88
|
CheckboxMeta,
|
|
89
|
+
ChipMeta,
|
|
90
|
+
ChipGroupMeta,
|
|
88
91
|
ClusterMeta,
|
|
89
92
|
CopyButtonMeta,
|
|
90
93
|
CurrencyInputMeta,
|