@instructure/ui-options 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 +36 -294
- package/es/Options/{Item → v1/Item}/index.js +1 -1
- package/es/Options/{Separator → v1/Separator}/index.js +1 -1
- package/es/Options/{index.js → v1/index.js} +2 -2
- package/es/Options/v2/Item/index.js +127 -0
- package/es/Options/v2/Item/props.js +26 -0
- package/es/Options/v2/Item/styles.js +189 -0
- package/es/Options/v2/Item/theme.js +72 -0
- package/es/Options/v2/Separator/index.js +69 -0
- package/es/Options/v2/Separator/props.js +26 -0
- package/es/Options/v2/Separator/styles.js +46 -0
- package/es/Options/v2/Separator/theme.js +45 -0
- package/es/Options/v2/index.js +147 -0
- package/es/Options/v2/props.js +26 -0
- package/es/Options/v2/styles.js +57 -0
- package/es/Options/v2/theme.js +47 -0
- package/es/{index.js → exports/a.js} +6 -6
- package/es/exports/b.js +29 -0
- package/lib/Options/{Item → v1/Item}/index.js +1 -1
- package/lib/Options/{Separator → v1/Separator}/index.js +1 -1
- package/lib/Options/v1/index.js +153 -0
- package/lib/Options/v2/Item/index.js +132 -0
- package/lib/Options/v2/Item/props.js +31 -0
- package/lib/Options/v2/Item/styles.js +195 -0
- package/lib/Options/v2/Item/theme.js +78 -0
- package/lib/Options/v2/Separator/index.js +74 -0
- package/lib/Options/v2/Separator/props.js +31 -0
- package/lib/Options/v2/Separator/styles.js +52 -0
- package/lib/Options/v2/Separator/theme.js +51 -0
- package/lib/Options/{index.js → v2/index.js} +4 -5
- package/lib/Options/v2/props.js +31 -0
- package/lib/Options/v2/styles.js +63 -0
- package/lib/Options/v2/theme.js +53 -0
- package/lib/{index.js → exports/a.js} +7 -7
- package/lib/exports/b.js +47 -0
- package/package.json +40 -18
- package/src/Options/{Item → v1/Item}/index.tsx +1 -1
- package/src/Options/{Separator → v1/Separator}/index.tsx +1 -1
- package/src/Options/{index.tsx → v1/index.tsx} +2 -2
- package/src/Options/v2/Item/index.tsx +180 -0
- package/src/Options/v2/Item/props.ts +136 -0
- package/src/Options/v2/Item/styles.ts +205 -0
- package/src/Options/v2/Item/theme.ts +79 -0
- package/src/Options/v2/README.md +409 -0
- package/src/Options/v2/Separator/index.tsx +79 -0
- package/src/Options/v2/Separator/props.ts +52 -0
- package/src/Options/v2/Separator/styles.ts +52 -0
- package/src/Options/v2/Separator/theme.ts +48 -0
- package/src/Options/v2/index.tsx +188 -0
- package/src/Options/v2/props.ts +76 -0
- package/src/Options/v2/styles.ts +63 -0
- package/src/Options/v2/theme.ts +52 -0
- package/src/{index.ts → exports/a.ts} +9 -9
- package/src/exports/b.ts +38 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/Options/v1/Item/index.d.ts.map +1 -0
- package/types/Options/v1/Item/props.d.ts.map +1 -0
- package/types/Options/v1/Item/styles.d.ts.map +1 -0
- package/types/Options/v1/Item/theme.d.ts.map +1 -0
- package/types/Options/v1/Separator/index.d.ts.map +1 -0
- package/types/Options/v1/Separator/props.d.ts.map +1 -0
- package/types/Options/v1/Separator/styles.d.ts.map +1 -0
- package/types/Options/v1/Separator/theme.d.ts.map +1 -0
- package/types/Options/v1/index.d.ts.map +1 -0
- package/types/Options/v1/props.d.ts.map +1 -0
- package/types/Options/v1/styles.d.ts.map +1 -0
- package/types/Options/v1/theme.d.ts.map +1 -0
- package/types/Options/v2/Item/index.d.ts +31 -0
- package/types/Options/v2/Item/index.d.ts.map +1 -0
- package/types/Options/v2/Item/props.d.ts +70 -0
- package/types/Options/v2/Item/props.d.ts.map +1 -0
- package/types/Options/v2/Item/styles.d.ts +15 -0
- package/types/Options/v2/Item/styles.d.ts.map +1 -0
- package/types/Options/v2/Item/theme.d.ts +11 -0
- package/types/Options/v2/Item/theme.d.ts.map +1 -0
- package/types/Options/v2/Separator/index.d.ts +22 -0
- package/types/Options/v2/Separator/index.d.ts.map +1 -0
- package/types/Options/v2/Separator/props.d.ts +17 -0
- package/types/Options/v2/Separator/props.d.ts.map +1 -0
- package/types/Options/v2/Separator/styles.d.ts +15 -0
- package/types/Options/v2/Separator/styles.d.ts.map +1 -0
- package/types/Options/v2/Separator/theme.d.ts +11 -0
- package/types/Options/v2/Separator/theme.d.ts.map +1 -0
- package/types/Options/v2/index.d.ts +43 -0
- package/types/Options/v2/index.d.ts.map +1 -0
- package/types/Options/v2/props.d.ts +32 -0
- package/types/Options/v2/props.d.ts.map +1 -0
- package/types/Options/v2/styles.d.ts +15 -0
- package/types/Options/v2/styles.d.ts.map +1 -0
- package/types/Options/v2/theme.d.ts +11 -0
- package/types/Options/v2/theme.d.ts.map +1 -0
- package/types/exports/a.d.ts +10 -0
- package/types/exports/a.d.ts.map +1 -0
- package/types/exports/b.d.ts +10 -0
- package/types/exports/b.d.ts.map +1 -0
- package/types/Options/Item/index.d.ts.map +0 -1
- package/types/Options/Item/props.d.ts.map +0 -1
- package/types/Options/Item/styles.d.ts.map +0 -1
- package/types/Options/Item/theme.d.ts.map +0 -1
- package/types/Options/Separator/index.d.ts.map +0 -1
- package/types/Options/Separator/props.d.ts.map +0 -1
- package/types/Options/Separator/styles.d.ts.map +0 -1
- package/types/Options/Separator/theme.d.ts.map +0 -1
- package/types/Options/index.d.ts.map +0 -1
- package/types/Options/props.d.ts.map +0 -1
- package/types/Options/styles.d.ts.map +0 -1
- package/types/Options/theme.d.ts.map +0 -1
- package/types/index.d.ts +0 -10
- package/types/index.d.ts.map +0 -1
- /package/es/Options/{Item → v1/Item}/props.js +0 -0
- /package/es/Options/{Item → v1/Item}/styles.js +0 -0
- /package/es/Options/{Item → v1/Item}/theme.js +0 -0
- /package/es/Options/{Separator → v1/Separator}/props.js +0 -0
- /package/es/Options/{Separator → v1/Separator}/styles.js +0 -0
- /package/es/Options/{Separator → v1/Separator}/theme.js +0 -0
- /package/es/Options/{props.js → v1/props.js} +0 -0
- /package/es/Options/{styles.js → v1/styles.js} +0 -0
- /package/es/Options/{theme.js → v1/theme.js} +0 -0
- /package/lib/Options/{Item → v1/Item}/props.js +0 -0
- /package/lib/Options/{Item → v1/Item}/styles.js +0 -0
- /package/lib/Options/{Item → v1/Item}/theme.js +0 -0
- /package/lib/Options/{Separator → v1/Separator}/props.js +0 -0
- /package/lib/Options/{Separator → v1/Separator}/styles.js +0 -0
- /package/lib/Options/{Separator → v1/Separator}/theme.js +0 -0
- /package/lib/Options/{props.js → v1/props.js} +0 -0
- /package/lib/Options/{styles.js → v1/styles.js} +0 -0
- /package/lib/Options/{theme.js → v1/theme.js} +0 -0
- /package/src/Options/{Item → v1/Item}/props.ts +0 -0
- /package/src/Options/{Item → v1/Item}/styles.ts +0 -0
- /package/src/Options/{Item → v1/Item}/theme.ts +0 -0
- /package/src/Options/{README.md → v1/README.md} +0 -0
- /package/src/Options/{Separator → v1/Separator}/props.ts +0 -0
- /package/src/Options/{Separator → v1/Separator}/styles.ts +0 -0
- /package/src/Options/{Separator → v1/Separator}/theme.ts +0 -0
- /package/src/Options/{props.ts → v1/props.ts} +0 -0
- /package/src/Options/{styles.ts → v1/styles.ts} +0 -0
- /package/src/Options/{theme.ts → v1/theme.ts} +0 -0
- /package/types/Options/{Item → v1/Item}/index.d.ts +0 -0
- /package/types/Options/{Item → v1/Item}/props.d.ts +0 -0
- /package/types/Options/{Item → v1/Item}/styles.d.ts +0 -0
- /package/types/Options/{Item → v1/Item}/theme.d.ts +0 -0
- /package/types/Options/{Separator → v1/Separator}/index.d.ts +0 -0
- /package/types/Options/{Separator → v1/Separator}/props.d.ts +0 -0
- /package/types/Options/{Separator → v1/Separator}/styles.d.ts +0 -0
- /package/types/Options/{Separator → v1/Separator}/theme.d.ts +0 -0
- /package/types/Options/{index.d.ts → v1/index.d.ts} +0 -0
- /package/types/Options/{props.d.ts → v1/props.d.ts} +0 -0
- /package/types/Options/{styles.d.ts → v1/styles.d.ts} +0 -0
- /package/types/Options/{theme.d.ts → v1/theme.d.ts} +0 -0
|
@@ -0,0 +1,205 @@
|
|
|
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 { matchComponentTypes } from '@instructure/ui-react-utils'
|
|
26
|
+
|
|
27
|
+
import type { NewComponentTypes } from '@instructure/ui-themes'
|
|
28
|
+
import type { OptionsItemProps, OptionsItemStyle } from './props'
|
|
29
|
+
import { ReactNode } from 'react'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* ---
|
|
33
|
+
* private: true
|
|
34
|
+
* ---
|
|
35
|
+
* Generates the style object from the theme and provided additional information
|
|
36
|
+
* @param {Object} componentTheme The theme variable object.
|
|
37
|
+
* @param {Object} props the props of the component, the style is applied to
|
|
38
|
+
* @param {Object} state the state of the component, the style is applied to
|
|
39
|
+
* @return {Object} The final style object, which will be used in the component
|
|
40
|
+
*/
|
|
41
|
+
const generateStyle = (
|
|
42
|
+
componentTheme: NewComponentTypes['OptionsItem'],
|
|
43
|
+
props: OptionsItemProps
|
|
44
|
+
): OptionsItemStyle => {
|
|
45
|
+
const {
|
|
46
|
+
variant,
|
|
47
|
+
children,
|
|
48
|
+
renderBeforeLabel: hasContentBeforeLabel,
|
|
49
|
+
renderAfterLabel: hasContentAfterLabel,
|
|
50
|
+
beforeLabelContentVAlign,
|
|
51
|
+
afterLabelContentVAlign
|
|
52
|
+
} = props
|
|
53
|
+
// TODO if children are () => ReactNode this wont match anything
|
|
54
|
+
const containsList = matchComponentTypes(children as ReactNode, ['Options'])
|
|
55
|
+
|
|
56
|
+
// used for label and description too
|
|
57
|
+
const variantVariants = {
|
|
58
|
+
highlighted: {
|
|
59
|
+
background: componentTheme.highlightedBackground,
|
|
60
|
+
color: componentTheme.highlightedLabelColor
|
|
61
|
+
},
|
|
62
|
+
selected: {
|
|
63
|
+
background: componentTheme.selectedBackground,
|
|
64
|
+
color: componentTheme.selectedLabelColor
|
|
65
|
+
},
|
|
66
|
+
disabled: { cursor: 'not-allowed', opacity: 0.68 },
|
|
67
|
+
'highlighted-disabled': {
|
|
68
|
+
background: componentTheme.highlightedBackground,
|
|
69
|
+
color: componentTheme.highlightedLabelColor,
|
|
70
|
+
cursor: 'not-allowed'
|
|
71
|
+
},
|
|
72
|
+
'selected-highlighted': {
|
|
73
|
+
background: componentTheme.selectedHighlightedBackground,
|
|
74
|
+
color: componentTheme.highlightedLabelColor
|
|
75
|
+
},
|
|
76
|
+
default: {
|
|
77
|
+
transition: 'background 200ms'
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const getContentVAlign = (type: 'before' | 'after') => {
|
|
82
|
+
const vAlign =
|
|
83
|
+
type === 'before' ? beforeLabelContentVAlign : afterLabelContentVAlign
|
|
84
|
+
|
|
85
|
+
const vOffset =
|
|
86
|
+
type === 'before'
|
|
87
|
+
? componentTheme.beforeLabelContentVOffset
|
|
88
|
+
: componentTheme.afterLabelContentVOffset
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
start: {
|
|
92
|
+
alignItems: 'flex-start',
|
|
93
|
+
paddingBlockStart: vOffset
|
|
94
|
+
},
|
|
95
|
+
center: {
|
|
96
|
+
alignItems: 'center',
|
|
97
|
+
paddingBlockStart: vOffset,
|
|
98
|
+
paddingBlockEnd: vOffset
|
|
99
|
+
},
|
|
100
|
+
end: {
|
|
101
|
+
alignItems: 'flex-end',
|
|
102
|
+
paddingBlockEnd: vOffset
|
|
103
|
+
}
|
|
104
|
+
}[vAlign!]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const linkStyles = { textDecoration: 'none', color: 'currentColor' }
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
item: {
|
|
111
|
+
label: 'optionItem',
|
|
112
|
+
background: componentTheme.background,
|
|
113
|
+
color: componentTheme.color,
|
|
114
|
+
cursor: 'pointer',
|
|
115
|
+
display: 'block',
|
|
116
|
+
fontSize: componentTheme.fontSize,
|
|
117
|
+
fontFamily: componentTheme.fontFamily,
|
|
118
|
+
fontWeight: props.isSelected
|
|
119
|
+
? componentTheme.fontWeightSelected
|
|
120
|
+
: componentTheme.fontWeight,
|
|
121
|
+
lineHeight: componentTheme.lineHeight,
|
|
122
|
+
outline: 'none',
|
|
123
|
+
position: 'relative',
|
|
124
|
+
userSelect: 'none',
|
|
125
|
+
...variantVariants[variant!],
|
|
126
|
+
...(containsList && { cursor: 'default' }),
|
|
127
|
+
|
|
128
|
+
// for nested items
|
|
129
|
+
'[class*=-optionItem] &': {
|
|
130
|
+
// except if it has icon before
|
|
131
|
+
...(!hasContentBeforeLabel && {
|
|
132
|
+
'[class$=-optionItem__container]': {
|
|
133
|
+
paddingInlineStart: componentTheme.nestedPadding
|
|
134
|
+
},
|
|
135
|
+
'[class$=-optionItem__content--before]': {
|
|
136
|
+
insetInlineStart: componentTheme.nestedPadding
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
container: {
|
|
142
|
+
label: 'optionItem__container',
|
|
143
|
+
display: 'block',
|
|
144
|
+
outline: 'none',
|
|
145
|
+
padding: `${componentTheme.paddingVertical} ${componentTheme.paddingHorizontal}`,
|
|
146
|
+
...(containsList && { padding: '0' }),
|
|
147
|
+
...(hasContentBeforeLabel && {
|
|
148
|
+
paddingInlineEnd: componentTheme.iconPadding,
|
|
149
|
+
paddingInlineStart: `calc(${componentTheme.iconPadding} * 2 + 1em)`
|
|
150
|
+
}),
|
|
151
|
+
...(hasContentAfterLabel && {
|
|
152
|
+
paddingInlineEnd: `calc(${componentTheme.iconPadding} * 2 + 1em)`,
|
|
153
|
+
paddingInlineStart: componentTheme.iconPadding
|
|
154
|
+
}),
|
|
155
|
+
...(hasContentBeforeLabel &&
|
|
156
|
+
hasContentAfterLabel && {
|
|
157
|
+
paddingInlineEnd: `calc(${componentTheme.iconPadding} * 2 + 1em)`,
|
|
158
|
+
paddingInlineStart: `calc(${componentTheme.iconPadding} * 2 + 1em)`
|
|
159
|
+
}),
|
|
160
|
+
|
|
161
|
+
// NOTE: needs separate groups for `:is()` and `:-webkit-any()` because of css selector group validation (see https://www.w3.org/TR/selectors-3/#grouping)
|
|
162
|
+
'&:is(a)': {
|
|
163
|
+
'&, &:link, &:visited, &:active, &:hover, &:focus': linkStyles
|
|
164
|
+
},
|
|
165
|
+
'&:-webkit-any(a)': {
|
|
166
|
+
'&, &:link, &:visited, &:active, &:hover, &:focus': linkStyles
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
content: {
|
|
170
|
+
label: 'optionItem__content',
|
|
171
|
+
display: 'flex',
|
|
172
|
+
height: '100%',
|
|
173
|
+
boxSizing: 'border-box',
|
|
174
|
+
pointerEvents: 'none',
|
|
175
|
+
position: 'absolute',
|
|
176
|
+
top: '0'
|
|
177
|
+
},
|
|
178
|
+
contentBefore: {
|
|
179
|
+
label: 'optionItem__content--before',
|
|
180
|
+
insetInlineEnd: 'auto',
|
|
181
|
+
insetInlineStart: componentTheme.iconPadding,
|
|
182
|
+
...getContentVAlign('before')
|
|
183
|
+
},
|
|
184
|
+
contentAfter: {
|
|
185
|
+
label: 'optionItem__content--after',
|
|
186
|
+
insetInlineEnd: componentTheme.iconPadding,
|
|
187
|
+
insetInlineStart: 'auto',
|
|
188
|
+
...getContentVAlign('after')
|
|
189
|
+
},
|
|
190
|
+
description: {
|
|
191
|
+
label: 'optionItem__description',
|
|
192
|
+
display: 'block',
|
|
193
|
+
paddingBlockStart: componentTheme.descriptionPaddingStart,
|
|
194
|
+
fontWeight: componentTheme.descriptionFontWeight,
|
|
195
|
+
fontSize: componentTheme.descriptionFontSize,
|
|
196
|
+
lineHeight: componentTheme.descriptionLineHeight,
|
|
197
|
+
color: componentTheme.descriptionColor,
|
|
198
|
+
|
|
199
|
+
...variantVariants[variant!],
|
|
200
|
+
background: 'none' // needed to clear variant background
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export default generateStyle
|
|
@@ -0,0 +1,79 @@
|
|
|
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 type { Theme, ThemeSpecificStyle } from '@instructure/ui-themes'
|
|
26
|
+
import { OptionsItemTheme } from '@instructure/shared-types'
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Generates the theme object for the component from the theme and provided additional information
|
|
30
|
+
* @param {Object} theme The actual theme object.
|
|
31
|
+
* @return {Object} The final theme object with the overrides and component variables
|
|
32
|
+
*/
|
|
33
|
+
const generateComponentTheme = (theme: Theme): OptionsItemTheme => {
|
|
34
|
+
const { colors, typography, spacing, key: themeName } = theme
|
|
35
|
+
|
|
36
|
+
const themeSpecificStyle: ThemeSpecificStyle<OptionsItemTheme> = {
|
|
37
|
+
canvas: {
|
|
38
|
+
color: theme['ic-brand-font-color-dark'],
|
|
39
|
+
highlightedBackground: theme['ic-brand-primary']
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const componentVariables: OptionsItemTheme = {
|
|
44
|
+
fontSize: typography?.fontSizeMedium,
|
|
45
|
+
fontFamily: typography?.fontFamily,
|
|
46
|
+
fontWeight: typography?.fontWeightNormal,
|
|
47
|
+
lineHeight: typography?.lineHeightCondensed,
|
|
48
|
+
fontWeightSelected: typography?.fontWeightNormal,
|
|
49
|
+
|
|
50
|
+
color: colors?.contrasts?.grey125125,
|
|
51
|
+
background: colors?.contrasts?.white1010,
|
|
52
|
+
highlightedLabelColor: colors?.contrasts?.white1010,
|
|
53
|
+
highlightedBackground: colors?.contrasts?.blue4570,
|
|
54
|
+
selectedLabelColor: colors?.contrasts?.white1010,
|
|
55
|
+
selectedBackground: colors?.contrasts?.grey4570,
|
|
56
|
+
selectedHighlightedBackground: colors?.contrasts?.blue5782,
|
|
57
|
+
|
|
58
|
+
padding: `${spacing?.xSmall} ${spacing?.small}`,
|
|
59
|
+
iconPadding: spacing?.small,
|
|
60
|
+
nestedPadding: spacing?.small,
|
|
61
|
+
|
|
62
|
+
beforeLabelContentVOffset: '0.625rem',
|
|
63
|
+
afterLabelContentVOffset: '0.625rem',
|
|
64
|
+
|
|
65
|
+
descriptionFontSize: typography.fontSizeSmall,
|
|
66
|
+
descriptionFontWeight: typography.fontWeightNormal,
|
|
67
|
+
descriptionLineHeight: typography.lineHeight,
|
|
68
|
+
descriptionPaddingStart: '0.25em',
|
|
69
|
+
descriptionColor: colors?.contrasts?.grey5782
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
...componentVariables,
|
|
74
|
+
...themeSpecificStyle[themeName]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { generateComponentTheme as optionsItemThemeGenerator }
|
|
79
|
+
export default generateComponentTheme
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
---
|
|
2
|
+
describes: Options
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
`Options` is a view-only component for creating option lists and menus, like those used in [Select](Select) and [Menu](Menu). It should only be used if an existing component doesn't offer the level of customization needed.
|
|
6
|
+
|
|
7
|
+
The `variant` prop of `Option.Item` provides several visual treatments. Use the `highlighted` variant to indicate that an option is being hovered, focused, or otherwise interacted with and the `selected` variant to indicate the selected option.
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
---
|
|
11
|
+
type: example
|
|
12
|
+
---
|
|
13
|
+
<View display="block" width="300px">
|
|
14
|
+
<Options>
|
|
15
|
+
<Options.Item onClick={() => console.log('clicked!')}>
|
|
16
|
+
Default option
|
|
17
|
+
</Options.Item>
|
|
18
|
+
<Options.Item variant="highlighted">
|
|
19
|
+
Highlighted option
|
|
20
|
+
</Options.Item>
|
|
21
|
+
<Options.Item variant="selected">
|
|
22
|
+
Selected option
|
|
23
|
+
</Options.Item>
|
|
24
|
+
<Options.Item variant="disabled">
|
|
25
|
+
Disabled option
|
|
26
|
+
</Options.Item>
|
|
27
|
+
<Options.Item variant="highlighted-disabled">
|
|
28
|
+
Highlighted disabled option
|
|
29
|
+
</Options.Item>
|
|
30
|
+
</Options>
|
|
31
|
+
</View>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`Options` components can be nested to create sub menus. Icons may be added to any `Options.Item` before or after its text content using `renderBeforeLabel` and `renderAfterLabel` respectively.
|
|
35
|
+
|
|
36
|
+
> `Options` and `Options.Item` receive default roles of `list` and `listitem` respectively, but the most applicable roles should be used. These will commonly be `listbox`/`option` or `menu`/`menuitem`.
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
---
|
|
40
|
+
type: example
|
|
41
|
+
---
|
|
42
|
+
<View display="block" width="300px">
|
|
43
|
+
<Options role="menu" as="ul">
|
|
44
|
+
<Options.Item role="menuitem">
|
|
45
|
+
Option one
|
|
46
|
+
</Options.Item>
|
|
47
|
+
<Options.Item role="menuitem" variant="highlighted">
|
|
48
|
+
Option two
|
|
49
|
+
</Options.Item>
|
|
50
|
+
<Options.Item role="menuitem" renderAfterLabel={IconArrowOpenEndSolid}>
|
|
51
|
+
Flyout menu option
|
|
52
|
+
</Options.Item>
|
|
53
|
+
<Options.Separator as="li" />
|
|
54
|
+
<Options role="menu" as="ul" renderLabel={'Sub menu'}>
|
|
55
|
+
<Options.Item role="menuitem">
|
|
56
|
+
Sub option one
|
|
57
|
+
</Options.Item>
|
|
58
|
+
<Options.Item role="menuitem">
|
|
59
|
+
Sub option two
|
|
60
|
+
</Options.Item>
|
|
61
|
+
</Options>
|
|
62
|
+
<Options.Separator />
|
|
63
|
+
<Options role="menu" as="ul" renderLabel={'Radio group'}>
|
|
64
|
+
<Options.Item
|
|
65
|
+
role="menuitemradio"
|
|
66
|
+
aria-checked="true"
|
|
67
|
+
renderBeforeLabel={IconCheckSolid}
|
|
68
|
+
>
|
|
69
|
+
Radio option one
|
|
70
|
+
</Options.Item>
|
|
71
|
+
<Options.Item
|
|
72
|
+
role="menuitemradio"
|
|
73
|
+
aria-checked="false"
|
|
74
|
+
renderBeforeLabel={
|
|
75
|
+
<IconCheckLine style={{opacity: 0}} />
|
|
76
|
+
}
|
|
77
|
+
>
|
|
78
|
+
Radio option two
|
|
79
|
+
</Options.Item>
|
|
80
|
+
</Options>
|
|
81
|
+
<Options.Separator />
|
|
82
|
+
<Options.Item role="menuitem">
|
|
83
|
+
Option three
|
|
84
|
+
</Options.Item>
|
|
85
|
+
</Options>
|
|
86
|
+
</View>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`Options` does not manage any state or react to any user interaction. The consuming component or app should listen for events on items and update the `variant` props accordingly via its own state.
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
---
|
|
93
|
+
type: example
|
|
94
|
+
---
|
|
95
|
+
const Example = ({ options }) => {
|
|
96
|
+
const [highlighted, setHighlighted] = useState(-1)
|
|
97
|
+
const [selected, setSelected] = useState(-1)
|
|
98
|
+
|
|
99
|
+
const handleKeyDown = (event) => {
|
|
100
|
+
let index = highlighted
|
|
101
|
+
|
|
102
|
+
if (event.keyCode === 40 && highlighted < options.length - 1) {
|
|
103
|
+
// down arrow
|
|
104
|
+
event.preventDefault()
|
|
105
|
+
index = highlighted + 1
|
|
106
|
+
} else if (event.keyCode === 38 && highlighted > 0) {
|
|
107
|
+
// up arrow
|
|
108
|
+
event.preventDefault()
|
|
109
|
+
index = highlighted - 1
|
|
110
|
+
} else if (event.keyCode === 13 && highlighted > -1) {
|
|
111
|
+
// enter
|
|
112
|
+
setSelected(index)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
setHighlighted(index)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const handleFocus = (index) => {
|
|
119
|
+
setHighlighted(index)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const handleMouseOver = (index) => {
|
|
123
|
+
setHighlighted(index)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const handleClick = (index) => {
|
|
127
|
+
setSelected(index)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<View display="block" width="300px" shadow="above">
|
|
132
|
+
<Options
|
|
133
|
+
onKeyDown={handleKeyDown}
|
|
134
|
+
onMouseOut={() => setHighlighted(-1)}
|
|
135
|
+
tabIndex="0"
|
|
136
|
+
>
|
|
137
|
+
{options.map((option, index) => {
|
|
138
|
+
let variant = 'default'
|
|
139
|
+
if (highlighted === index) {
|
|
140
|
+
variant = 'highlighted'
|
|
141
|
+
} else if (selected === index) {
|
|
142
|
+
variant = 'selected'
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<Options.Item
|
|
147
|
+
key={option}
|
|
148
|
+
variant={variant}
|
|
149
|
+
tabIndex="-1"
|
|
150
|
+
onMouseOver={(e) => handleMouseOver(index)}
|
|
151
|
+
onFocus={() => handleFocus(index)}
|
|
152
|
+
onClick={() => handleClick(index)}
|
|
153
|
+
>
|
|
154
|
+
{option}
|
|
155
|
+
</Options.Item>
|
|
156
|
+
)
|
|
157
|
+
})}
|
|
158
|
+
</Options>
|
|
159
|
+
</View>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
render(
|
|
164
|
+
<Example
|
|
165
|
+
options={[
|
|
166
|
+
'Option one',
|
|
167
|
+
'Option two',
|
|
168
|
+
'Option three',
|
|
169
|
+
'Option four',
|
|
170
|
+
'Option five'
|
|
171
|
+
]}
|
|
172
|
+
/>
|
|
173
|
+
)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
You can recolor the text and the background of the items for their `default`, `highlighted` and `selected` variants.
|
|
177
|
+
|
|
178
|
+
By default, the icons in the `Option.Item` have the same color as the text. If you want to set the color of the icon separately, pass a function to the `renderBeforeLabel` or `renderAfterLabel` prop. This function will have a `props` parameter, so you can access the properties of that `Option.Item` (e.g. the current `variant`). The available props are: `[ variant, as, role, children ]`.
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
---
|
|
182
|
+
type: example
|
|
183
|
+
---
|
|
184
|
+
const Example = ({ options }) => {
|
|
185
|
+
const [highlighted, setHighlighted] = useState(-1)
|
|
186
|
+
const [selected, setSelected] = useState(-1)
|
|
187
|
+
|
|
188
|
+
const handleKeyDown = (event) => {
|
|
189
|
+
let index = highlighted
|
|
190
|
+
|
|
191
|
+
if (event.keyCode === 40 && highlighted < options.length - 1) {
|
|
192
|
+
// down arrow
|
|
193
|
+
event.preventDefault()
|
|
194
|
+
index = highlighted + 1
|
|
195
|
+
} else if (event.keyCode === 38 && highlighted > 0) {
|
|
196
|
+
// up arrow
|
|
197
|
+
event.preventDefault()
|
|
198
|
+
index = highlighted - 1
|
|
199
|
+
} else if (event.keyCode === 13 && highlighted > -1) {
|
|
200
|
+
// enter
|
|
201
|
+
setSelected(index)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
setHighlighted(index)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const handleFocus = (event, index) => {
|
|
208
|
+
setHighlighted(index)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const handleMouseOver = (event, index) => {
|
|
212
|
+
setHighlighted(index)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const handleClick = (event, index) => {
|
|
216
|
+
setSelected(index)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<View display="block" width="300px" shadow="above">
|
|
221
|
+
<Options
|
|
222
|
+
onKeyDown={handleKeyDown}
|
|
223
|
+
onMouseOut={() => setHighlighted(-1)}
|
|
224
|
+
tabIndex="0"
|
|
225
|
+
>
|
|
226
|
+
{options.map((option, index) => {
|
|
227
|
+
let variant = 'default'
|
|
228
|
+
if (highlighted === index) {
|
|
229
|
+
variant = 'highlighted'
|
|
230
|
+
} else if (selected === index) {
|
|
231
|
+
variant = 'selected'
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return (
|
|
235
|
+
<Options.Item
|
|
236
|
+
key={option.label}
|
|
237
|
+
variant={variant}
|
|
238
|
+
tabIndex="-1"
|
|
239
|
+
onMouseOver={(e) => handleMouseOver(e, index)}
|
|
240
|
+
onFocus={(e) => handleFocus(e, index)}
|
|
241
|
+
onClick={(e) => handleClick(e, index)}
|
|
242
|
+
{...option.extraProps}
|
|
243
|
+
>
|
|
244
|
+
{option.label}
|
|
245
|
+
</Options.Item>
|
|
246
|
+
)
|
|
247
|
+
})}
|
|
248
|
+
</Options>
|
|
249
|
+
</View>
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
render(
|
|
254
|
+
<Example
|
|
255
|
+
options={[
|
|
256
|
+
{
|
|
257
|
+
label: 'Default item',
|
|
258
|
+
extraProps: {
|
|
259
|
+
renderBeforeLabel: IconCheckSolid
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
label: 'Text is green',
|
|
264
|
+
extraProps: {
|
|
265
|
+
renderBeforeLabel: IconCheckSolid,
|
|
266
|
+
themeOverride: { color: '#0B874B' }
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
label: 'Highlighted text is black',
|
|
271
|
+
extraProps: {
|
|
272
|
+
renderBeforeLabel: IconCheckSolid,
|
|
273
|
+
themeOverride: { highlightedLabelColor: '#2D3B45' }
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
label: 'Highlighted background is purple',
|
|
278
|
+
extraProps: {
|
|
279
|
+
renderBeforeLabel: IconCheckSolid,
|
|
280
|
+
themeOverride: { highlightedBackground: '#BF32A4' }
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
label: 'Only the icon is red',
|
|
285
|
+
extraProps: {
|
|
286
|
+
renderBeforeLabel: (props) => {
|
|
287
|
+
return (
|
|
288
|
+
<IconCheckSolid
|
|
289
|
+
{...(props.variant === 'default' && { color: 'warning' })}
|
|
290
|
+
/>
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
]}
|
|
296
|
+
/>
|
|
297
|
+
)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Additional/secondary text can be added via the `description` prop, and the ARIA role of it can be set with the `descriptionRole` prop.
|
|
301
|
+
|
|
302
|
+
For longer, multi-line options the problem of the vertical alignment comes up. The content of the `renderBeforeLabel` and `renderAfterLabel` props are vertically centered by default. This can be changed with the `beforeLabelContentVAlign` and `afterLabelContentVAlign` props.
|
|
303
|
+
|
|
304
|
+
```js
|
|
305
|
+
---
|
|
306
|
+
type: example
|
|
307
|
+
---
|
|
308
|
+
const Example = () => {
|
|
309
|
+
const [highlighted, setHighlighted] = useState(-1)
|
|
310
|
+
|
|
311
|
+
const handleMouseOver = (index) => {
|
|
312
|
+
setHighlighted(index)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return (
|
|
316
|
+
<View display="block" width="300px">
|
|
317
|
+
<Options onMouseOut={() => setHighlighted(-1)}>
|
|
318
|
+
<Options.Item
|
|
319
|
+
onMouseOver={() => handleMouseOver(1)}
|
|
320
|
+
variant={highlighted === 1 ? 'highlighted' : 'default'}
|
|
321
|
+
description="Curabitur fringilla, urna ut efficitur molestie, nibh lacus tincidunt elit, ut tempor ipsum nunc sit amet massa."
|
|
322
|
+
renderBeforeLabel={IconCheckSolid}
|
|
323
|
+
renderAfterLabel={IconArrowOpenEndSolid}
|
|
324
|
+
beforeLabelContentVAlign="start"
|
|
325
|
+
afterLabelContentVAlign="start"
|
|
326
|
+
>
|
|
327
|
+
Option one
|
|
328
|
+
</Options.Item>
|
|
329
|
+
<Options.Item
|
|
330
|
+
onMouseOver={() => handleMouseOver(2)}
|
|
331
|
+
variant={highlighted === 2 ? 'highlighted' : 'default'}
|
|
332
|
+
description="Curabitur fringilla, urna ut efficitur molestie, nibh lacus tincidunt elit, ut tempor ipsum nunc sit amet massa."
|
|
333
|
+
renderBeforeLabel={IconCheckSolid}
|
|
334
|
+
renderAfterLabel={IconArrowOpenEndSolid}
|
|
335
|
+
beforeLabelContentVAlign="center"
|
|
336
|
+
afterLabelContentVAlign="center"
|
|
337
|
+
>
|
|
338
|
+
Option two
|
|
339
|
+
</Options.Item>
|
|
340
|
+
<Options.Item
|
|
341
|
+
onMouseOver={() => handleMouseOver(3)}
|
|
342
|
+
variant={highlighted === 3 ? 'highlighted' : 'default'}
|
|
343
|
+
description="Curabitur fringilla, urna ut efficitur molestie, nibh lacus tincidunt elit, ut tempor ipsum nunc sit amet massa."
|
|
344
|
+
renderBeforeLabel={IconCheckSolid}
|
|
345
|
+
renderAfterLabel={IconArrowOpenEndSolid}
|
|
346
|
+
beforeLabelContentVAlign="end"
|
|
347
|
+
afterLabelContentVAlign="end"
|
|
348
|
+
>
|
|
349
|
+
Option three
|
|
350
|
+
</Options.Item>
|
|
351
|
+
</Options>
|
|
352
|
+
</View>
|
|
353
|
+
)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
render(<Example />)
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Providing a `href` prop will render the option as `<a>` link element.
|
|
360
|
+
|
|
361
|
+
**WARNING!** Since `Options` is a view-only component, you have to make sure it is accessible, and if set the variant to disabled, disable the links as well!
|
|
362
|
+
|
|
363
|
+
```js
|
|
364
|
+
---
|
|
365
|
+
type: example
|
|
366
|
+
---
|
|
367
|
+
const Example = () => {
|
|
368
|
+
const [highlighted, setHighlighted] = useState(-1)
|
|
369
|
+
|
|
370
|
+
const handleMouseOver = (index) => {
|
|
371
|
+
setHighlighted(index)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return (
|
|
375
|
+
<View display="block" width="300px">
|
|
376
|
+
<Options onMouseOut={() => setHighlighted(-1)}>
|
|
377
|
+
<Options.Item
|
|
378
|
+
onMouseOver={() => handleMouseOver(1)}
|
|
379
|
+
variant={highlighted === 1 ? 'highlighted' : 'default'}
|
|
380
|
+
href="/"
|
|
381
|
+
>
|
|
382
|
+
Link one
|
|
383
|
+
</Options.Item>
|
|
384
|
+
<Options.Item
|
|
385
|
+
onMouseOver={() => handleMouseOver(2)}
|
|
386
|
+
variant={highlighted === 2 ? 'highlighted' : 'default'}
|
|
387
|
+
href="/"
|
|
388
|
+
>
|
|
389
|
+
Link two
|
|
390
|
+
</Options.Item>
|
|
391
|
+
<Options.Item
|
|
392
|
+
onMouseOver={() => handleMouseOver(3)}
|
|
393
|
+
variant={highlighted === 3 ? 'highlighted' : 'default'}
|
|
394
|
+
variant="disabled"
|
|
395
|
+
aria-disabled="true"
|
|
396
|
+
onClick={(e) => {
|
|
397
|
+
e.preventDefault()
|
|
398
|
+
}}
|
|
399
|
+
href="/"
|
|
400
|
+
>
|
|
401
|
+
Link three
|
|
402
|
+
</Options.Item>
|
|
403
|
+
</Options>
|
|
404
|
+
</View>
|
|
405
|
+
)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
render(<Example />)
|
|
409
|
+
```
|