@react-spectrum/utils 3.0.0-nightly-641446f65-240905

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 (64) hide show
  1. package/README.md +3 -0
  2. package/dist/BreakpointProvider.main.js +75 -0
  3. package/dist/BreakpointProvider.main.js.map +1 -0
  4. package/dist/BreakpointProvider.mjs +64 -0
  5. package/dist/BreakpointProvider.module.js +64 -0
  6. package/dist/BreakpointProvider.module.js.map +1 -0
  7. package/dist/Slots.main.js +73 -0
  8. package/dist/Slots.main.js.map +1 -0
  9. package/dist/Slots.mjs +61 -0
  10. package/dist/Slots.module.js +61 -0
  11. package/dist/Slots.module.js.map +1 -0
  12. package/dist/classNames.main.js +50 -0
  13. package/dist/classNames.main.js.map +1 -0
  14. package/dist/classNames.mjs +39 -0
  15. package/dist/classNames.module.js +39 -0
  16. package/dist/classNames.module.js.map +1 -0
  17. package/dist/getWrappedElement.main.js +32 -0
  18. package/dist/getWrappedElement.main.js.map +1 -0
  19. package/dist/getWrappedElement.mjs +23 -0
  20. package/dist/getWrappedElement.module.js +23 -0
  21. package/dist/getWrappedElement.module.js.map +1 -0
  22. package/dist/import.mjs +36 -0
  23. package/dist/main.js +70 -0
  24. package/dist/main.js.map +1 -0
  25. package/dist/module.js +36 -0
  26. package/dist/module.js.map +1 -0
  27. package/dist/styleProps.main.js +468 -0
  28. package/dist/styleProps.main.js.map +1 -0
  29. package/dist/styleProps.mjs +456 -0
  30. package/dist/styleProps.module.js +456 -0
  31. package/dist/styleProps.module.js.map +1 -0
  32. package/dist/types.d.ts +62 -0
  33. package/dist/types.d.ts.map +1 -0
  34. package/dist/useDOMRef.main.js +64 -0
  35. package/dist/useDOMRef.main.js.map +1 -0
  36. package/dist/useDOMRef.mjs +54 -0
  37. package/dist/useDOMRef.module.js +54 -0
  38. package/dist/useDOMRef.module.js.map +1 -0
  39. package/dist/useHasChild.main.js +35 -0
  40. package/dist/useHasChild.main.js.map +1 -0
  41. package/dist/useHasChild.mjs +30 -0
  42. package/dist/useHasChild.module.js +30 -0
  43. package/dist/useHasChild.module.js.map +1 -0
  44. package/dist/useIsMobileDevice.main.js +28 -0
  45. package/dist/useIsMobileDevice.main.js.map +1 -0
  46. package/dist/useIsMobileDevice.mjs +23 -0
  47. package/dist/useIsMobileDevice.module.js +23 -0
  48. package/dist/useIsMobileDevice.module.js.map +1 -0
  49. package/dist/useMediaQuery.main.js +46 -0
  50. package/dist/useMediaQuery.main.js.map +1 -0
  51. package/dist/useMediaQuery.mjs +41 -0
  52. package/dist/useMediaQuery.module.js +41 -0
  53. package/dist/useMediaQuery.module.js.map +1 -0
  54. package/package.json +41 -0
  55. package/src/BreakpointProvider.tsx +92 -0
  56. package/src/Slots.tsx +70 -0
  57. package/src/classNames.ts +56 -0
  58. package/src/getWrappedElement.tsx +23 -0
  59. package/src/index.ts +35 -0
  60. package/src/styleProps.ts +313 -0
  61. package/src/useDOMRef.ts +57 -0
  62. package/src/useHasChild.ts +23 -0
  63. package/src/useIsMobileDevice.ts +24 -0
  64. package/src/useMediaQuery.ts +44 -0
