@edgeandnode/eds-react 1.1.0 → 1.2.0
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/README.md +27 -3
- package/dist/components/Address.d.ts +3 -3
- package/dist/components/Address.d.ts.map +1 -1
- package/dist/components/Address.js +1 -1
- package/dist/components/Address.js.map +1 -1
- package/dist/components/Avatar.d.ts +3 -3
- package/dist/components/Avatar.d.ts.map +1 -1
- package/dist/components/Avatar.js +13 -1
- package/dist/components/Avatar.js.map +1 -1
- package/dist/components/Breadcrumbs.parts.d.ts +3 -3
- package/dist/components/Breadcrumbs.parts.d.ts.map +1 -1
- package/dist/components/Breadcrumbs.parts.js +1 -1
- package/dist/components/Breadcrumbs.parts.js.map +1 -1
- package/dist/components/Button.js +1 -1
- 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.parts.d.ts.map +1 -1
- package/dist/components/Chip.parts.js +1 -1
- package/dist/components/Chip.parts.js.map +1 -1
- package/dist/components/Cluster.d.ts +3 -3
- package/dist/components/Cluster.d.ts.map +1 -1
- package/dist/components/Cluster.js.map +1 -1
- package/dist/components/Icon.d.ts.map +1 -1
- package/dist/components/Icon.js +0 -2
- package/dist/components/Icon.js.map +1 -1
- package/dist/components/Link.d.ts.map +1 -1
- package/dist/components/Link.js +2 -0
- package/dist/components/Link.js.map +1 -1
- package/dist/components/Menu.parts.js +1 -1
- package/dist/components/Menu.parts.js.map +1 -1
- package/dist/components/Search.d.ts.map +1 -1
- package/dist/components/Search.js +1 -1
- package/dist/components/Search.js.map +1 -1
- package/dist/components/SegmentedControl.parts.d.ts.map +1 -1
- package/dist/components/SegmentedControl.parts.js +1 -1
- package/dist/components/SegmentedControl.parts.js.map +1 -1
- package/dist/components/TabSet.parts.js +1 -1
- package/dist/components/TabSet.parts.js.map +1 -1
- package/dist/components/Tooltip.parts.d.ts.map +1 -1
- package/dist/components/Tooltip.parts.js.map +1 -1
- package/dist/components/base/Checkable.parts.js +1 -1
- package/dist/components/base/Checkable.parts.js.map +1 -1
- package/dist/components/base/Field.parts.js +1 -1
- package/dist/components/base/Field.parts.js.map +1 -1
- package/dist/components/base/Portal.js +3 -3
- package/dist/components/base/Portal.js.map +1 -1
- package/dist/components/base/Transition.js +1 -1
- package/dist/components/base/Transition.js.map +1 -1
- package/dist/hooks/useCSSPropsPolyfill.d.ts.map +1 -1
- package/dist/hooks/useCSSPropsPolyfill.js +7 -19
- package/dist/hooks/useCSSPropsPolyfill.js.map +1 -1
- package/dist/hooks/useCSSState.d.ts +4 -0
- package/dist/hooks/useCSSState.d.ts.map +1 -1
- package/dist/hooks/useCSSState.js +24 -6
- package/dist/hooks/useCSSState.js.map +1 -1
- package/dist/hooks/useStyleObserver.d.ts.map +1 -1
- package/dist/hooks/useStyleObserver.js +9 -5
- package/dist/hooks/useStyleObserver.js.map +1 -1
- package/dist/postcss-plugin.d.ts +2 -0
- package/dist/postcss-plugin.d.ts.map +1 -0
- package/dist/postcss-plugin.js +2 -0
- package/dist/postcss-plugin.js.map +1 -0
- package/dist/utils/internal/BatchedElementStyleObserver.d.ts +13 -0
- package/dist/utils/internal/BatchedElementStyleObserver.d.ts.map +1 -0
- package/dist/utils/internal/BatchedElementStyleObserver.js +28 -0
- package/dist/utils/internal/BatchedElementStyleObserver.js.map +1 -0
- package/dist/utils/internal/getStyleQueriesSupported.d.ts +2 -0
- package/dist/utils/internal/getStyleQueriesSupported.d.ts.map +1 -0
- package/dist/utils/internal/getStyleQueriesSupported.js +12 -0
- package/dist/utils/internal/getStyleQueriesSupported.js.map +1 -0
- package/dist/utils/internal/index.d.ts +2 -0
- package/dist/utils/internal/index.d.ts.map +1 -1
- package/dist/utils/internal/index.js +2 -0
- package/dist/utils/internal/index.js.map +1 -1
- package/dist/utils/internal/styleScheduler.d.ts +8 -3
- package/dist/utils/internal/styleScheduler.d.ts.map +1 -1
- package/dist/utils/internal/styleScheduler.js +46 -16
- package/dist/utils/internal/styleScheduler.js.map +1 -1
- package/dist/utils/styleObserver.d.ts +2 -0
- package/dist/utils/styleObserver.d.ts.map +1 -1
- package/dist/utils/styleObserver.js +40 -33
- package/dist/utils/styleObserver.js.map +1 -1
- package/dist/vite-plugin.d.ts +2 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +2 -0
- package/dist/vite-plugin.js.map +1 -0
- package/package.json +13 -11
- package/scripts/update-skill.cjs +42 -0
- package/skill/SKILL.md +18 -14
- package/src/components/Address.tsx +12 -10
- package/src/components/Avatar.tsx +61 -49
- package/src/components/Breadcrumbs.parts.tsx +11 -9
- package/src/components/Button.tsx +1 -1
- package/src/components/Card.tsx +1 -1
- package/src/components/Chip.parts.tsx +9 -6
- package/src/components/Cluster.tsx +3 -5
- package/src/components/Icon.tsx +0 -2
- package/src/components/Link.tsx +2 -0
- package/src/components/Menu.parts.tsx +1 -1
- package/src/components/Search.tsx +9 -6
- package/src/components/SegmentedControl.parts.tsx +8 -5
- package/src/components/TabSet.parts.tsx +1 -1
- package/src/components/Tooltip.parts.tsx +4 -1
- package/src/components/base/Checkable.parts.tsx +1 -1
- package/src/components/base/Field.parts.tsx +1 -1
- package/src/components/base/Portal.tsx +3 -3
- package/src/components/base/Transition.tsx +1 -1
- package/src/hooks/useCSSPropsPolyfill.ts +7 -20
- package/src/hooks/useCSSState.ts +29 -8
- package/src/hooks/useStyleObserver.ts +18 -14
- package/src/postcss-plugin.ts +1 -0
- package/src/utils/internal/BatchedElementStyleObserver.ts +31 -0
- package/src/utils/internal/getStyleQueriesSupported.ts +11 -0
- package/src/utils/internal/index.ts +2 -0
- package/src/utils/internal/styleScheduler.ts +43 -15
- package/src/utils/styleObserver.ts +38 -40
- package/src/vite-plugin.ts +1 -0
|
@@ -26,15 +26,14 @@ export declare namespace AddressProps {
|
|
|
26
26
|
interface ButtonProps
|
|
27
27
|
extends
|
|
28
28
|
CommonProps,
|
|
29
|
-
|
|
29
|
+
MaybeButtonOrLinkProps.ButtonProps,
|
|
30
30
|
MaybeButtonOrLinkProps.DisableableProps {}
|
|
31
31
|
interface LinkProps
|
|
32
32
|
extends
|
|
33
33
|
CommonProps,
|
|
34
|
-
|
|
34
|
+
MaybeButtonOrLinkProps.LinkProps,
|
|
35
35
|
MaybeButtonOrLinkProps.DisableableProps {}
|
|
36
|
-
interface OtherElementProps
|
|
37
|
-
extends CommonProps, Omit<MaybeButtonOrLinkProps.OtherElementProps, 'as'> {}
|
|
36
|
+
interface OtherElementProps extends CommonProps, MaybeButtonOrLinkProps.OtherElementProps {}
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
export type AddressProps =
|
|
@@ -55,12 +54,15 @@ export function Address({
|
|
|
55
54
|
|
|
56
55
|
const { rootProps, nestedProps } = splitProps(props)
|
|
57
56
|
|
|
58
|
-
const [stateRef, state] = useCSSState(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
const [stateRef, state] = useCSSState(
|
|
58
|
+
{
|
|
59
|
+
pointer: undefined,
|
|
60
|
+
focus: undefined,
|
|
61
|
+
disabled: Boolean(props.disabled),
|
|
62
|
+
'show-copy': false,
|
|
63
|
+
},
|
|
64
|
+
{ returnState: false },
|
|
65
|
+
)
|
|
64
66
|
const [cssPropsPolyfillStateRef, cssPropsPolyfillAttributes] = useCSSPropsPolyfill(
|
|
65
67
|
AddressMeta,
|
|
66
68
|
{ variant, size, copy },
|
|
@@ -13,6 +13,7 @@ import { cn, getAssetUrl, getCSSPropsAttributes } from '../utils/index.ts'
|
|
|
13
13
|
import { getTooltipProps } from '../utils/internal/index.ts'
|
|
14
14
|
import { AvatarMeta } from './Avatar.meta.ts'
|
|
15
15
|
import { MaybeButtonOrLink, type MaybeButtonOrLinkProps } from './base/MaybeButtonOrLink.tsx'
|
|
16
|
+
import { Render } from './base/Render.tsx'
|
|
16
17
|
import { Tooltip, type TooltipProps } from './Tooltip.tsx'
|
|
17
18
|
|
|
18
19
|
const PLACEHOLDER_SRC_CIRCLE = createIdenticon(PLACEHOLDER_ADDRESS)
|
|
@@ -37,13 +38,11 @@ export declare namespace AvatarProps {
|
|
|
37
38
|
}
|
|
38
39
|
interface CommonProps extends OwnProps, EDSComponentProps<typeof AvatarMeta> {}
|
|
39
40
|
interface ButtonProps
|
|
40
|
-
extends CommonProps, Omit<MaybeButtonOrLinkProps.ButtonProps, '
|
|
41
|
+
extends CommonProps, Omit<MaybeButtonOrLinkProps.ButtonProps, 'children' | 'render'> {}
|
|
41
42
|
interface LinkProps
|
|
42
|
-
extends CommonProps, Omit<MaybeButtonOrLinkProps.LinkProps, '
|
|
43
|
+
extends CommonProps, Omit<MaybeButtonOrLinkProps.LinkProps, 'children' | 'render'> {}
|
|
43
44
|
interface OtherElementProps
|
|
44
|
-
extends
|
|
45
|
-
CommonProps,
|
|
46
|
-
Omit<MaybeButtonOrLinkProps.OtherElementProps, 'as' | 'children' | 'render'> {}
|
|
45
|
+
extends CommonProps, Omit<MaybeButtonOrLinkProps.OtherElementProps, 'children' | 'render'> {}
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
export type AvatarProps =
|
|
@@ -96,55 +95,68 @@ export function Avatar({
|
|
|
96
95
|
props,
|
|
97
96
|
)}
|
|
98
97
|
render={(renderProps, { Element, elementProps, category }) => {
|
|
98
|
+
const avatar = (
|
|
99
|
+
<Element {...elementProps}>
|
|
100
|
+
<span className="nested/avatar-paint pointer-events-none shrink-0">
|
|
101
|
+
<span
|
|
102
|
+
className="
|
|
103
|
+
block overflow-clip bg-default
|
|
104
|
+
group-clickable-focus-visible/avatar:outline
|
|
105
|
+
@prop-size-xsmall/avatar:size-4
|
|
106
|
+
@prop-size-small/avatar:size-5
|
|
107
|
+
@prop-size-medium/avatar:size-6
|
|
108
|
+
@prop-size-large/avatar:size-8
|
|
109
|
+
@prop-size-xlarge/avatar:size-12
|
|
110
|
+
@prop-shape-circle/avatar:rounded-full
|
|
111
|
+
@prop-shape-rounded/avatar:rounded-4
|
|
112
|
+
@prop-shape-rounded/avatar:@prop-size-xsmall/avatar:rounded-2
|
|
113
|
+
@prop-shape-rounded/avatar:@prop-size-xlarge/avatar:rounded-8
|
|
114
|
+
uu:*:absolute
|
|
115
|
+
uu:*:inset-0
|
|
116
|
+
uu:*:m-auto
|
|
117
|
+
uu:*:size-full
|
|
118
|
+
uu:*:object-cover
|
|
119
|
+
i:@prop-size-xsmall/avatar:**:icon:default-size-2.5
|
|
120
|
+
i:@prop-size-small/avatar:**:icon:default-size-3
|
|
121
|
+
i:@prop-size-medium/avatar:**:icon:default-size-4
|
|
122
|
+
i:@prop-size-large/avatar:**:icon:default-size-5
|
|
123
|
+
i:@prop-size-xlarge/avatar:**:icon:default-size-8
|
|
124
|
+
"
|
|
125
|
+
>
|
|
126
|
+
{src ? (
|
|
127
|
+
typeof src === 'string' ? (
|
|
128
|
+
<img
|
|
129
|
+
src={src}
|
|
130
|
+
alt=""
|
|
131
|
+
onError={() => setCurrentSourceIndex(currentSourceIndex + 1)}
|
|
132
|
+
className="[[src^='data:image/bmp']]:[image-rendering:pixelated]"
|
|
133
|
+
/>
|
|
134
|
+
) : (
|
|
135
|
+
<Tooltip.Collector>{src}</Tooltip.Collector>
|
|
136
|
+
)
|
|
137
|
+
) : null}
|
|
138
|
+
{alt ? <span className="sr-only">{alt}</span> : null}
|
|
139
|
+
</span>
|
|
140
|
+
</span>
|
|
141
|
+
</Element>
|
|
142
|
+
)
|
|
143
|
+
const clickable = category !== 'other'
|
|
144
|
+
const focusable = clickable || props.as !== undefined || props.tabIndex !== undefined
|
|
145
|
+
/**
|
|
146
|
+
* Without any content to show (or collect from `src`), a `Tooltip` would only cost its
|
|
147
|
+
* trigger and observers. Avatars that can take focus keep it anyway, since adding or
|
|
148
|
+
* removing it remounts the element, which would lose focus.
|
|
149
|
+
*/
|
|
150
|
+
if (!focusable && tooltip === undefined && typeof src === 'string') {
|
|
151
|
+
return <Render render={avatar} {...renderProps} />
|
|
152
|
+
}
|
|
99
153
|
return (
|
|
100
154
|
<Tooltip
|
|
101
|
-
content={
|
|
155
|
+
content={clickable ? alt : undefined}
|
|
102
156
|
{...getTooltipProps(tooltip)}
|
|
103
157
|
triggerProps={renderProps}
|
|
104
158
|
>
|
|
105
|
-
|
|
106
|
-
<span className="nested/avatar-paint pointer-events-none shrink-0">
|
|
107
|
-
<span
|
|
108
|
-
className="
|
|
109
|
-
block overflow-clip bg-default
|
|
110
|
-
group-clickable-focus-visible/avatar:outline
|
|
111
|
-
@prop-size-xsmall/avatar:size-4
|
|
112
|
-
@prop-size-small/avatar:size-5
|
|
113
|
-
@prop-size-medium/avatar:size-6
|
|
114
|
-
@prop-size-large/avatar:size-8
|
|
115
|
-
@prop-size-xlarge/avatar:size-12
|
|
116
|
-
@prop-shape-circle/avatar:rounded-full
|
|
117
|
-
@prop-shape-rounded/avatar:rounded-4
|
|
118
|
-
@prop-shape-rounded/avatar:@prop-size-xsmall/avatar:rounded-2
|
|
119
|
-
@prop-shape-rounded/avatar:@prop-size-xlarge/avatar:rounded-8
|
|
120
|
-
uu:*:absolute
|
|
121
|
-
uu:*:inset-0
|
|
122
|
-
uu:*:m-auto
|
|
123
|
-
uu:*:size-full
|
|
124
|
-
uu:*:object-cover
|
|
125
|
-
i:@prop-size-xsmall/avatar:**:icon:default-size-2.5
|
|
126
|
-
i:@prop-size-small/avatar:**:icon:default-size-3
|
|
127
|
-
i:@prop-size-medium/avatar:**:icon:default-size-4
|
|
128
|
-
i:@prop-size-large/avatar:**:icon:default-size-5
|
|
129
|
-
i:@prop-size-xlarge/avatar:**:icon:default-size-8
|
|
130
|
-
"
|
|
131
|
-
>
|
|
132
|
-
{src ? (
|
|
133
|
-
typeof src === 'string' ? (
|
|
134
|
-
<img
|
|
135
|
-
src={src}
|
|
136
|
-
alt=""
|
|
137
|
-
onError={() => setCurrentSourceIndex(currentSourceIndex + 1)}
|
|
138
|
-
className="[[src^='data:image/bmp']]:[image-rendering:pixelated]"
|
|
139
|
-
/>
|
|
140
|
-
) : (
|
|
141
|
-
<Tooltip.Collector>{src}</Tooltip.Collector>
|
|
142
|
-
)
|
|
143
|
-
) : null}
|
|
144
|
-
{alt ? <span className="sr-only">{alt}</span> : null}
|
|
145
|
-
</span>
|
|
146
|
-
</span>
|
|
147
|
-
</Element>
|
|
159
|
+
{avatar}
|
|
148
160
|
</Tooltip>
|
|
149
161
|
)
|
|
150
162
|
}}
|
|
@@ -269,10 +269,9 @@ export declare namespace BreadcrumbsItemProps {
|
|
|
269
269
|
current?: boolean | undefined
|
|
270
270
|
}
|
|
271
271
|
interface CommonProps extends OwnProps, EDSComponentProps<typeof BreadcrumbsItemMeta> {}
|
|
272
|
-
interface ButtonProps extends CommonProps,
|
|
273
|
-
interface LinkProps extends CommonProps,
|
|
274
|
-
interface OtherElementProps
|
|
275
|
-
extends CommonProps, Omit<MaybeButtonOrLinkProps.OtherElementProps, 'as'> {}
|
|
272
|
+
interface ButtonProps extends CommonProps, MaybeButtonOrLinkProps.ButtonProps {}
|
|
273
|
+
interface LinkProps extends CommonProps, MaybeButtonOrLinkProps.LinkProps {}
|
|
274
|
+
interface OtherElementProps extends CommonProps, MaybeButtonOrLinkProps.OtherElementProps {}
|
|
276
275
|
}
|
|
277
276
|
|
|
278
277
|
export type BreadcrumbsItemProps =
|
|
@@ -291,11 +290,14 @@ export function BreadcrumbsItem({
|
|
|
291
290
|
const current = passedCurrent ?? isLastItem
|
|
292
291
|
const { rootProps, nestedProps } = splitProps(props)
|
|
293
292
|
|
|
294
|
-
const [stateRef, state] = useCSSState(
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
293
|
+
const [stateRef, state] = useCSSState(
|
|
294
|
+
{
|
|
295
|
+
pointer: undefined,
|
|
296
|
+
focus: undefined,
|
|
297
|
+
current,
|
|
298
|
+
},
|
|
299
|
+
{ returnState: false },
|
|
300
|
+
)
|
|
299
301
|
const [cssPropsPolyfillStatePassedRef, cssPropsPolyfillAttributes] = useCSSPropsPolyfill(
|
|
300
302
|
BreadcrumbsItemMeta,
|
|
301
303
|
{ hideLabel },
|
|
@@ -76,7 +76,7 @@ export function Button({
|
|
|
76
76
|
disabled: Boolean(disabled),
|
|
77
77
|
'addon-only': !children ? 'auto' : '0',
|
|
78
78
|
},
|
|
79
|
-
{ ref: passedRef },
|
|
79
|
+
{ ref: passedRef, returnState: false, exposeState: false },
|
|
80
80
|
)
|
|
81
81
|
const [cssPropsPolyfillStatePassedRef, cssPropsPolyfillAttributes, cssProps] =
|
|
82
82
|
useCSSPropsPolyfill(
|
package/src/components/Card.tsx
CHANGED
|
@@ -72,7 +72,7 @@ export function Card({ ref: passedRef, as, variant, disabled, children, ...props
|
|
|
72
72
|
checked: props.checked,
|
|
73
73
|
disabled: Boolean(disabled),
|
|
74
74
|
},
|
|
75
|
-
{ ref: passedRef },
|
|
75
|
+
{ ref: passedRef, returnState: false },
|
|
76
76
|
)
|
|
77
77
|
const [cssPropsPolyfillStatePassedRef, cssPropsPolyfillAttributes] = useCSSPropsPolyfill(
|
|
78
78
|
CardMeta,
|
|
@@ -65,12 +65,15 @@ export function ChipRoot({
|
|
|
65
65
|
|
|
66
66
|
const groupContext = useContext(ChipGroupContext)
|
|
67
67
|
|
|
68
|
-
const [stateRef, state] = useCSSState(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
const [stateRef, state] = useCSSState(
|
|
69
|
+
{
|
|
70
|
+
pointer: undefined,
|
|
71
|
+
focus: undefined,
|
|
72
|
+
checked: undefined,
|
|
73
|
+
disabled: Boolean(disabled),
|
|
74
|
+
},
|
|
75
|
+
{ returnState: false },
|
|
76
|
+
)
|
|
74
77
|
const [cssPropsPolyfillStateRef, cssPropsPolyfillAttributes] = useCSSPropsPolyfill(
|
|
75
78
|
ChipMeta,
|
|
76
79
|
{ size },
|
|
@@ -11,13 +11,11 @@ import { ClusterMeta } from './Cluster.meta.ts'
|
|
|
11
11
|
|
|
12
12
|
export declare namespace ClusterProps {
|
|
13
13
|
interface ButtonProps
|
|
14
|
-
extends EDSComponentProps<typeof ClusterMeta>,
|
|
14
|
+
extends EDSComponentProps<typeof ClusterMeta>, MaybeButtonOrLinkProps.ButtonProps {}
|
|
15
15
|
interface LinkProps
|
|
16
|
-
extends EDSComponentProps<typeof ClusterMeta>,
|
|
16
|
+
extends EDSComponentProps<typeof ClusterMeta>, MaybeButtonOrLinkProps.LinkProps {}
|
|
17
17
|
interface OtherElementProps
|
|
18
|
-
extends
|
|
19
|
-
EDSComponentProps<typeof ClusterMeta>,
|
|
20
|
-
Omit<MaybeButtonOrLinkProps.OtherElementProps, 'as'> {}
|
|
18
|
+
extends EDSComponentProps<typeof ClusterMeta>, MaybeButtonOrLinkProps.OtherElementProps {}
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
export type ClusterProps =
|
package/src/components/Icon.tsx
CHANGED
|
@@ -54,8 +54,6 @@ export function Icon({ src, alt, size, color, ...props }: IconPropsWithSrc) {
|
|
|
54
54
|
{
|
|
55
55
|
className: cn(`
|
|
56
56
|
eds-icon root-flex items-center justify-center
|
|
57
|
-
${/* Fix icons wobbling in Safari when rendered on top of opacity/background transitions */ ''}
|
|
58
|
-
u:will-change-transform
|
|
59
57
|
${/* Flip some icons horizontally in RTL so that `ArrowLeft` points right, etc. */ ''}
|
|
60
58
|
rtl:data-[name*=Left]:*:-scale-x-100
|
|
61
59
|
rtl:data-[name*=Right]:*:-scale-x-100
|
package/src/components/Link.tsx
CHANGED
|
@@ -1015,7 +1015,7 @@ export function MenuItem<T extends OptionValue>({
|
|
|
1015
1015
|
checked,
|
|
1016
1016
|
disabled,
|
|
1017
1017
|
},
|
|
1018
|
-
{ ref: elementPassedRef },
|
|
1018
|
+
{ ref: elementPassedRef, returnState: false, exposeState: false },
|
|
1019
1019
|
)
|
|
1020
1020
|
|
|
1021
1021
|
const groupContext = useContext(MenuGroupContext)
|
|
@@ -54,12 +54,15 @@ export function Search({
|
|
|
54
54
|
const previousActiveElementRef = useRef<HTMLElement | null>(null)
|
|
55
55
|
const [value, setValue] = useControlled(controlledValue, defaultValue ?? '', onValueChange)
|
|
56
56
|
|
|
57
|
-
const [stateRef, state] = useCSSState(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
const [stateRef, state] = useCSSState(
|
|
58
|
+
{
|
|
59
|
+
pointer: undefined,
|
|
60
|
+
focus: undefined,
|
|
61
|
+
blank: value === '',
|
|
62
|
+
open: value !== '',
|
|
63
|
+
},
|
|
64
|
+
{ returnState: false },
|
|
65
|
+
)
|
|
63
66
|
const [cssPropsPolyfillStateRef, cssPropsPolyfillAttributes, cssProps] = useCSSPropsPolyfill(
|
|
64
67
|
SearchMeta,
|
|
65
68
|
{ size, layout },
|
|
@@ -152,11 +152,14 @@ export function SegmentedControlOption<T extends OptionValue>({
|
|
|
152
152
|
const autoValue = useAutoValue(children)
|
|
153
153
|
const value = passedValue !== undefined ? passedValue : autoValue
|
|
154
154
|
|
|
155
|
-
const [stateRef, state] = useCSSState(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
155
|
+
const [stateRef, state] = useCSSState(
|
|
156
|
+
{
|
|
157
|
+
pointer: undefined,
|
|
158
|
+
checked: undefined,
|
|
159
|
+
'is-toggle': undefined,
|
|
160
|
+
},
|
|
161
|
+
{ returnState: false },
|
|
162
|
+
)
|
|
160
163
|
const [cssPropsPolyfillStatePassedRef, cssPropsPolyfillAttributes] = useCSSPropsPolyfill(
|
|
161
164
|
SegmentedControlOptionMeta,
|
|
162
165
|
{ hideLabel },
|
|
@@ -203,7 +203,7 @@ export function TabSetTab({
|
|
|
203
203
|
checked: selected,
|
|
204
204
|
disabled: Boolean(disabled),
|
|
205
205
|
},
|
|
206
|
-
{ ref: passedRef },
|
|
206
|
+
{ ref: passedRef, returnState: false, exposeState: false },
|
|
207
207
|
)
|
|
208
208
|
const [cssPropsPolyfillStatePassedRef, cssPropsPolyfillAttributes] = useCSSPropsPolyfill(
|
|
209
209
|
TabSetTabMeta,
|
|
@@ -176,7 +176,10 @@ export function TooltipRoot({
|
|
|
176
176
|
<Render render={children} {...renderProps} disabled={triggerDisabled} />
|
|
177
177
|
)}
|
|
178
178
|
/>
|
|
179
|
-
{
|
|
179
|
+
{/**
|
|
180
|
+
* Observe CSS props even without content: changing `disabled` can make a descendant's collected
|
|
181
|
+
* content available to another tooltip.
|
|
182
|
+
*/}
|
|
180
183
|
<Portal>
|
|
181
184
|
<span
|
|
182
185
|
ref={cssPropsPolyfillRef}
|
|
@@ -107,7 +107,7 @@ export const FieldRoot = ({
|
|
|
107
107
|
blank: typeof value === 'object' ? value.optionValue === null : !value,
|
|
108
108
|
disabled,
|
|
109
109
|
},
|
|
110
|
-
{ ref: passedRef },
|
|
110
|
+
{ ref: passedRef, returnState: false },
|
|
111
111
|
)
|
|
112
112
|
const [cssPropsPolyfillStatePassedRef, cssPropsPolyfillAttributes] = useCSSPropsPolyfill(
|
|
113
113
|
FieldMeta,
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
import type { ReactNode } from 'react'
|
|
4
4
|
import { createPortal } from 'react-dom'
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { useIsHydrated } from '../../hooks/index.ts'
|
|
7
7
|
|
|
8
8
|
export function Portal({ children }: { children: ReactNode }) {
|
|
9
|
-
const
|
|
9
|
+
const hydrated = useIsHydrated()
|
|
10
10
|
|
|
11
11
|
// Don't render until after hydration
|
|
12
|
-
if (
|
|
12
|
+
if (!hydrated) {
|
|
13
13
|
return null
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -94,7 +94,7 @@ export function Transition({
|
|
|
94
94
|
{
|
|
95
95
|
transition: 'idle',
|
|
96
96
|
},
|
|
97
|
-
{ ref: passedRef },
|
|
97
|
+
{ ref: passedRef, returnState: false, exposeState: false },
|
|
98
98
|
)
|
|
99
99
|
|
|
100
100
|
// Collapse to the early-exit branch under reduced-motion — `global.css`'s `@media` block can't skip the cross-fade structure (both `<m.span>`s mount during a swap), only `duration <= 0` can.
|
|
@@ -9,8 +9,9 @@ import {
|
|
|
9
9
|
type EDSComponent,
|
|
10
10
|
} from '@edgeandnode/eds-css'
|
|
11
11
|
|
|
12
|
+
import { getStyleQueriesSupported } from '../utils/internal/index.ts'
|
|
12
13
|
import { useCSSProps, type PassedValues, type PropValues } from './useCSSProps.ts'
|
|
13
|
-
import {
|
|
14
|
+
import { useIsHydrated } from './useIsHydrated.ts'
|
|
14
15
|
|
|
15
16
|
type ExtractPolyfillValues<P extends CSSPropsConfig> = {
|
|
16
17
|
[K in keyof P as P[K]['type'] extends 'values' ? K : never]: CSSPropType<P[K]> | undefined
|
|
@@ -114,16 +115,14 @@ export function useCSSPropsPolyfill<
|
|
|
114
115
|
})
|
|
115
116
|
|
|
116
117
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* to immediately rerender in browsers that support style queries); unfortunately, that would
|
|
121
|
-
* cause a flash of missing styles in browsers that need the polyfill.
|
|
118
|
+
* Include the polyfill attributes until hydration completes (even if the polyfill is disabled) to
|
|
119
|
+
* prevent hydration errors. Components mounted after hydration (e.g. on client-side navigation)
|
|
120
|
+
* skip them, so they don't have to rerender (and restyle their subtree) just to remove them.
|
|
122
121
|
*/
|
|
123
|
-
const
|
|
122
|
+
const hydrated = useIsHydrated()
|
|
124
123
|
const polyfillAttributes = cssPropsOverride
|
|
125
124
|
? cssPropsOverride.polyfillAttributes
|
|
126
|
-
: polyfillEnabled ||
|
|
125
|
+
: polyfillEnabled || !hydrated
|
|
127
126
|
? Object.fromEntries(
|
|
128
127
|
cssProps
|
|
129
128
|
.filter((prop) => prop.name in passedValues)
|
|
@@ -148,15 +147,3 @@ export function useCSSPropsPolyfill<
|
|
|
148
147
|
|
|
149
148
|
return [callbackRef, polyfillAttributes, returnedPropValues] as const
|
|
150
149
|
}
|
|
151
|
-
|
|
152
|
-
let privateStyleQueriesSupported: boolean | null = null
|
|
153
|
-
|
|
154
|
-
function getStyleQueriesSupported(): boolean {
|
|
155
|
-
if (typeof window === 'undefined' || typeof document === 'undefined') return false
|
|
156
|
-
if (privateStyleQueriesSupported === null) {
|
|
157
|
-
privateStyleQueriesSupported =
|
|
158
|
-
window.getComputedStyle(document.body).getPropertyValue('--eds-style-queries-test') ===
|
|
159
|
-
'success'
|
|
160
|
-
}
|
|
161
|
-
return privateStyleQueriesSupported
|
|
162
|
-
}
|
package/src/hooks/useCSSState.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type { Ref } from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { cssStateVariables } from '@edgeandnode/eds-css'
|
|
4
4
|
|
|
5
|
+
import { getStyleQueriesSupported } from '../utils/internal/index.ts'
|
|
6
|
+
import { useIsHydrated } from './useIsHydrated.ts'
|
|
5
7
|
import { useMapWithInit } from './useMapWithInit.ts'
|
|
6
8
|
import { useRefWithInit } from './useRefWithInit.ts'
|
|
7
9
|
import { useStyleObserver } from './useStyleObserver.ts'
|
|
@@ -50,9 +52,16 @@ export function useCSSState<
|
|
|
50
52
|
options: {
|
|
51
53
|
ref?: Ref<HTMLElement> | undefined
|
|
52
54
|
onChange?: (state: CSSStateType<T>) => void
|
|
55
|
+
/** Set to `false` when only the attributes are consumed; the polyfill still observes state. */
|
|
56
|
+
returnState?: boolean | undefined
|
|
57
|
+
/** Set to `false` when the component does not spread `exposedAttributes` or `allAttributes`. */
|
|
58
|
+
exposeState?: boolean | undefined
|
|
53
59
|
} = {},
|
|
54
60
|
) {
|
|
55
|
-
const { ref, onChange } = options
|
|
61
|
+
const { ref, onChange, returnState = true, exposeState = true } = options
|
|
62
|
+
const polyfillEnabled = !getStyleQueriesSupported()
|
|
63
|
+
const hydrated = useIsHydrated()
|
|
64
|
+
const observeState = returnState || Boolean(onChange) || polyfillEnabled
|
|
56
65
|
|
|
57
66
|
const variables = useRefWithInit(() =>
|
|
58
67
|
Object.keys(variableInitialValues).map((variableName) => {
|
|
@@ -60,10 +69,21 @@ export function useCSSState<
|
|
|
60
69
|
const defaultValue = !isArbitrary
|
|
61
70
|
? cssStateVariables[variableName as CSSStateVariableName].defaultValue
|
|
62
71
|
: undefined
|
|
72
|
+
const passedValue = variableInitialValues[variableName as keyof T]
|
|
63
73
|
return {
|
|
64
74
|
name: variableName,
|
|
65
75
|
defaultValue,
|
|
66
|
-
initialValue:
|
|
76
|
+
initialValue: passedValue ?? defaultValue,
|
|
77
|
+
/**
|
|
78
|
+
* Nothing is exposed until CSS says otherwise, and a missing `data-eds-exposed-*` attribute
|
|
79
|
+
* falls back to the native state. Starting from the default value instead would make the
|
|
80
|
+
* first style observation remove the attribute, rerendering the component and restyling its
|
|
81
|
+
* whole subtree.
|
|
82
|
+
*/
|
|
83
|
+
initialExposedValue:
|
|
84
|
+
passedValue === undefined || String(passedValue) === String(defaultValue)
|
|
85
|
+
? undefined
|
|
86
|
+
: passedValue,
|
|
67
87
|
observedProperty: isArbitrary
|
|
68
88
|
? `--eds-state-${variableName}`
|
|
69
89
|
: `--eds-observed-${variableName}`,
|
|
@@ -74,14 +94,15 @@ export function useCSSState<
|
|
|
74
94
|
}),
|
|
75
95
|
).current
|
|
76
96
|
|
|
77
|
-
const properties =
|
|
78
|
-
|
|
79
|
-
|
|
97
|
+
const properties = variables.flatMap((variable) => [
|
|
98
|
+
...(observeState ? [variable.observedProperty] : []),
|
|
99
|
+
...(exposeState ? [variable.exposedProperty] : []),
|
|
100
|
+
])
|
|
80
101
|
|
|
81
102
|
const propertyValues = useMapWithInit(() =>
|
|
82
103
|
variables.flatMap((variable) => [
|
|
83
104
|
[variable.observedProperty, String(variable.initialValue ?? '')],
|
|
84
|
-
[variable.exposedProperty, String(variable.
|
|
105
|
+
[variable.exposedProperty, String(variable.initialExposedValue ?? '')],
|
|
85
106
|
]),
|
|
86
107
|
)
|
|
87
108
|
|
|
@@ -131,12 +152,12 @@ export function useCSSState<
|
|
|
131
152
|
}>(
|
|
132
153
|
(result, variable) => {
|
|
133
154
|
const exposedValue = getStateValue(variable, 'exposed')
|
|
134
|
-
if (exposedValue !== undefined) {
|
|
155
|
+
if (exposeState && exposedValue !== undefined) {
|
|
135
156
|
result.exposedAttributes[variable.exposedAttribute] = String(exposedValue)
|
|
136
157
|
result.allAttributes[variable.exposedAttribute] = String(exposedValue)
|
|
137
158
|
}
|
|
138
159
|
const observedValue = getStateValue(variable, 'observed')
|
|
139
|
-
if (observedValue !== undefined) {
|
|
160
|
+
if ((polyfillEnabled || !hydrated) && observedValue !== undefined) {
|
|
140
161
|
result.polyfillAttributes[variable.polyfillAttribute] = String(observedValue)
|
|
141
162
|
result.allAttributes[variable.polyfillAttribute] = String(observedValue)
|
|
142
163
|
}
|
|
@@ -32,25 +32,29 @@ export function useStyleObserver<T extends string>(
|
|
|
32
32
|
if (!element || properties.length === 0 || disabled) return
|
|
33
33
|
|
|
34
34
|
// TODO: Use `style-observer`'s `immediate` option when it exists: https://github.com/LeaVerou/style-observer/issues/45
|
|
35
|
-
|
|
36
|
-
styleScheduler.schedule(() => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
const cancelImmediate = immediate
|
|
36
|
+
? styleScheduler.schedule(() => {
|
|
37
|
+
const computedStyle = window.getComputedStyle(element)
|
|
38
|
+
memoizedOnChange(
|
|
39
|
+
properties.map((property) => ({
|
|
40
|
+
target: element,
|
|
41
|
+
property,
|
|
42
|
+
value: computedStyle.getPropertyValue(property),
|
|
43
|
+
oldValue: '',
|
|
44
|
+
})),
|
|
45
|
+
)
|
|
46
|
+
})
|
|
47
|
+
: undefined
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
const unsubscribe = styleObserver.subscribe(
|
|
50
50
|
element,
|
|
51
51
|
properties,
|
|
52
52
|
memoizedOnChange as (records: StyleObserverRecord[]) => void,
|
|
53
53
|
)
|
|
54
|
+
return () => {
|
|
55
|
+
cancelImmediate?.()
|
|
56
|
+
unsubscribe()
|
|
57
|
+
}
|
|
54
58
|
},
|
|
55
59
|
[properties, memoizedOnChange, immediate, disabled],
|
|
56
60
|
[watchedRef],
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@edgeandnode/eds-css/postcss-plugin'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ElementStyleObserver } from 'style-observer'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `style-observer` reads computed styles between its transition writes on each new target. Buffer
|
|
5
|
+
* those first writes so every target can read its original styles before any transitions change.
|
|
6
|
+
* Subsequent updates retain the library's transition and browser compatibility handling.
|
|
7
|
+
*/
|
|
8
|
+
export class BatchedElementStyleObserver extends ElementStyleObserver {
|
|
9
|
+
private pendingStyles: Map<string, { value: string; priority: string | undefined }> | undefined =
|
|
10
|
+
new Map()
|
|
11
|
+
|
|
12
|
+
override setProperty(property: string, value: string, priority?: string) {
|
|
13
|
+
if (this.pendingStyles) {
|
|
14
|
+
this.pendingStyles.set(property, { value, priority })
|
|
15
|
+
} else {
|
|
16
|
+
super.setProperty(property, value, priority)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
override getProperty(property: string) {
|
|
21
|
+
return this.pendingStyles?.get(property)?.value ?? super.getProperty(property)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
flush() {
|
|
25
|
+
const pendingStyles = this.pendingStyles
|
|
26
|
+
this.pendingStyles = undefined
|
|
27
|
+
for (const [property, { value, priority }] of pendingStyles ?? []) {
|
|
28
|
+
super.setProperty(property, value, priority)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
let styleQueriesSupported: boolean | null = null
|
|
2
|
+
|
|
3
|
+
export function getStyleQueriesSupported(): boolean {
|
|
4
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') return false
|
|
5
|
+
if (styleQueriesSupported === null) {
|
|
6
|
+
styleQueriesSupported =
|
|
7
|
+
window.getComputedStyle(document.body).getPropertyValue('--eds-style-queries-test') ===
|
|
8
|
+
'success'
|
|
9
|
+
}
|
|
10
|
+
return styleQueriesSupported
|
|
11
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { getTooltipProps } from './getTooltipProps.ts'
|
|
2
2
|
export { isLazy, resolveLazy } from './resolveLazy.ts'
|
|
3
3
|
export { styleScheduler } from './styleScheduler.ts'
|
|
4
|
+
export { BatchedElementStyleObserver } from './BatchedElementStyleObserver.ts'
|
|
5
|
+
export { getStyleQueriesSupported } from './getStyleQueriesSupported.ts'
|