@instructure/emotion 11.6.0 → 11.6.1-snapshot-129
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/CHANGELOG.md +51 -303
- package/es/InstUISettingsProvider/index.js +1 -1
- package/es/getComponentThemeOverride.js +5 -4
- package/es/getTheme.js +2 -2
- package/es/index.js +4 -1
- package/es/styleUtils/calcFocusOutlineStyles.js +71 -0
- package/es/styleUtils/calcSpacingFromShorthand.js +112 -0
- package/es/styleUtils/index.js +2 -1
- package/es/useStyle.js +27 -6
- package/es/useStyleLegacy.js +49 -0
- package/es/useTheme.js +1 -1
- package/es/withStyle.js +13 -31
- package/es/withStyleLegacy.js +116 -0
- package/lib/InstUISettingsProvider/index.js +1 -1
- package/lib/getComponentThemeOverride.js +5 -4
- package/lib/getTheme.js +2 -2
- package/lib/index.js +38 -7
- package/lib/styleUtils/calcFocusOutlineStyles.js +78 -0
- package/lib/styleUtils/calcSpacingFromShorthand.js +118 -0
- package/lib/styleUtils/index.js +14 -7
- package/lib/useStyle.js +28 -6
- package/lib/useStyleLegacy.js +59 -0
- package/lib/useTheme.js +1 -1
- package/lib/withStyle.js +13 -31
- package/lib/withStyleLegacy.js +125 -0
- package/package.json +11 -9
- package/src/EmotionTypes.ts +10 -1
- package/src/InstUISettingsProvider/index.tsx +5 -1
- package/src/getComponentThemeOverride.ts +9 -8
- package/src/getTheme.ts +8 -2
- package/src/index.ts +7 -2
- package/src/styleUtils/calcFocusOutlineStyles.ts +106 -0
- package/src/styleUtils/calcSpacingFromShorthand.ts +127 -0
- package/src/styleUtils/index.ts +2 -1
- package/src/useStyle.ts +63 -32
- package/src/useStyleLegacy.ts +92 -0
- package/src/useTheme.ts +4 -1
- package/src/withStyle.tsx +29 -39
- package/src/withStyleLegacy.tsx +212 -0
- package/tsconfig.build.json +3 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/EmotionTypes.d.ts +4 -2
- package/types/EmotionTypes.d.ts.map +1 -1
- package/types/InstUISettingsProvider/index.d.ts.map +1 -1
- package/types/getComponentThemeOverride.d.ts +4 -5
- package/types/getComponentThemeOverride.d.ts.map +1 -1
- package/types/getTheme.d.ts.map +1 -1
- package/types/index.d.ts +6 -2
- package/types/index.d.ts.map +1 -1
- package/types/styleUtils/calcFocusOutlineStyles.d.ts +51 -0
- package/types/styleUtils/calcFocusOutlineStyles.d.ts.map +1 -0
- package/types/styleUtils/calcSpacingFromShorthand.d.ts +33 -0
- package/types/styleUtils/calcSpacingFromShorthand.d.ts.map +1 -0
- package/types/styleUtils/index.d.ts +2 -1
- package/types/styleUtils/index.d.ts.map +1 -1
- package/types/useStyle.d.ts +15 -13
- package/types/useStyle.d.ts.map +1 -1
- package/types/useStyleLegacy.d.ts +22 -0
- package/types/useStyleLegacy.d.ts.map +1 -0
- package/types/useTheme.d.ts.map +1 -1
- package/types/withStyle.d.ts +2 -22
- package/types/withStyle.d.ts.map +1 -1
- package/types/withStyleLegacy.d.ts +22 -0
- package/types/withStyleLegacy.d.ts.map +1 -0
- package/es/styleUtils/mapSpacingToShorthand.js +0 -29
- package/lib/styleUtils/mapSpacingToShorthand.js +0 -35
- package/src/styleUtils/mapSpacingToShorthand.ts +0 -35
- package/types/styleUtils/mapSpacingToShorthand.d.ts +0 -5
- package/types/styleUtils/mapSpacingToShorthand.d.ts.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instructure/emotion",
|
|
3
|
-
"version": "11.6.
|
|
3
|
+
"version": "11.6.1-snapshot-129",
|
|
4
4
|
"description": "A UI component library made by Instructure Inc.",
|
|
5
5
|
"author": "Instructure, Inc. Engineering and Product Design",
|
|
6
6
|
"module": "./es/index.js",
|
|
@@ -17,13 +17,14 @@
|
|
|
17
17
|
"@babel/runtime": "^7.27.6",
|
|
18
18
|
"@emotion/react": "^11",
|
|
19
19
|
"hoist-non-react-statics": "^3.3.2",
|
|
20
|
-
"@instructure/
|
|
21
|
-
"@instructure/shared-types": "11.6.
|
|
22
|
-
"@instructure/ui-
|
|
23
|
-
"@instructure/ui-i18n": "11.6.
|
|
24
|
-
"@instructure/
|
|
25
|
-
"@instructure/ui-
|
|
26
|
-
"@instructure/ui-
|
|
20
|
+
"@instructure/ui-decorator": "11.6.1-snapshot-129",
|
|
21
|
+
"@instructure/shared-types": "11.6.1-snapshot-129",
|
|
22
|
+
"@instructure/ui-color-utils": "11.6.1-snapshot-129",
|
|
23
|
+
"@instructure/ui-i18n": "11.6.1-snapshot-129",
|
|
24
|
+
"@instructure/console": "11.6.1-snapshot-129",
|
|
25
|
+
"@instructure/ui-react-utils": "11.6.1-snapshot-129",
|
|
26
|
+
"@instructure/ui-themes": "11.6.1-snapshot-129",
|
|
27
|
+
"@instructure/ui-utils": "11.6.1-snapshot-129"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"@testing-library/jest-dom": "^6.6.3",
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
"@testing-library/user-event": "^14.6.1",
|
|
32
33
|
"react-dom": "18.3.1",
|
|
33
34
|
"vitest": "^3.2.2",
|
|
34
|
-
"@instructure/ui-babel-preset": "11.6.
|
|
35
|
+
"@instructure/ui-babel-preset": "11.6.1-snapshot-129"
|
|
35
36
|
},
|
|
36
37
|
"peerDependencies": {
|
|
37
38
|
"react": ">=18 <=19"
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
"sideEffects": false,
|
|
43
44
|
"exports": {
|
|
44
45
|
".": {
|
|
46
|
+
"src": "./src/index.ts",
|
|
45
47
|
"types": "./types/index.d.ts",
|
|
46
48
|
"import": "./es/index.js",
|
|
47
49
|
"require": "./lib/index.js",
|
package/src/EmotionTypes.ts
CHANGED
|
@@ -28,6 +28,7 @@ import type {
|
|
|
28
28
|
ComponentThemeMap,
|
|
29
29
|
DeepPartial
|
|
30
30
|
} from '@instructure/shared-types'
|
|
31
|
+
import type { Theme, SharedTokens } from '@instructure/ui-themes'
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* A theme object where every prop is optional
|
|
@@ -101,7 +102,7 @@ type Overrides = {
|
|
|
101
102
|
componentOverrides?: ComponentOverride
|
|
102
103
|
}
|
|
103
104
|
|
|
104
|
-
type BaseThemeOrOverride =
|
|
105
|
+
type BaseThemeOrOverride = Theme | PartialTheme | Overrides
|
|
105
106
|
|
|
106
107
|
type ThemeOrOverride =
|
|
107
108
|
| BaseThemeOrOverride
|
|
@@ -120,6 +121,13 @@ type GenerateStyle = (
|
|
|
120
121
|
state?: State
|
|
121
122
|
) => StyleObject
|
|
122
123
|
|
|
124
|
+
type GenerateStyleRework = (
|
|
125
|
+
componentTheme: ComponentTheme,
|
|
126
|
+
props: Props,
|
|
127
|
+
sharedTokens: SharedTokens,
|
|
128
|
+
state?: State
|
|
129
|
+
) => StyleObject
|
|
130
|
+
|
|
123
131
|
type GenerateStyleFunctional = (
|
|
124
132
|
componentTheme: ComponentTheme,
|
|
125
133
|
params: Record<string, unknown>
|
|
@@ -149,6 +157,7 @@ export type {
|
|
|
149
157
|
State,
|
|
150
158
|
GenerateComponentTheme,
|
|
151
159
|
GenerateStyle,
|
|
160
|
+
GenerateStyleRework,
|
|
152
161
|
GenerateStyleFunctional,
|
|
153
162
|
ComponentStyle
|
|
154
163
|
}
|
|
@@ -73,7 +73,11 @@ function InstUISettingsProvider({
|
|
|
73
73
|
}: InstUIProviderProps) {
|
|
74
74
|
const finalDir = dir || useContext(TextDirectionContext)
|
|
75
75
|
|
|
76
|
-
if (
|
|
76
|
+
if (
|
|
77
|
+
(process.env.NODE_ENV !== 'production' ||
|
|
78
|
+
process.env.GITHUB_PULL_REQUEST_PREVIEW === 'true') &&
|
|
79
|
+
finalDir === 'auto'
|
|
80
|
+
) {
|
|
77
81
|
console.warn(
|
|
78
82
|
"'auto' is not an supported value for the 'dir' prop. Please pass 'ltr' or 'rtl'"
|
|
79
83
|
)
|
|
@@ -27,8 +27,9 @@ import type {
|
|
|
27
27
|
Overrides,
|
|
28
28
|
ComponentOverride
|
|
29
29
|
} from './EmotionTypes'
|
|
30
|
-
import type {
|
|
31
|
-
import
|
|
30
|
+
import type { ComponentTheme } from '@instructure/shared-types'
|
|
31
|
+
import { ThemeOverrideProp } from './withStyle'
|
|
32
|
+
import { ThemeOverrideValue } from './useStyle'
|
|
32
33
|
|
|
33
34
|
type ComponentName = keyof ComponentOverride | undefined
|
|
34
35
|
|
|
@@ -42,7 +43,7 @@ type ComponentName = keyof ComponentOverride | undefined
|
|
|
42
43
|
* @param theme - Theme object
|
|
43
44
|
* @param displayName - Name of the component
|
|
44
45
|
* @param componentId - componentId of the component
|
|
45
|
-
* @param
|
|
46
|
+
* @param themeOverride - The theme override object
|
|
46
47
|
* @param componentTheme - The component's default theme
|
|
47
48
|
* @returns The calculated theme override object
|
|
48
49
|
*/
|
|
@@ -50,14 +51,13 @@ const getComponentThemeOverride = (
|
|
|
50
51
|
theme: ThemeOrOverride,
|
|
51
52
|
displayName: string,
|
|
52
53
|
componentId?: string,
|
|
53
|
-
|
|
54
|
+
// ThemeOverrideProp is the old type, ThemeOverrideValue is the new one
|
|
55
|
+
themeOverride?: ThemeOverrideProp['themeOverride'] | ThemeOverrideValue,
|
|
54
56
|
componentTheme?: ComponentTheme
|
|
55
57
|
): Partial<ComponentTheme> => {
|
|
56
58
|
const name = displayName as ComponentName
|
|
57
59
|
const id = componentId as ComponentName
|
|
58
60
|
|
|
59
|
-
const themeOverride = props ? props.themeOverride : undefined
|
|
60
|
-
|
|
61
61
|
const { componentOverrides } = theme as Overrides
|
|
62
62
|
|
|
63
63
|
let overridesFromTheme: Partial<ComponentTheme> = {}
|
|
@@ -71,10 +71,11 @@ const getComponentThemeOverride = (
|
|
|
71
71
|
if (themeOverride) {
|
|
72
72
|
if (typeof themeOverride === 'function') {
|
|
73
73
|
overrideFromComponent = themeOverride(
|
|
74
|
-
|
|
74
|
+
//TODO type properly when the old theme is gone
|
|
75
|
+
componentTheme || ({} as any),
|
|
75
76
|
// the `theme` technically could be a partial theme / override object too,
|
|
76
77
|
// but we want to display all possible options
|
|
77
|
-
theme as
|
|
78
|
+
theme as any
|
|
78
79
|
)
|
|
79
80
|
} else {
|
|
80
81
|
overrideFromComponent = themeOverride
|
package/src/getTheme.ts
CHANGED
|
@@ -54,7 +54,10 @@ const getTheme =
|
|
|
54
54
|
// we need to clone the ancestor theme not to override it
|
|
55
55
|
let currentTheme
|
|
56
56
|
if (Object.keys(ancestorTheme).length === 0) {
|
|
57
|
-
if (
|
|
57
|
+
if (
|
|
58
|
+
process.env.NODE_ENV !== 'production' ||
|
|
59
|
+
process.env.GITHUB_PULL_REQUEST_PREVIEW === 'true'
|
|
60
|
+
) {
|
|
58
61
|
console.warn(
|
|
59
62
|
'No theme provided for [InstUISettingsProvider], using default `canvas` theme.'
|
|
60
63
|
)
|
|
@@ -78,7 +81,10 @@ const getTheme =
|
|
|
78
81
|
// If the prop passed is not an Object, it will throw an error.
|
|
79
82
|
// We are using this fail-safe here for the non-TS users,
|
|
80
83
|
// because the whole page can break without a theme.
|
|
81
|
-
if (
|
|
84
|
+
if (
|
|
85
|
+
process.env.NODE_ENV !== 'production' ||
|
|
86
|
+
process.env.GITHUB_PULL_REQUEST_PREVIEW === 'true'
|
|
87
|
+
) {
|
|
82
88
|
console.warn(
|
|
83
89
|
'The `theme` property provided to InstUISettingsProvider is not a valid InstUI theme object.\ntheme: ',
|
|
84
90
|
resolvedThemeOrOverride
|
package/src/index.ts
CHANGED
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
export * from '@emotion/react'
|
|
27
27
|
|
|
28
28
|
export { InstUISettingsProvider } from './InstUISettingsProvider'
|
|
29
|
+
export { withStyleLegacy } from './withStyleLegacy'
|
|
30
|
+
export { getComponentThemeOverride } from './getComponentThemeOverride'
|
|
29
31
|
export { withStyle } from './withStyle'
|
|
30
32
|
export {
|
|
31
33
|
ThemeablePropValues,
|
|
@@ -33,14 +35,17 @@ export {
|
|
|
33
35
|
getShorthandPropValue,
|
|
34
36
|
mirrorShorthandCorners,
|
|
35
37
|
mirrorShorthandEdges,
|
|
36
|
-
|
|
38
|
+
calcSpacingFromShorthand,
|
|
39
|
+
calcFocusOutlineStyles
|
|
37
40
|
} from './styleUtils'
|
|
38
41
|
|
|
42
|
+
export { useStyleLegacy } from './useStyleLegacy'
|
|
39
43
|
export { useStyle } from './useStyle'
|
|
40
44
|
export { useTheme } from './useTheme'
|
|
41
45
|
|
|
42
46
|
export type { ComponentStyle, StyleObject, Overrides } from './EmotionTypes'
|
|
43
|
-
export type { WithStyleProps } from './
|
|
47
|
+
export type { WithStyleProps } from './withStyleLegacy'
|
|
48
|
+
export type { ThemeOverrideValue } from './useStyle'
|
|
44
49
|
export type {
|
|
45
50
|
SpacingValues,
|
|
46
51
|
Spacing,
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The MIT License (MIT)
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2015 - present Instructure, Inc.
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
import { alpha } from '@instructure/ui-color-utils'
|
|
25
|
+
import type { SharedTokens } from '@instructure/ui-themes'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* This function creates CSS-in-JS styles for focus indicators.
|
|
29
|
+
*
|
|
30
|
+
* @returns CSS-in-JS style object containing focus outline styles ready for use with emotion or similar libraries.
|
|
31
|
+
*/
|
|
32
|
+
const calcFocusOutlineStyles = (
|
|
33
|
+
/**
|
|
34
|
+
* The focus outline theme configuration object containing color and sizing tokens.
|
|
35
|
+
*/
|
|
36
|
+
theme: SharedTokens['focusOutline'],
|
|
37
|
+
params?: {
|
|
38
|
+
/**
|
|
39
|
+
* The color variant to use for the focus outline
|
|
40
|
+
*/
|
|
41
|
+
focusColor?: 'info' | 'inverse' | 'success' | 'danger'
|
|
42
|
+
/**
|
|
43
|
+
* Whether to position the outline outside ('offset') or inside ('inset') the element.
|
|
44
|
+
*/
|
|
45
|
+
focusPosition?: 'offset' | 'inset'
|
|
46
|
+
/**
|
|
47
|
+
* Whether to include smooth transition animations for focus changes.
|
|
48
|
+
*/
|
|
49
|
+
shouldAnimateFocus?: boolean
|
|
50
|
+
/**
|
|
51
|
+
* Whether to apply focus styles to :focus-within pseudo-class for container elements.
|
|
52
|
+
*/
|
|
53
|
+
focusWithin?: boolean
|
|
54
|
+
/**
|
|
55
|
+
* Whether to force showing the focus outline.
|
|
56
|
+
*/
|
|
57
|
+
withFocusOutline?: boolean
|
|
58
|
+
/**
|
|
59
|
+
* What CSS selector to use to display the focus ring, `:focus` by default.
|
|
60
|
+
*/
|
|
61
|
+
customCSSSelector?: string
|
|
62
|
+
}
|
|
63
|
+
) => {
|
|
64
|
+
const focusColor = params?.focusColor ?? 'info'
|
|
65
|
+
const focusPosition = params?.focusPosition ?? 'offset'
|
|
66
|
+
const shouldAnimateFocus = params?.shouldAnimateFocus ?? true
|
|
67
|
+
const focusWithin = params?.focusWithin ?? false
|
|
68
|
+
const withFocusOutline = params?.withFocusOutline ?? false
|
|
69
|
+
const selector = params?.customCSSSelector ?? '&:focus'
|
|
70
|
+
|
|
71
|
+
const focusColorVariants = {
|
|
72
|
+
info: theme.infoColor,
|
|
73
|
+
inverse: theme.onColor,
|
|
74
|
+
success: theme.successColor,
|
|
75
|
+
danger: theme.dangerColor
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const outlineStyle = {
|
|
79
|
+
outlineColor: focusColorVariants[focusColor!],
|
|
80
|
+
outlineStyle: 'solid',
|
|
81
|
+
outlineWidth: theme.width,
|
|
82
|
+
outlineOffset: theme[focusPosition]
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
...(shouldAnimateFocus && {
|
|
86
|
+
transition: 'outline-color 0.2s, outline-offset 0.25s'
|
|
87
|
+
}),
|
|
88
|
+
outlineOffset: '-0.8rem',
|
|
89
|
+
outlineStyle: 'solid',
|
|
90
|
+
outlineColor: alpha(outlineStyle.outlineColor, 0),
|
|
91
|
+
...(withFocusOutline && outlineStyle),
|
|
92
|
+
[selector]: {
|
|
93
|
+
...outlineStyle,
|
|
94
|
+
'&:hover, &:active': {
|
|
95
|
+
// apply the same style so it's not overridden by some global style
|
|
96
|
+
...outlineStyle
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
...(focusWithin && {
|
|
100
|
+
'&:focus-within': outlineStyle
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export default calcFocusOutlineStyles
|
|
106
|
+
export { calcFocusOutlineStyles }
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The MIT License (MIT)
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2015 - present Instructure, Inc.
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
import type { Spacing } from './ThemeablePropValues'
|
|
25
|
+
|
|
26
|
+
type DeepStringRecord = {
|
|
27
|
+
[key: string]: string | DeepStringRecord
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Converts hyphen-case strings to camelCase
|
|
32
|
+
* Example: 'medium-small' -> 'mediumSmall'
|
|
33
|
+
*/
|
|
34
|
+
function camelize(str: string): string {
|
|
35
|
+
return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase())
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Converts shorthand spacing values into CSS strings using theme spacing tokens.
|
|
40
|
+
*
|
|
41
|
+
* This function parses space-separated spacing values (margin, padding) and resolves theme
|
|
42
|
+
* tokens to their actual CSS values. It supports CSS shorthand syntax (1-4 values), nested
|
|
43
|
+
* theme token paths using dot notation, and automatically converts hyphen-case tokens to camelCase.
|
|
44
|
+
*
|
|
45
|
+
* @param {Spacing | undefined} value - The shorthand spacing value string containing space-separated tokens or CSS values.
|
|
46
|
+
* Tokens can be in camelCase (mediumSmall) or hyphen-case (medium-small).
|
|
47
|
+
* Can be undefined, in which case '0' is returned.
|
|
48
|
+
* @param {Record<string, string>} spacingMap - The spacing theme object containing spacing tokens and nested values.
|
|
49
|
+
* Typically comes from `sharedTokens.margin.spacing` or `sharedTokens.padding.spacing` in the component theme.
|
|
50
|
+
*
|
|
51
|
+
* @returns {string} The resolved CSS spacing string ready to be used in styles.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // Hyphen-case tokens are converted to camelCase
|
|
55
|
+
* calcSpacingFromShorthand('medium-small', spacingMap) // resolves to spacingMap.mediumSmall
|
|
56
|
+
* calcSpacingFromShorthand('x-large small', spacingMap) // resolves to spacingMap.xLarge + spacingMap.small
|
|
57
|
+
*
|
|
58
|
+
* // Dot notation paths are NOT converted
|
|
59
|
+
* calcSpacingFromShorthand('gap.nested-value', spacingMap) // resolves to spacingMap.gap['nested-value']
|
|
60
|
+
*
|
|
61
|
+
* // CSS values like 'none', 'auto', '10px' are returned as-is
|
|
62
|
+
* calcSpacingFromShorthand('none', spacingMap) // returns 'none'
|
|
63
|
+
*/
|
|
64
|
+
export function calcSpacingFromShorthand(
|
|
65
|
+
value: Spacing | undefined,
|
|
66
|
+
spacingMap: DeepStringRecord
|
|
67
|
+
) {
|
|
68
|
+
// return undefined when there is no value -> this is important when a component (like View)
|
|
69
|
+
// doesn't have a prop like `padding` but has inline css for padding
|
|
70
|
+
// this makes sure to not overwrite the inline style
|
|
71
|
+
if (!value) return
|
|
72
|
+
|
|
73
|
+
const tokens = value.trim().split(' ')
|
|
74
|
+
|
|
75
|
+
// Handle whitespace-only strings
|
|
76
|
+
if (tokens.length === 1 && tokens[0] === '') return ''
|
|
77
|
+
|
|
78
|
+
// Map each token to its resolved CSS value
|
|
79
|
+
const resolvedValues = tokens.map((token) => {
|
|
80
|
+
// Handle special CSS value 'none' - convert to 0 for valid CSS
|
|
81
|
+
if (token === 'none') {
|
|
82
|
+
return '0'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Handle valid CSS numeric and keyword values
|
|
86
|
+
if (token === '0' || token === 'auto') {
|
|
87
|
+
return token
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Handle dot notation for nested theme token paths (no camelization)
|
|
91
|
+
if (token.includes('.')) {
|
|
92
|
+
const path = token.split('.')
|
|
93
|
+
let currentLevel: string | DeepStringRecord = spacingMap
|
|
94
|
+
|
|
95
|
+
for (const key of path) {
|
|
96
|
+
if (
|
|
97
|
+
currentLevel &&
|
|
98
|
+
typeof currentLevel === 'object' &&
|
|
99
|
+
key in currentLevel
|
|
100
|
+
) {
|
|
101
|
+
currentLevel = currentLevel[key]
|
|
102
|
+
} else {
|
|
103
|
+
console.warn(`Theme token path "${token}" not found in theme.`)
|
|
104
|
+
// If path doesn't resolve, return the original token as fallback
|
|
105
|
+
return token
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (typeof currentLevel === 'string') {
|
|
109
|
+
return currentLevel
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// For direct tokens, try camelized version
|
|
114
|
+
const camelizedToken = camelize(token)
|
|
115
|
+
const directValue = spacingMap[camelizedToken]
|
|
116
|
+
if (typeof directValue === 'string') {
|
|
117
|
+
return directValue
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Return the original token if not found (could be a direct CSS value like 'auto', '10px', etc.)
|
|
121
|
+
console.warn(`Theme token path "${token}" not found in theme.`)
|
|
122
|
+
return token
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// Return the space-separated resolved values
|
|
126
|
+
return resolvedValues.join(' ')
|
|
127
|
+
}
|
package/src/styleUtils/index.ts
CHANGED
|
@@ -27,7 +27,8 @@ export { makeThemeVars } from './makeThemeVars'
|
|
|
27
27
|
export { getShorthandPropValue } from './getShorthandPropValue'
|
|
28
28
|
export { mirrorShorthandCorners } from './mirrorShorthandCorners'
|
|
29
29
|
export { mirrorShorthandEdges } from './mirrorShorthandEdges'
|
|
30
|
-
export {
|
|
30
|
+
export { calcSpacingFromShorthand } from './calcSpacingFromShorthand'
|
|
31
|
+
export { calcFocusOutlineStyles } from './calcFocusOutlineStyles'
|
|
31
32
|
|
|
32
33
|
export type {
|
|
33
34
|
SpacingValues,
|
package/src/useStyle.ts
CHANGED
|
@@ -24,55 +24,86 @@
|
|
|
24
24
|
|
|
25
25
|
import { useTheme } from './useTheme'
|
|
26
26
|
import { getComponentThemeOverride } from './getComponentThemeOverride'
|
|
27
|
-
import type {
|
|
27
|
+
import type {
|
|
28
|
+
SharedTokens,
|
|
29
|
+
NewComponentTypes,
|
|
30
|
+
Theme
|
|
31
|
+
} from '@instructure/ui-themes'
|
|
32
|
+
import type { BaseThemeOrOverride } from './EmotionTypes'
|
|
28
33
|
|
|
29
34
|
// returns the second parameter of a function
|
|
30
35
|
type SecondParameter<T extends (...args: any) => any> =
|
|
31
36
|
Parameters<T>[1] extends undefined ? never : Parameters<T>[1]
|
|
32
37
|
|
|
33
|
-
type
|
|
34
|
-
|
|
35
|
-
params
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
type GenerateStyleParams =
|
|
39
|
+
| ((componentTheme: any, params: any, sharedTokens: SharedTokens) => any)
|
|
40
|
+
| ((componentTheme: any, params: any) => any)
|
|
41
|
+
| ((componentTheme: any) => any)
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Type for a theme override
|
|
45
|
+
*/
|
|
46
|
+
type ThemeOverrideValue =
|
|
47
|
+
| Partial<Theme>
|
|
48
|
+
| ((
|
|
49
|
+
componentTheme: Theme,
|
|
50
|
+
currentTheme: NewComponentTypes[keyof NewComponentTypes]
|
|
51
|
+
) => Partial<Theme>)
|
|
52
|
+
|
|
53
|
+
const isNewThemeObject = (obj: BaseThemeOrOverride): obj is Theme => {
|
|
54
|
+
return typeof (obj as any)?.newTheme === 'object'
|
|
39
55
|
}
|
|
40
56
|
|
|
41
|
-
|
|
57
|
+
/**
|
|
58
|
+
* new useStyle syntax, use this with v12 themes
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
// TODO: improve useStyle to handle generateStyle functions that don't
|
|
62
|
+
// have a theme.
|
|
63
|
+
const useStyle = <P extends GenerateStyleParams>(useStyleParams: {
|
|
42
64
|
generateStyle: P
|
|
43
65
|
params?: SecondParameter<P>
|
|
44
|
-
|
|
45
|
-
componentId
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
generateComponentTheme,
|
|
55
|
-
params,
|
|
56
|
-
componentId,
|
|
57
|
-
displayName
|
|
58
|
-
} = useStyleParams
|
|
66
|
+
// needs to be a string too because it might be a child component
|
|
67
|
+
componentId: keyof NewComponentTypes | string
|
|
68
|
+
themeOverride: ThemeOverrideValue | undefined
|
|
69
|
+
displayName?: string
|
|
70
|
+
//in case of a child component needed to use it's parent's tokens, provide parent's name
|
|
71
|
+
useTokensFrom?: keyof NewComponentTypes
|
|
72
|
+
}): ReturnType<P> => {
|
|
73
|
+
const { generateStyle, params, componentId, displayName, themeOverride } =
|
|
74
|
+
useStyleParams
|
|
75
|
+
const useTokensFrom = useStyleParams.useTokensFrom
|
|
59
76
|
const theme = useTheme()
|
|
60
|
-
const baseComponentTheme = generateComponentTheme
|
|
61
|
-
? generateComponentTheme(theme as BaseTheme)
|
|
62
|
-
: {}
|
|
63
77
|
|
|
64
|
-
|
|
78
|
+
let baseComponentTheme = {}
|
|
79
|
+
const componentWithTokensId = useTokensFrom ?? componentId
|
|
80
|
+
|
|
81
|
+
if (
|
|
82
|
+
isNewThemeObject(theme) && // TODO: is it possible not to have a theme object here?
|
|
83
|
+
theme.newTheme.components[componentWithTokensId as keyof NewComponentTypes]
|
|
84
|
+
) {
|
|
85
|
+
baseComponentTheme =
|
|
86
|
+
theme.newTheme.components[
|
|
87
|
+
componentWithTokensId as keyof NewComponentTypes
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
const finalOverride = getComponentThemeOverride(
|
|
65
91
|
theme,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
92
|
+
useTokensFrom ?? displayName ?? componentId ?? '',
|
|
93
|
+
componentWithTokensId,
|
|
94
|
+
themeOverride,
|
|
69
95
|
baseComponentTheme
|
|
70
96
|
)
|
|
71
97
|
|
|
72
|
-
const componentTheme = { ...baseComponentTheme, ...
|
|
98
|
+
const componentTheme = { ...baseComponentTheme, ...finalOverride }
|
|
73
99
|
|
|
74
|
-
return generateStyle(
|
|
100
|
+
return generateStyle(
|
|
101
|
+
componentTheme,
|
|
102
|
+
params,
|
|
103
|
+
(theme as Theme).newTheme.sharedTokens
|
|
104
|
+
)
|
|
75
105
|
}
|
|
76
106
|
|
|
77
107
|
export default useStyle
|
|
78
108
|
export { useStyle }
|
|
109
|
+
export type { ThemeOverrideValue }
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The MIT License (MIT)
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2015 - present Instructure, Inc.
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { useTheme } from './useTheme'
|
|
26
|
+
import { getComponentThemeOverride } from './getComponentThemeOverride'
|
|
27
|
+
import type { ComponentTheme } from '@instructure/shared-types'
|
|
28
|
+
import type { Theme } from '@instructure/ui-themes'
|
|
29
|
+
|
|
30
|
+
// returns the second parameter of a function
|
|
31
|
+
type SecondParameter<T extends (...args: any) => any> =
|
|
32
|
+
Parameters<T>[1] extends undefined ? never : Parameters<T>[1]
|
|
33
|
+
|
|
34
|
+
type GenerateComponentTheme = (theme: Theme) => ComponentTheme
|
|
35
|
+
|
|
36
|
+
type UseStyleParamsWithTheme<
|
|
37
|
+
P extends (componentTheme: any, params: any, theme: any) => any
|
|
38
|
+
> = {
|
|
39
|
+
generateStyle: P
|
|
40
|
+
params?: SecondParameter<P>
|
|
41
|
+
generateComponentTheme: GenerateComponentTheme
|
|
42
|
+
componentId: string
|
|
43
|
+
displayName?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// TODO this is only used by the old themes, remove when everything uses the new
|
|
47
|
+
// theming system
|
|
48
|
+
type UseStyleParamsWithoutTheme<
|
|
49
|
+
P extends (componentTheme: any, params: any, theme: any) => any
|
|
50
|
+
> = {
|
|
51
|
+
generateStyle: P
|
|
52
|
+
params?: SecondParameter<P>
|
|
53
|
+
generateComponentTheme?: undefined
|
|
54
|
+
componentId?: undefined
|
|
55
|
+
displayName?: undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/*
|
|
59
|
+
* This is only used by the **old themes**, remove when everything uses the new
|
|
60
|
+
* theming system (InstUI v12)
|
|
61
|
+
*/
|
|
62
|
+
const useStyleLegacy = <
|
|
63
|
+
P extends (componentTheme: any, params: any, theme: any) => any
|
|
64
|
+
>(
|
|
65
|
+
useStyleParams: UseStyleParamsWithTheme<P> | UseStyleParamsWithoutTheme<P>
|
|
66
|
+
): ReturnType<P> => {
|
|
67
|
+
const { generateStyle, params, componentId, displayName } = useStyleParams
|
|
68
|
+
const generateComponentTheme: GenerateComponentTheme = (
|
|
69
|
+
useStyleParams as UseStyleParamsWithTheme<P>
|
|
70
|
+
)?.generateComponentTheme
|
|
71
|
+
const theme = useTheme()
|
|
72
|
+
|
|
73
|
+
const baseComponentTheme =
|
|
74
|
+
typeof generateComponentTheme === 'function'
|
|
75
|
+
? generateComponentTheme(theme as Theme)
|
|
76
|
+
: {}
|
|
77
|
+
|
|
78
|
+
const themeOverride = getComponentThemeOverride(
|
|
79
|
+
theme,
|
|
80
|
+
displayName ?? componentId ?? '',
|
|
81
|
+
componentId,
|
|
82
|
+
params?.themeOverride,
|
|
83
|
+
baseComponentTheme
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
const componentTheme = { ...baseComponentTheme, ...themeOverride }
|
|
87
|
+
|
|
88
|
+
return generateStyle(componentTheme, params ? params : {}, theme)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export default useStyleLegacy
|
|
92
|
+
export { useStyleLegacy }
|
package/src/useTheme.ts
CHANGED
|
@@ -40,7 +40,10 @@ const useTheme = () => {
|
|
|
40
40
|
// This reads the theme from Emotion's ThemeContext
|
|
41
41
|
let theme = useEmotionTheme() as BaseThemeOrOverride
|
|
42
42
|
if (isEmpty(theme)) {
|
|
43
|
-
if (
|
|
43
|
+
if (
|
|
44
|
+
process.env.NODE_ENV !== 'production' ||
|
|
45
|
+
process.env.GITHUB_PULL_REQUEST_PREVIEW === 'true'
|
|
46
|
+
) {
|
|
44
47
|
console.warn(
|
|
45
48
|
`No theme provided for [InstUISettingsProvider], using default <canvas> theme.`
|
|
46
49
|
)
|