package/src/Slots.tsx ADDED
@@ -0,0 +1,70 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {mergeProps} from '@react-aria/utils';
14
+ import React, {useContext, useMemo} from 'react';
15
+
16
+ interface SlotProps {
17
+ slot?: string
18
+ }
19
+
20
+ let SlotContext = React.createContext(null);
21
+
22
+ export function useSlotProps<T>(props: T & {id?: string}, defaultSlot?: string): T {
23
+ let slot = (props as SlotProps).slot || defaultSlot;
24
+ let {[slot]: slotProps = {}} = useContext(SlotContext) || {};
25
+
26
+ return mergeProps(props, mergeProps(slotProps, {id: props.id}));
27
+ }
28
+
29
+ export function cssModuleToSlots(cssModule) {
30
+ return Object.keys(cssModule).reduce((acc, slot) => {
31
+ acc[slot] = {UNSAFE_className: cssModule[slot]};
32
+ return acc;
33
+ }, {});
34
+ }
35
+
36
+ export function SlotProvider(props) {
37
+ // eslint-disable-next-line react-hooks/exhaustive-deps
38
+ let parentSlots = useContext(SlotContext) || {};
39
+ let {slots = {}, children} = props;
40
+
41
+ // Merge props for each slot from parent context and props
42
+ let value = useMemo(() =>
43
+ Object.keys(parentSlots)
44
+ .concat(Object.keys(slots))
45
+ .reduce((o, p) => ({
46
+ ...o,
47
+ [p]: mergeProps(parentSlots[p] || {}, slots[p] || {})}), {})
48
+ , [parentSlots, slots]);
49
+
50
+ return (
51
+ <SlotContext.Provider value={value}>
52
+ {children}
53
+ </SlotContext.Provider>
54
+ );
55
+ }
56
+
57
+ export function ClearSlots(props) {
58
+ let {children, ...otherProps} = props;
59
+ let content = children;
60
+ if (React.Children.toArray(children).length <= 1) {
61
+ if (typeof children === 'function') { // need to know if the node is a string or something else that react can render that doesn't get props
62
+ content = React.cloneElement(React.Children.only(children), otherProps);
63
+ }
64
+ }
65
+ return (
66
+ <SlotContext.Provider value={{}}>
67
+ {content}
68
+ </SlotContext.Provider>
69
+ );
70
+ }
@@ -0,0 +1,56 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import _clsx from 'clsx';
14
+
15
+ export let shouldKeepSpectrumClassNames = false;
16
+
17
+ export function keepSpectrumClassNames() {
18
+ shouldKeepSpectrumClassNames = true;
19
+ console.warn(
20
+ 'Legacy spectrum-prefixed class names enabled for backward compatibility. ' +
21
+ 'We recommend replacing instances of CSS overrides targeting spectrum selectors ' +
22
+ 'in your app with custom class names of your own, and disabling this flag.'
23
+ );
24
+ }
25
+
26
+ export function classNames(cssModule: {[key: string]: string}, ...values: Array<string | Object | undefined>): string {
27
+ let classes = [];
28
+ for (let value of values) {
29
+ if (typeof value === 'object' && value) {
30
+ let mapped = {};
31
+ for (let key in value) {
32
+ if (cssModule[key]) {
33
+ mapped[cssModule[key]] = value[key];
34
+ }
35
+
36
+ if (shouldKeepSpectrumClassNames || !cssModule[key]) {
37
+ mapped[key] = value[key];
38
+ }
39
+ }
40
+
41
+ classes.push(mapped);
42
+ } else if (typeof value === 'string') {
43
+ if (cssModule[value]) {
44
+ classes.push(cssModule[value]);
45
+ }
46
+
47
+ if (shouldKeepSpectrumClassNames || !cssModule[value]) {
48
+ classes.push(value);
49
+ }
50
+ } else {
51
+ classes.push(value);
52
+ }
53
+ }
54
+
55
+ return _clsx(...classes);
56
+ }
@@ -0,0 +1,23 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import React, {JSXElementConstructor, ReactElement, ReactNode} from 'react';
14
+
15
+ export function getWrappedElement(children: string | ReactElement | ReactNode): ReactElement<any, JSXElementConstructor<any>> {
16
+ let element;
17
+ if (typeof children === 'string') {
18
+ element = <span>{children}</span>;
19
+ } else {
20
+ element = React.Children.only(children);
21
+ }
22
+ return element;
23
+ }
package/src/index.ts ADDED
@@ -0,0 +1,35 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ /// <reference types="css-module-types" />
14
+
15
+ export type {StyleHandlers} from './styleProps';
16
+ export {shouldKeepSpectrumClassNames, keepSpectrumClassNames, classNames} from './classNames';
17
+ export {getWrappedElement} from './getWrappedElement';
18
+ export {useMediaQuery} from './useMediaQuery';
19
+ export {createDOMRef, createFocusableRef, useDOMRef, useFocusableRef, unwrapDOMRef, useUnwrapDOMRef} from './useDOMRef';
20
+ export {
21
+ baseStyleProps,
22
+ viewStyleProps,
23
+ dimensionValue,
24
+ responsiveDimensionValue,
25
+ convertStyleProps,
26
+ useStyleProps,
27
+ passthroughStyle,
28
+ getResponsiveProp
29
+ } from './styleProps';
30
+ export {useSlotProps, cssModuleToSlots, SlotProvider, ClearSlots} from './Slots';
31
+ export {useHasChild} from './useHasChild';
32
+ export {useIsMobileDevice} from './useIsMobileDevice';
33
+ export {useValueEffect} from '@react-aria/utils';
34
+ export {BreakpointProvider, useMatchedBreakpoints, useBreakpoint} from './BreakpointProvider';
35
+ export {useResizeObserver} from '@react-aria/utils';
@@ -0,0 +1,313 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {BackgroundColorValue, BorderColorValue, BorderRadiusValue, BorderSizeValue, ColorValue, ColorVersion, DimensionValue, Direction, Responsive, ResponsiveProp, StyleProps, ViewStyleProps} from '@react-types/shared';
14
+ import {CSSProperties, HTMLAttributes} from 'react';
15
+ import {useBreakpoint} from './BreakpointProvider';
16
+ import {useLocale} from '@react-aria/i18n';
17
+
18
+ type Breakpoint = 'base' | 'S' | 'M' | 'L' | string;
19
+ type StyleName = string | string[] | ((dir: Direction) => string);
20
+ type StyleHandler = (value: any, colorVersion?: number) => string;
21
+ export interface StyleHandlers {
22
+ [key: string]: [StyleName, StyleHandler]
23
+ }
24
+
25
+ export const baseStyleProps: StyleHandlers = {
26
+ margin: ['margin', dimensionValue],
27
+ marginStart: [rtl('marginLeft', 'marginRight'), dimensionValue],
28
+ marginEnd: [rtl('marginRight', 'marginLeft'), dimensionValue],
29
+ // marginLeft: ['marginLeft', dimensionValue],
30
+ // marginRight: ['marginRight', dimensionValue],
31
+ marginTop: ['marginTop', dimensionValue],
32
+ marginBottom: ['marginBottom', dimensionValue],
33
+ marginX: [['marginLeft', 'marginRight'], dimensionValue],
34
+ marginY: [['marginTop', 'marginBottom'], dimensionValue],
35
+ width: ['width', dimensionValue],
36
+ height: ['height', dimensionValue],
37
+ minWidth: ['minWidth', dimensionValue],
38
+ minHeight: ['minHeight', dimensionValue],
39
+ maxWidth: ['maxWidth', dimensionValue],
40
+ maxHeight: ['maxHeight', dimensionValue],
41
+ isHidden: ['display', hiddenValue],
42
+ alignSelf: ['alignSelf', passthroughStyle],
43
+ justifySelf: ['justifySelf', passthroughStyle],
44
+ position: ['position', anyValue],
45
+ zIndex: ['zIndex', anyValue],
46
+ top: ['top', dimensionValue],
47
+ bottom: ['bottom', dimensionValue],
48
+ start: [rtl('left', 'right'), dimensionValue],
49
+ end: [rtl('right', 'left'), dimensionValue],
50
+ left: ['left', dimensionValue],
51
+ right: ['right', dimensionValue],
52
+ order: ['order', anyValue],
53
+ flex: ['flex', flexValue],
54
+ flexGrow: ['flexGrow', passthroughStyle],
55
+ flexShrink: ['flexShrink', passthroughStyle],
56
+ flexBasis: ['flexBasis', passthroughStyle],
57
+ gridArea: ['gridArea', passthroughStyle],
58
+ gridColumn: ['gridColumn', passthroughStyle],
59
+ gridColumnEnd: ['gridColumnEnd', passthroughStyle],
60
+ gridColumnStart: ['gridColumnStart', passthroughStyle],
61
+ gridRow: ['gridRow', passthroughStyle],
62
+ gridRowEnd: ['gridRowEnd', passthroughStyle],
63
+ gridRowStart: ['gridRowStart', passthroughStyle]
64
+ };
65
+
66
+ export const viewStyleProps: StyleHandlers = {
67
+ ...baseStyleProps,
68
+ backgroundColor: ['backgroundColor', backgroundColorValue],
69
+ borderWidth: ['borderWidth', borderSizeValue],
70
+ borderStartWidth: [rtl('borderLeftWidth', 'borderRightWidth'), borderSizeValue],
71
+ borderEndWidth: [rtl('borderRightWidth', 'borderLeftWidth'), borderSizeValue],
72
+ borderLeftWidth: ['borderLeftWidth', borderSizeValue],
73
+ borderRightWidth: ['borderRightWidth', borderSizeValue],
74
+ borderTopWidth: ['borderTopWidth', borderSizeValue],
75
+ borderBottomWidth: ['borderBottomWidth', borderSizeValue],
76
+ borderXWidth: [['borderLeftWidth', 'borderRightWidth'], borderSizeValue],
77
+ borderYWidth: [['borderTopWidth', 'borderBottomWidth'], borderSizeValue],
78
+ borderColor: ['borderColor', borderColorValue],
79
+ borderStartColor: [rtl('borderLeftColor', 'borderRightColor'), borderColorValue],
80
+ borderEndColor: [rtl('borderRightColor', 'borderLeftColor'), borderColorValue],
81
+ borderLeftColor: ['borderLeftColor', borderColorValue],
82
+ borderRightColor: ['borderRightColor', borderColorValue],
83
+ borderTopColor: ['borderTopColor', borderColorValue],
84
+ borderBottomColor: ['borderBottomColor', borderColorValue],
85
+ borderXColor: [['borderLeftColor', 'borderRightColor'], borderColorValue],
86
+ borderYColor: [['borderTopColor', 'borderBottomColor'], borderColorValue],
87
+ borderRadius: ['borderRadius', borderRadiusValue],
88
+ borderTopStartRadius: [rtl('borderTopLeftRadius', 'borderTopRightRadius'), borderRadiusValue],
89
+ borderTopEndRadius: [rtl('borderTopRightRadius', 'borderTopLeftRadius'), borderRadiusValue],
90
+ borderBottomStartRadius: [rtl('borderBottomLeftRadius', 'borderBottomRightRadius'), borderRadiusValue],
91
+ borderBottomEndRadius: [rtl('borderBottomRightRadius', 'borderBottomLeftRadius'), borderRadiusValue],
92
+ borderTopLeftRadius: ['borderTopLeftRadius', borderRadiusValue],
93
+ borderTopRightRadius: ['borderTopRightRadius', borderRadiusValue],
94
+ borderBottomLeftRadius: ['borderBottomLeftRadius', borderRadiusValue],
95
+ borderBottomRightRadius: ['borderBottomRightRadius', borderRadiusValue],
96
+ padding: ['padding', dimensionValue],
97
+ paddingStart: [rtl('paddingLeft', 'paddingRight'), dimensionValue],
98
+ paddingEnd: [rtl('paddingRight', 'paddingLeft'), dimensionValue],
99
+ paddingLeft: ['paddingLeft', dimensionValue],
100
+ paddingRight: ['paddingRight', dimensionValue],
101
+ paddingTop: ['paddingTop', dimensionValue],
102
+ paddingBottom: ['paddingBottom', dimensionValue],
103
+ paddingX: [['paddingLeft', 'paddingRight'], dimensionValue],
104
+ paddingY: [['paddingTop', 'paddingBottom'], dimensionValue],
105
+ overflow: ['overflow', passthroughStyle]
106
+ };
107
+
108
+ const borderStyleProps = {
109
+ borderWidth: 'borderStyle',
110
+ borderLeftWidth: 'borderLeftStyle',
111
+ borderRightWidth: 'borderRightStyle',
112
+ borderTopWidth: 'borderTopStyle',
113
+ borderBottomWidth: 'borderBottomStyle'
114
+ };
115
+
116
+ function rtl(ltr: string, rtl: string) {
117
+ return (direction: Direction) => (
118
+ direction === 'rtl' ? rtl : ltr
119
+ );
120
+ }
121
+
122
+ const UNIT_RE = /(%|px|em|rem|vw|vh|auto|cm|mm|in|pt|pc|ex|ch|rem|vmin|vmax|fr)$/;
123
+ const FUNC_RE = /^\s*\w+\(/;
124
+ const SPECTRUM_VARIABLE_RE = /(static-)?size-\d+|single-line-(height|width)/g;
125
+
126
+ export function dimensionValue(value: DimensionValue) {
127
+ if (typeof value === 'number') {
128
+ return value + 'px';
129
+ }
130
+
131
+ if (!value) {
132
+ return undefined;
133
+ }
134
+
135
+ if (UNIT_RE.test(value)) {
136
+ return value;
137
+ }
138
+
139
+ if (FUNC_RE.test(value)) {
140
+ return value.replace(SPECTRUM_VARIABLE_RE, 'var(--spectrum-global-dimension-$&, var(--spectrum-alias-$&))');
141
+ }
142
+
143
+ return `var(--spectrum-global-dimension-${value}, var(--spectrum-alias-${value}))`;
144
+ }
145
+
146
+ export function responsiveDimensionValue(value: Responsive<DimensionValue>, matchedBreakpoints: Breakpoint[]) {
147
+ value = getResponsiveProp(value, matchedBreakpoints);
148
+ return dimensionValue(value);
149
+ }
150
+
151
+ type ColorType = 'default' | 'background' | 'border' | 'icon' | 'status';
152
+ function colorValue(value: ColorValue, type: ColorType = 'default', version = 5) {
153
+ if (version > 5) {
154
+ return `var(--spectrum-${value}, var(--spectrum-semantic-${value}-color-${type}))`;
155
+ }
156
+
157
+ return `var(--spectrum-legacy-color-${value}, var(--spectrum-global-color-${value}, var(--spectrum-semantic-${value}-color-${type})))`;
158
+ }
159
+
160
+ function backgroundColorValue(value: BackgroundColorValue, version = 5) {
161
+ if (!value) {
162
+ return undefined;
163
+ }
164
+
165
+ return `var(--spectrum-alias-background-color-${value}, ${colorValue(value as ColorValue, 'background', version)})`;
166
+ }
167
+
168
+ function borderColorValue(value: BorderColorValue, version = 5) {
169
+ if (!value) {
170
+ return undefined;
171
+ }
172
+
173
+ if (value === 'default') {
174
+ return 'var(--spectrum-alias-border-color)';
175
+ }
176
+
177
+ return `var(--spectrum-alias-border-color-${value}, ${colorValue(value as ColorValue, 'border', version)})`;
178
+ }
179
+
180
+ function borderSizeValue(value?: BorderSizeValue | null) {
181
+ return value && value !== 'none'
182
+ ? `var(--spectrum-alias-border-size-${value})`
183
+ : '0';
184
+ }
185
+
186
+ function borderRadiusValue(value: BorderRadiusValue) {
187
+ if (!value) {
188
+ return undefined;
189
+ }
190
+
191
+ return `var(--spectrum-alias-border-radius-${value})`;
192
+ }
193
+
194
+ function hiddenValue(value: boolean) {
195
+ return value ? 'none' : undefined;
196
+ }
197
+
198
+ function anyValue(value: any) {
199
+ return value;
200
+ }
201
+
202
+ function flexValue(value: boolean | number | string) {
203
+ if (typeof value === 'boolean') {
204
+ return value ? '1' : undefined;
205
+ }
206
+
207
+ return '' + value;
208
+ }
209
+
210
+ export function convertStyleProps<C extends ColorVersion>(props: ViewStyleProps<C>, handlers: StyleHandlers, direction: Direction, matchedBreakpoints: Breakpoint[]) {
211
+ let style: CSSProperties = {};
212
+ for (let key in props) {
213
+ let styleProp = handlers[key];
214
+ if (!styleProp || props[key] == null) {
215
+ continue;
216
+ }
217
+
218
+ let [name, convert] = styleProp;
219
+ if (typeof name === 'function') {
220
+ name = name(direction);
221
+ }
222
+
223
+ let prop = getResponsiveProp(props[key], matchedBreakpoints);
224
+ let value = convert(prop, props.colorVersion);
225
+ if (Array.isArray(name)) {
226
+ for (let k of name) {
227
+ style[k] = value;
228
+ }
229
+ } else {
230
+ style[name] = value;
231
+ }
232
+ }
233
+
234
+ for (let prop in borderStyleProps) {
235
+ if (style[prop]) {
236
+ style[borderStyleProps[prop]] = 'solid';
237
+ style.boxSizing = 'border-box';
238
+ }
239
+ }
240
+
241
+ return style;
242
+ }
243
+
244
+ type StylePropsOptions = {
245
+ matchedBreakpoints?: Breakpoint[]
246
+ };
247
+
248
+ export function useStyleProps<T extends StyleProps>(
249
+ props: T,
250
+ handlers: StyleHandlers = baseStyleProps,
251
+ options: StylePropsOptions = {}
252
+ ) {
253
+ let {
254
+ UNSAFE_className,
255
+ UNSAFE_style,
256
+ ...otherProps
257
+ } = props;
258
+ let breakpointProvider = useBreakpoint();
259
+ let {direction} = useLocale();
260
+ let {
261
+ matchedBreakpoints = breakpointProvider?.matchedBreakpoints || ['base']
262
+ } = options;
263
+ let styles = convertStyleProps(props, handlers, direction, matchedBreakpoints);
264
+ let style = {...UNSAFE_style, ...styles};
265
+
266
+ // @ts-ignore
267
+ if (otherProps.className) {
268
+ console.warn(
269
+ 'The className prop is unsafe and is unsupported in React Spectrum v3. ' +
270
+ 'Please use style props with Spectrum variables, or UNSAFE_className if you absolutely must do something custom. ' +
271
+ 'Note that this may break in future versions due to DOM structure changes.'
272
+ );
273
+ }
274
+
275
+ // @ts-ignore
276
+ if (otherProps.style) {
277
+ console.warn(
278
+ 'The style prop is unsafe and is unsupported in React Spectrum v3. ' +
279
+ 'Please use style props with Spectrum variables, or UNSAFE_style if you absolutely must do something custom. ' +
280
+ 'Note that this may break in future versions due to DOM structure changes.'
281
+ );
282
+ }
283
+
284
+ let styleProps: HTMLAttributes<HTMLElement> = {
285
+ style,
286
+ className: UNSAFE_className
287
+ };
288
+
289
+ if (getResponsiveProp(props.isHidden, matchedBreakpoints)) {
290
+ styleProps.hidden = true;
291
+ }
292
+
293
+ return {
294
+ styleProps
295
+ };
296
+ }
297
+
298
+ export function passthroughStyle(value) {
299
+ return value;
300
+ }
301
+
302
+ export function getResponsiveProp<T>(prop: Responsive<T>, matchedBreakpoints: Breakpoint[]): T {
303
+ if (prop && typeof prop === 'object' && !Array.isArray(prop)) {
304
+ for (let i = 0; i < matchedBreakpoints.length; i++) {
305
+ let breakpoint = matchedBreakpoints[i];
306
+ if (prop[breakpoint] != null) {
307
+ return prop[breakpoint];
308
+ }
309
+ }
310
+ return (prop as ResponsiveProp<T>).base;
311
+ }
312
+ return prop as T;
313
+ }
@@ -0,0 +1,57 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {DOMRef, DOMRefValue, FocusableElement, FocusableRef, FocusableRefValue, RefObject} from '@react-types/shared';
14
+ import {useImperativeHandle, useMemo, useRef} from 'react';
15
+
16
+ export function createDOMRef<T extends HTMLElement = HTMLElement>(ref: RefObject<T | null>): DOMRefValue<T> {
17
+ return {
18
+ UNSAFE_getDOMNode() {
19
+ return ref.current;
20
+ }
21
+ };
22
+ }
23
+
24
+ export function createFocusableRef<T extends HTMLElement = HTMLElement>(domRef: RefObject<T | null>, focusableRef: RefObject<FocusableElement | null> = domRef): FocusableRefValue<T> {
25
+ return {
26
+ ...createDOMRef(domRef),
27
+ focus() {
28
+ if (focusableRef.current) {
29
+ focusableRef.current.focus();
30
+ }
31
+ }
32
+ };
33
+ }
34
+
35
+ export function useDOMRef<T extends HTMLElement = HTMLElement>(ref: DOMRef<T>): RefObject<T | null> {
36
+ let domRef = useRef<T>(null);
37
+ useImperativeHandle(ref, () => createDOMRef(domRef));
38
+ return domRef;
39
+ }
40
+
41
+ export function useFocusableRef<T extends HTMLElement = HTMLElement>(ref: FocusableRef<T>, focusableRef?: RefObject<FocusableElement | null>): RefObject<T | null> {
42
+ let domRef = useRef<T>(null);
43
+ useImperativeHandle(ref, () => createFocusableRef(domRef, focusableRef));
44
+ return domRef;
45
+ }
46
+
47
+ export function unwrapDOMRef<T extends HTMLElement>(ref: RefObject<DOMRefValue<T> | null>): RefObject<T | null> {
48
+ return {
49
+ get current() {
50
+ return ref.current && ref.current.UNSAFE_getDOMNode();
51
+ }
52
+ };
53
+ }
54
+
55
+ export function useUnwrapDOMRef<T extends HTMLElement>(ref: RefObject<DOMRefValue<T> | null>) : RefObject<T | null> {
56
+ return useMemo(() => unwrapDOMRef(ref), [ref]);
57
+ }
@@ -0,0 +1,23 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {RefObject} from '@react-types/shared';
14
+ import {useLayoutEffect} from '@react-aria/utils';
15
+ import {useState} from 'react';
16
+
17
+ export function useHasChild(query: string, ref: RefObject<HTMLElement | null>) {
18
+ let [hasChild, setHasChild] = useState(true);
19
+ useLayoutEffect(() => {
20
+ setHasChild(!!(ref.current && ref.current.querySelector(query)));
21
+ }, [setHasChild, query, ref]);
22
+ return hasChild;
23
+ }
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {useIsSSR} from '@react-aria/ssr';
14
+
15
+ const MOBILE_SCREEN_WIDTH = 700;
16
+
17
+ export function useIsMobileDevice(): boolean {
18
+ let isSSR = useIsSSR();
19
+ if (isSSR || typeof window === 'undefined') {
20
+ return false;
21
+ }
22
+
23
+ return window.screen.width <= MOBILE_SCREEN_WIDTH;
24
+ }
@@ -0,0 +1,44 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {useEffect, useState} from 'react';
14
+ import {useIsSSR} from '@react-aria/ssr';
15
+
16
+ export function useMediaQuery(query: string) {
17
+ let supportsMatchMedia = typeof window !== 'undefined' && typeof window.matchMedia === 'function';
18
+ let [matches, setMatches] = useState(() =>
19
+ supportsMatchMedia
20
+ ? window.matchMedia(query).matches
21
+ : false
22
+ );
23
+
24
+ useEffect(() => {
25
+ if (!supportsMatchMedia) {
26
+ return;
27
+ }
28
+
29
+ let mq = window.matchMedia(query);
30
+ let onChange = (evt) => {
31
+ setMatches(evt.matches);
32
+ };
33
+
34
+ mq.addListener(onChange);
35
+ return () => {
36
+ mq.removeListener(onChange);
37
+ };
38
+ }, [supportsMatchMedia, query]);
39
+
40
+ // If in SSR, the media query should never match. Once the page hydrates,
41
+ // this will update and the real value will be returned.
42
+ let isSSR = useIsSSR();
43
+ return isSSR ? false : matches;
44
+ }