@primer/components 31.0.2-rc.c7dafefb → 31.2.0-rc.2a086bac

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 (229) hide show
  1. package/.storybook/main.js +7 -9
  2. package/.storybook/preview.js +5 -1
  3. package/CHANGELOG.md +21 -1
  4. package/dist/browser.esm.js +623 -620
  5. package/dist/browser.esm.js.map +1 -1
  6. package/dist/browser.umd.js +164 -161
  7. package/dist/browser.umd.js.map +1 -1
  8. package/docs/content/ActionList2.mdx +354 -0
  9. package/docs/content/FilterList.md +2 -2
  10. package/docs/content/TextInputWithTokens.mdx +114 -0
  11. package/docs/content/theming.md +23 -0
  12. package/docs/src/@primer/gatsby-theme-doctocat/components/hero.js +1 -3
  13. package/docs/src/@primer/gatsby-theme-doctocat/components/live-preview-wrapper.js +1 -1
  14. package/docs/src/@primer/gatsby-theme-doctocat/live-code-scope.js +17 -0
  15. package/lib/ActionList/Header.js +1 -1
  16. package/lib/ActionList2/Description.d.ts +12 -0
  17. package/lib/ActionList2/Description.js +53 -0
  18. package/lib/ActionList2/Divider.d.ts +5 -0
  19. package/lib/ActionList2/Divider.js +35 -0
  20. package/lib/ActionList2/Group.d.ts +11 -0
  21. package/lib/ActionList2/Group.js +57 -0
  22. package/lib/ActionList2/Header.d.ts +26 -0
  23. package/lib/ActionList2/Header.js +55 -0
  24. package/lib/ActionList2/Item.d.ts +63 -0
  25. package/lib/ActionList2/Item.js +234 -0
  26. package/lib/ActionList2/LinkItem.d.ts +17 -0
  27. package/lib/ActionList2/LinkItem.js +57 -0
  28. package/lib/ActionList2/List.d.ts +26 -0
  29. package/lib/ActionList2/List.js +59 -0
  30. package/lib/ActionList2/Selection.d.ts +5 -0
  31. package/lib/ActionList2/Selection.js +70 -0
  32. package/lib/ActionList2/Visuals.d.ts +9 -0
  33. package/lib/ActionList2/Visuals.js +90 -0
  34. package/lib/ActionList2/index.d.ts +36 -0
  35. package/lib/ActionList2/index.js +47 -0
  36. package/lib/Autocomplete/Autocomplete.d.ts +4 -4
  37. package/lib/Autocomplete/AutocompleteInput.d.ts +4 -4
  38. package/lib/Button/Button.d.ts +5 -5
  39. package/lib/Button/ButtonBase.d.ts +1 -1
  40. package/lib/Button/ButtonClose.d.ts +3 -3
  41. package/lib/Button/ButtonDanger.d.ts +5 -5
  42. package/lib/Button/ButtonInvisible.d.ts +5 -5
  43. package/lib/Button/ButtonOutline.d.ts +5 -5
  44. package/lib/Button/ButtonPrimary.d.ts +5 -5
  45. package/lib/CircleBadge.d.ts +2 -2
  46. package/lib/CircleOcticon.d.ts +4 -4
  47. package/lib/Dialog.d.ts +4 -4
  48. package/lib/Dropdown.d.ts +16 -16
  49. package/lib/DropdownMenu/DropdownButton.d.ts +6 -6
  50. package/lib/FilterList.d.ts +3 -3
  51. package/lib/Flash.d.ts +1 -1
  52. package/lib/Label.d.ts +1 -1
  53. package/lib/Overlay.js +3 -1
  54. package/lib/Portal/Portal.js +3 -2
  55. package/lib/Position.d.ts +4 -4
  56. package/lib/ProgressBar.d.ts +1 -1
  57. package/lib/SelectMenu/SelectMenu.d.ts +24 -24
  58. package/lib/SelectMenu/SelectMenuItem.d.ts +1 -1
  59. package/lib/TextInputWithTokens.d.ts +8 -4
  60. package/lib/TextInputWithTokens.js +61 -8
  61. package/lib/Timeline.d.ts +4 -4
  62. package/lib/Token/AvatarToken.d.ts +1 -1
  63. package/lib/Token/IssueLabelToken.d.ts +1 -1
  64. package/lib/Token/Token.d.ts +1 -1
  65. package/lib/_TextInputWrapper.d.ts +1 -1
  66. package/lib/_TextInputWrapper.js +2 -2
  67. package/lib/__tests__/ActionList2.test.d.ts +1 -0
  68. package/lib/__tests__/ActionList2.test.js +53 -0
  69. package/lib/__tests__/AnchoredOverlay.test.js +4 -2
  70. package/lib/__tests__/KeyPaths.types.test.d.ts +11 -0
  71. package/lib/__tests__/KeyPaths.types.test.js +10 -0
  72. package/lib/__tests__/TextInputWithTokens.test.js +138 -7
  73. package/lib/__tests__/utils/createSlots.test.d.ts +1 -0
  74. package/lib/__tests__/utils/createSlots.test.js +75 -0
  75. package/lib/hooks/useAnchoredPosition.js +3 -2
  76. package/lib/hooks/useCombinedRefs.d.ts +2 -2
  77. package/lib/hooks/useCombinedRefs.js +4 -6
  78. package/lib/hooks/useResizeObserver.js +2 -2
  79. package/lib/stories/ActionList2.stories.js +875 -0
  80. package/lib/stories/TextInput.stories.js +144 -0
  81. package/lib/stories/TextInputWithTokens.stories.js +18 -1
  82. package/lib/stories/Token.stories.js +19 -2
  83. package/lib/sx.d.ts +10 -2
  84. package/lib/sx.js +8 -0
  85. package/lib/theme-preval.js +81 -2
  86. package/lib/theme.d.ts +78 -0
  87. package/lib/theme.js +3 -1
  88. package/lib/unreleased.d.ts +7 -0
  89. package/lib/unreleased.js +18 -0
  90. package/lib/utils/create-slots.d.ts +17 -0
  91. package/lib/utils/create-slots.js +105 -0
  92. package/lib/utils/testing.d.ts +14 -1
  93. package/lib/utils/types/KeyPaths.d.ts +3 -0
  94. package/lib/utils/types/KeyPaths.js +1 -0
  95. package/lib/utils/use-force-update.d.ts +1 -0
  96. package/lib/utils/use-force-update.js +19 -0
  97. package/lib/utils/useIsomorphicLayoutEffect.d.ts +3 -0
  98. package/lib/utils/useIsomorphicLayoutEffect.js +12 -0
  99. package/lib-esm/ActionList/Header.js +1 -1
  100. package/lib-esm/ActionList2/Description.d.ts +12 -0
  101. package/lib-esm/ActionList2/Description.js +37 -0
  102. package/lib-esm/ActionList2/Divider.d.ts +5 -0
  103. package/lib-esm/ActionList2/Divider.js +23 -0
  104. package/lib-esm/ActionList2/Group.d.ts +11 -0
  105. package/lib-esm/ActionList2/Group.js +40 -0
  106. package/lib-esm/ActionList2/Header.d.ts +26 -0
  107. package/lib-esm/ActionList2/Header.js +44 -0
  108. package/lib-esm/ActionList2/Item.d.ts +63 -0
  109. package/lib-esm/ActionList2/Item.js +201 -0
  110. package/lib-esm/ActionList2/LinkItem.d.ts +17 -0
  111. package/lib-esm/ActionList2/LinkItem.js +43 -0
  112. package/lib-esm/ActionList2/List.d.ts +26 -0
  113. package/lib-esm/ActionList2/List.js +37 -0
  114. package/lib-esm/ActionList2/Selection.d.ts +5 -0
  115. package/lib-esm/ActionList2/Selection.js +52 -0
  116. package/lib-esm/ActionList2/Visuals.d.ts +9 -0
  117. package/lib-esm/ActionList2/Visuals.js +68 -0
  118. package/lib-esm/ActionList2/index.d.ts +36 -0
  119. package/lib-esm/ActionList2/index.js +33 -0
  120. package/lib-esm/Autocomplete/Autocomplete.d.ts +4 -4
  121. package/lib-esm/Autocomplete/AutocompleteInput.d.ts +4 -4
  122. package/lib-esm/Button/Button.d.ts +5 -5
  123. package/lib-esm/Button/ButtonBase.d.ts +1 -1
  124. package/lib-esm/Button/ButtonClose.d.ts +3 -3
  125. package/lib-esm/Button/ButtonDanger.d.ts +5 -5
  126. package/lib-esm/Button/ButtonInvisible.d.ts +5 -5
  127. package/lib-esm/Button/ButtonOutline.d.ts +5 -5
  128. package/lib-esm/Button/ButtonPrimary.d.ts +5 -5
  129. package/lib-esm/CircleBadge.d.ts +2 -2
  130. package/lib-esm/CircleOcticon.d.ts +4 -4
  131. package/lib-esm/Dialog.d.ts +4 -4
  132. package/lib-esm/Dropdown.d.ts +16 -16
  133. package/lib-esm/DropdownMenu/DropdownButton.d.ts +6 -6
  134. package/lib-esm/FilterList.d.ts +3 -3
  135. package/lib-esm/Flash.d.ts +1 -1
  136. package/lib-esm/Label.d.ts +1 -1
  137. package/lib-esm/Overlay.js +2 -1
  138. package/lib-esm/Portal/Portal.js +2 -1
  139. package/lib-esm/Position.d.ts +4 -4
  140. package/lib-esm/ProgressBar.d.ts +1 -1
  141. package/lib-esm/SelectMenu/SelectMenu.d.ts +24 -24
  142. package/lib-esm/SelectMenu/SelectMenuItem.d.ts +1 -1
  143. package/lib-esm/TextInputWithTokens.d.ts +8 -4
  144. package/lib-esm/TextInputWithTokens.js +60 -8
  145. package/lib-esm/Timeline.d.ts +4 -4
  146. package/lib-esm/Token/AvatarToken.d.ts +1 -1
  147. package/lib-esm/Token/IssueLabelToken.d.ts +1 -1
  148. package/lib-esm/Token/Token.d.ts +1 -1
  149. package/lib-esm/_TextInputWrapper.d.ts +1 -1
  150. package/lib-esm/_TextInputWrapper.js +2 -2
  151. package/lib-esm/__tests__/ActionList2.test.d.ts +1 -0
  152. package/lib-esm/__tests__/ActionList2.test.js +41 -0
  153. package/lib-esm/__tests__/AnchoredOverlay.test.js +4 -2
  154. package/lib-esm/__tests__/KeyPaths.types.test.d.ts +11 -0
  155. package/lib-esm/__tests__/KeyPaths.types.test.js +3 -0
  156. package/lib-esm/__tests__/TextInputWithTokens.test.js +132 -8
  157. package/lib-esm/__tests__/utils/createSlots.test.d.ts +1 -0
  158. package/lib-esm/__tests__/utils/createSlots.test.js +67 -0
  159. package/lib-esm/hooks/useAnchoredPosition.js +2 -1
  160. package/lib-esm/hooks/useCombinedRefs.d.ts +2 -2
  161. package/lib-esm/hooks/useCombinedRefs.js +3 -2
  162. package/lib-esm/hooks/useResizeObserver.js +2 -2
  163. package/lib-esm/stories/ActionList2.stories.js +764 -0
  164. package/lib-esm/stories/TextInput.stories.js +117 -0
  165. package/lib-esm/stories/TextInputWithTokens.stories.js +14 -0
  166. package/lib-esm/stories/Token.stories.js +14 -1
  167. package/lib-esm/sx.d.ts +10 -2
  168. package/lib-esm/sx.js +3 -1
  169. package/lib-esm/theme-preval.js +81 -2
  170. package/lib-esm/theme.d.ts +78 -0
  171. package/lib-esm/theme.js +2 -1
  172. package/lib-esm/unreleased.d.ts +7 -0
  173. package/lib-esm/unreleased.js +8 -0
  174. package/lib-esm/utils/create-slots.d.ts +17 -0
  175. package/lib-esm/utils/create-slots.js +84 -0
  176. package/lib-esm/utils/testing.d.ts +14 -1
  177. package/lib-esm/utils/types/KeyPaths.d.ts +3 -0
  178. package/lib-esm/utils/types/KeyPaths.js +1 -0
  179. package/lib-esm/utils/use-force-update.d.ts +1 -0
  180. package/lib-esm/utils/use-force-update.js +6 -0
  181. package/lib-esm/utils/useIsomorphicLayoutEffect.d.ts +3 -0
  182. package/lib-esm/utils/useIsomorphicLayoutEffect.js +3 -0
  183. package/migrating.md +1 -1
  184. package/package-lock.json +38098 -45
  185. package/package.json +7 -3
  186. package/script/build +2 -0
  187. package/src/ActionList/Header.tsx +1 -1
  188. package/src/ActionList2/Description.tsx +49 -0
  189. package/src/ActionList2/Divider.tsx +24 -0
  190. package/src/ActionList2/Group.tsx +34 -0
  191. package/src/ActionList2/Header.tsx +58 -0
  192. package/src/ActionList2/Item.tsx +228 -0
  193. package/src/ActionList2/LinkItem.tsx +49 -0
  194. package/src/ActionList2/List.tsx +55 -0
  195. package/src/ActionList2/Selection.tsx +40 -0
  196. package/src/ActionList2/Visuals.tsx +76 -0
  197. package/src/ActionList2/index.ts +39 -0
  198. package/src/Overlay.tsx +2 -1
  199. package/src/Portal/Portal.tsx +2 -1
  200. package/src/TextInputWithTokens.tsx +64 -8
  201. package/src/_TextInputWrapper.tsx +8 -0
  202. package/src/__tests__/ActionList2.test.tsx +47 -0
  203. package/src/__tests__/AnchoredOverlay.test.tsx +2 -2
  204. package/src/__tests__/KeyPaths.types.test.ts +14 -0
  205. package/src/__tests__/TextInputWithTokens.test.tsx +123 -1
  206. package/src/__tests__/__snapshots__/ActionList2.test.tsx.snap +14 -0
  207. package/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap +35 -135
  208. package/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap +7 -0
  209. package/src/__tests__/__snapshots__/TextInput.test.tsx.snap +6 -0
  210. package/src/__tests__/__snapshots__/TextInputWithTokens.test.tsx.snap +463 -0
  211. package/src/__tests__/utils/__snapshots__/createSlots.test.tsx.snap +55 -0
  212. package/src/__tests__/utils/createSlots.test.tsx +74 -0
  213. package/src/hooks/useAnchoredPosition.ts +2 -1
  214. package/src/hooks/useCombinedRefs.ts +3 -3
  215. package/src/hooks/useResizeObserver.ts +2 -2
  216. package/src/stories/ActionList2.stories.tsx +1279 -0
  217. package/src/stories/Button.stories.tsx +1 -1
  218. package/src/stories/TextInput.stories.tsx +113 -0
  219. package/src/stories/TextInputWithTokens.stories.tsx +9 -0
  220. package/src/stories/Token.stories.tsx +12 -1
  221. package/src/sx.ts +17 -2
  222. package/src/theme-preval.js +1 -0
  223. package/src/theme.ts +86 -0
  224. package/src/unreleased.ts +9 -0
  225. package/src/utils/create-slots.tsx +96 -0
  226. package/src/utils/types/KeyPaths.ts +10 -0
  227. package/src/utils/use-force-update.ts +7 -0
  228. package/src/utils/useIsomorphicLayoutEffect.ts +10 -0
  229. package/stats.html +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/components",
3
- "version": "31.0.2-rc.c7dafefb",
3
+ "version": "31.2.0-rc.2a086bac",
4
4
  "description": "Primer react components",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib-esm/index.js",
@@ -15,7 +15,7 @@
15
15
  "start": "concurrently npm:start:*",
16
16
  "start:docs": "cd docs && npm run develop",
17
17
  "start:storybook": "start-storybook -p 6006",
18
- "lint": "eslint '**/*.{js,ts,tsx,md,mdx}'",
18
+ "lint": "eslint '**/*.{js,ts,tsx,md,mdx}' --max-warnings=0",
19
19
  "lint:fix": "npm run lint -- --fix",
20
20
  "test": "jest",
21
21
  "test:update": "npm run test -- --updateSnapshot",
@@ -44,7 +44,7 @@
44
44
  "license": "MIT",
45
45
  "dependencies": {
46
46
  "@primer/octicons-react": "^13.0.0",
47
- "@primer/primitives": "5.1.0",
47
+ "@primer/primitives": "6.1.0",
48
48
  "@radix-ui/react-polymorphic": "0.0.14",
49
49
  "@react-aria/ssr": "3.1.0",
50
50
  "@styled-system/css": "5.1.5",
@@ -124,6 +124,8 @@
124
124
  "lodash.isobject": "3.0.2",
125
125
  "prettier": "2.3.2",
126
126
  "react": "17.0.2",
127
+ "react-dnd": "14.0.4",
128
+ "react-dnd-html5-backend": "14.0.2",
127
129
  "react-dom": "17.0.2",
128
130
  "react-test-renderer": "17.0.2",
129
131
  "rollup": "2.56.3",
@@ -132,7 +134,9 @@
132
134
  "rollup-plugin-visualizer": "5.5.0",
133
135
  "semver": "7.3.5",
134
136
  "size-limit": "5.0.2",
137
+ "storybook-addon-performance": "0.16.1",
135
138
  "styled-components": "4.4.1",
139
+ "ts-toolbelt": "9.6.0",
136
140
  "typescript": "4.2.2"
137
141
  },
138
142
  "peerDependencies": {
package/script/build CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/bin/bash
2
2
 
3
+ set -e
4
+
3
5
  # Clean up
4
6
  rm -rf dist
5
7
  rm -rf lib
@@ -68,7 +68,7 @@ export function Header({
68
68
  return (
69
69
  <StyledHeader role="heading" variant={variant} {...props}>
70
70
  {title}
71
- {auxiliaryText && <span>auxiliaryText</span>}
71
+ {auxiliaryText && <span>{auxiliaryText}</span>}
72
72
  </StyledHeader>
73
73
  )
74
74
  }
@@ -0,0 +1,49 @@
1
+ import React from 'react'
2
+ import Box from '../Box'
3
+ import {SxProp, merge} from '../sx'
4
+ import Truncate from '../Truncate'
5
+ import {Slot, ItemContext} from './Item'
6
+
7
+ export type DescriptionProps = {
8
+ /**
9
+ * Secondary text style variations.
10
+ *
11
+ * - `"inline"` - Secondary text is positioned beside primary text.
12
+ * - `"block"` - Secondary text is positioned below primary text.
13
+ */
14
+ variant?: 'inline' | 'block'
15
+ } & SxProp
16
+
17
+ export const Description: React.FC<DescriptionProps> = ({variant = 'inline', sx = {}, ...props}) => {
18
+ const styles = {
19
+ color: 'fg.muted',
20
+ fontSize: 0,
21
+ lineHeight: '16px',
22
+ flexGrow: 1,
23
+ flexBasis: 0,
24
+ minWidth: 0,
25
+ marginLeft: variant === 'block' ? 0 : 2
26
+ }
27
+
28
+ return (
29
+ <Slot name={variant === 'block' ? 'BlockDescription' : 'InlineDescription'}>
30
+ {({blockDescriptionId, inlineDescriptionId}: ItemContext) =>
31
+ variant === 'block' ? (
32
+ <Box as="span" sx={merge(styles, sx as SxProp)} id={blockDescriptionId}>
33
+ {props.children}
34
+ </Box>
35
+ ) : (
36
+ <Truncate
37
+ id={inlineDescriptionId}
38
+ sx={merge(styles, sx as SxProp)}
39
+ title={props.children as string}
40
+ inline={true}
41
+ maxWidth="100%"
42
+ >
43
+ {props.children}
44
+ </Truncate>
45
+ )
46
+ }
47
+ </Slot>
48
+ )
49
+ }
@@ -0,0 +1,24 @@
1
+ import React from 'react'
2
+ import Box from '../Box'
3
+ import {get} from '../constants'
4
+ import {Theme} from '../ThemeProvider'
5
+
6
+ /**
7
+ * Visually separates `Item`s or `Group`s in an `ActionList`.
8
+ */
9
+ export function Divider(): JSX.Element {
10
+ return (
11
+ <Box
12
+ as="li"
13
+ role="separator"
14
+ sx={{
15
+ height: 1,
16
+ backgroundColor: 'actionListItem.inlineDivider',
17
+ marginTop: (theme: Theme) => `calc(${get('space.2')(theme)} - 1px)`,
18
+ marginBottom: 2,
19
+ listStyle: 'none' // hide the ::marker inserted by browser's stylesheet
20
+ }}
21
+ data-component="ActionList.Divider"
22
+ />
23
+ )
24
+ }
@@ -0,0 +1,34 @@
1
+ import React from 'react'
2
+ import Box from '../Box'
3
+ import {SxProp} from '../sx'
4
+ import {Header, HeaderProps} from './Header'
5
+ import {ListProps} from './List'
6
+
7
+ export type GroupProps = HeaderProps &
8
+ SxProp & {
9
+ selectionVariant?: ListProps['selectionVariant'] | false
10
+ }
11
+
12
+ type ContextProps = Pick<GroupProps, 'selectionVariant'>
13
+ export const GroupContext = React.createContext<ContextProps>({})
14
+
15
+ export const Group: React.FC<GroupProps> = ({title, variant, auxiliaryText, selectionVariant, sx = {}, ...props}) => {
16
+ return (
17
+ <Box
18
+ as="li"
19
+ sx={{
20
+ '&:not(:first-child)': {marginTop: 2},
21
+ listStyle: 'none', // hide the ::marker inserted by browser's stylesheet
22
+ ...sx
23
+ }}
24
+ {...props}
25
+ >
26
+ {title && <Header title={title} variant={variant} auxiliaryText={auxiliaryText} />}
27
+ <GroupContext.Provider value={{selectionVariant}}>
28
+ <Box as="ul" sx={{paddingInlineStart: 0}}>
29
+ {props.children}
30
+ </Box>
31
+ </GroupContext.Provider>
32
+ </Box>
33
+ )
34
+ }
@@ -0,0 +1,58 @@
1
+ import React from 'react'
2
+ import Box from '../Box'
3
+ import {SxProp} from '../sx'
4
+ import {ListContext} from './List'
5
+
6
+ /**
7
+ * Contract for props passed to the `Header` component.
8
+ */
9
+ export type HeaderProps = {
10
+ /**
11
+ * Style variations. Usage is discretionary.
12
+ *
13
+ * - `"filled"` - Superimposed on a background, offset from nearby content
14
+ * - `"subtle"` - Relatively less offset from nearby content
15
+ */
16
+ variant?: 'subtle' | 'filled'
17
+
18
+ /**
19
+ * Primary text which names a `Group`.
20
+ */
21
+ title?: string
22
+
23
+ /**
24
+ * Secondary text which provides additional information about a `Group`.
25
+ */
26
+ auxiliaryText?: string
27
+ } & SxProp
28
+
29
+ /**
30
+ * Displays the name and description of a `Group`.
31
+ */
32
+ export const Header = ({variant = 'subtle', title, auxiliaryText, sx = {}, ...props}: HeaderProps): JSX.Element => {
33
+ const {variant: listVariant} = React.useContext(ListContext)
34
+
35
+ const styles = {
36
+ paddingY: '6px',
37
+ paddingX: listVariant === 'full' ? 2 : 3,
38
+ fontSize: 0,
39
+ fontWeight: 'bold',
40
+ color: 'fg.muted',
41
+ ...(variant === 'filled' && {
42
+ backgroundColor: 'canvas.subtle',
43
+ marginX: 0,
44
+ marginBottom: 2,
45
+ borderTop: '1px solid',
46
+ borderBottom: '1px solid',
47
+ borderColor: 'neutral.muted'
48
+ }),
49
+ ...sx
50
+ }
51
+
52
+ return (
53
+ <Box sx={styles} role="heading" {...props}>
54
+ {title}
55
+ {auxiliaryText && <span>{auxiliaryText}</span>}
56
+ </Box>
57
+ )
58
+ }
@@ -0,0 +1,228 @@
1
+ import React from 'react'
2
+ import {ForwardRefComponent as PolymorphicForwardRefComponent} from '@radix-ui/react-polymorphic'
3
+ import {useSSRSafeId} from '@react-aria/ssr'
4
+ import styled from 'styled-components'
5
+ import {useTheme} from '../ThemeProvider'
6
+ import Box, {BoxProps} from '../Box'
7
+ import sx, {SxProp, merge} from '../sx'
8
+ import createSlots from '../utils/create-slots'
9
+ import {AriaRole} from '../utils/types'
10
+ import {ListContext} from './List'
11
+ import {Selection} from './Selection'
12
+
13
+ export const getVariantStyles = (variant: ItemProps['variant'], disabled: ItemProps['disabled']) => {
14
+ if (disabled) {
15
+ return {
16
+ color: 'fg.muted',
17
+ iconColor: 'fg.muted',
18
+ annotationColor: 'fg.muted'
19
+ }
20
+ }
21
+
22
+ switch (variant) {
23
+ case 'danger':
24
+ return {
25
+ color: 'danger.fg',
26
+ iconColor: 'danger.fg',
27
+ annotationColor: 'fg.muted',
28
+ hoverColor: 'actionListItem.danger.hoverText'
29
+ }
30
+ default:
31
+ return {
32
+ color: 'fg.default',
33
+ iconColor: 'fg.muted',
34
+ annotationColor: 'fg.muted',
35
+ hoverColor: 'fg.default'
36
+ }
37
+ }
38
+ }
39
+
40
+ export type ItemProps = {
41
+ /**
42
+ * Primary content for an Item
43
+ */
44
+ children?: React.ReactNode
45
+ /**
46
+ * Callback that will trigger both on click selection and keyboard selection.
47
+ */
48
+ onSelect?: (event: React.MouseEvent<HTMLLIElement> | React.KeyboardEvent<HTMLLIElement>) => void
49
+ /**
50
+ * Is the `Item` is currently selected?
51
+ */
52
+ selected?: boolean
53
+ /**
54
+ * Style variations associated with various `Item` types.
55
+ *
56
+ * - `"default"` - An action `Item`.
57
+ * - `"danger"` - A destructive action `Item`.
58
+ */
59
+ variant?: 'default' | 'danger'
60
+ /**
61
+ * Items that are disabled can not be clicked, selected, or navigated through.
62
+ */
63
+ disabled?: boolean
64
+ /**
65
+ * The ARIA role describing the function of `Item` component. `option` is a common value.
66
+ */
67
+ role?: AriaRole
68
+ /**
69
+ * id to attach to the root element of the Item
70
+ */
71
+ id?: string
72
+ /**
73
+ * Private API for use internally only. Used by LinkItem to wrap contents in an anchor
74
+ */
75
+ _PrivateItemWrapper?: React.FC
76
+ } & SxProp
77
+
78
+ const {Slots, Slot} = createSlots(['LeadingVisual', 'InlineDescription', 'BlockDescription', 'TrailingVisual'])
79
+ export {Slot}
80
+ export type ItemContext = Pick<ItemProps, 'variant' | 'disabled'> & {
81
+ inlineDescriptionId: string
82
+ blockDescriptionId: string
83
+ }
84
+
85
+ const LiBox = styled.li<SxProp>(sx)
86
+ export const TEXT_ROW_HEIGHT = '20px' // custom value off the scale
87
+
88
+ export const Item = React.forwardRef<HTMLLIElement, ItemProps>(
89
+ (
90
+ {
91
+ variant = 'default',
92
+ disabled = false,
93
+ selected = undefined,
94
+ onSelect = () => null,
95
+ sx: sxProp = {},
96
+ id,
97
+ _PrivateItemWrapper = ({children}) => <>{children}</>,
98
+ ...props
99
+ },
100
+ forwardedRef
101
+ ): JSX.Element => {
102
+ const {variant: listVariant, showDividers} = React.useContext(ListContext)
103
+
104
+ const {theme} = useTheme()
105
+
106
+ const styles = {
107
+ display: 'flex',
108
+ paddingX: 2,
109
+ fontSize: 1,
110
+ paddingY: '6px', // custom value off the scale
111
+ lineHeight: TEXT_ROW_HEIGHT,
112
+ marginX: listVariant === 'inset' ? 2 : 0,
113
+ minHeight: 5,
114
+ borderRadius: 2,
115
+ transition: 'background 33.333ms linear',
116
+ color: getVariantStyles(variant, disabled).color,
117
+ textDecoration: 'none', // for as="a"
118
+ ':not([aria-disabled])': {cursor: 'pointer'},
119
+ '@media (hover: hover) and (pointer: fine)': {
120
+ ':hover:not([aria-disabled])': {
121
+ backgroundColor: `actionListItem.${variant}.hoverBg`,
122
+ color: getVariantStyles(variant, disabled).hoverColor
123
+ },
124
+ ':focus:not([aria-disabled])': {
125
+ backgroundColor: `actionListItem.${variant}.selectedBg`,
126
+ color: getVariantStyles(variant, disabled).hoverColor,
127
+ outline: 'none'
128
+ },
129
+ ':active:not([aria-disabled])': {
130
+ backgroundColor: `actionListItem.${variant}.activeBg`,
131
+ color: getVariantStyles(variant, disabled).hoverColor
132
+ }
133
+ },
134
+
135
+ /** Divider styles */
136
+ '[data-component="ActionList.Item--DividerContainer"]': {
137
+ position: 'relative'
138
+ },
139
+ '[data-component="ActionList.Item--DividerContainer"]::before': {
140
+ content: '" "',
141
+ display: 'block',
142
+ position: 'absolute',
143
+ width: '100%',
144
+ top: '-7px',
145
+ border: '0 solid',
146
+ borderTopWidth: showDividers ? `1px` : '0',
147
+ borderColor: 'var(--divider-color, transparent)'
148
+ },
149
+ // show between 2 items
150
+ ':not(:first-of-type):not([aria-selected=true])': {'--divider-color': theme?.colors.actionListItem.inlineDivider},
151
+ // hide divider after dividers & group header, with higher importance!
152
+ '[data-component="ActionList.Divider"] + &': {'--divider-color': 'transparent !important'},
153
+ // hide border on current and previous item
154
+ '&:hover:not([aria-disabled]), &:focus:not([aria-disabled])': {'--divider-color': 'transparent'},
155
+ '&:hover:not([aria-disabled]) + &, &:focus:not([aria-disabled]) + &': {'--divider-color': 'transparent'},
156
+ // hide border around selected item
157
+ '&[aria-selected=true] + &': {'--divider-color': 'transparent'}
158
+ }
159
+
160
+ const clickHandler = React.useCallback(
161
+ event => {
162
+ if (disabled) return
163
+ if (!event.defaultPrevented) onSelect(event)
164
+ },
165
+ [onSelect, disabled]
166
+ )
167
+
168
+ // use props.id if provided, otherwise generate one.
169
+ const labelId = useSSRSafeId(id)
170
+ const inlineDescriptionId = useSSRSafeId(id && `${id}--inline-description`)
171
+ const blockDescriptionId = useSSRSafeId(id && `${id}--block-description`)
172
+
173
+ return (
174
+ <Slots context={{variant, disabled, inlineDescriptionId, blockDescriptionId}}>
175
+ {slots => (
176
+ <LiBox
177
+ ref={forwardedRef}
178
+ sx={merge(styles, sxProp as SxProp)}
179
+ onClick={clickHandler}
180
+ aria-selected={selected}
181
+ aria-disabled={disabled ? true : undefined}
182
+ tabIndex={disabled ? undefined : -1}
183
+ aria-labelledby={labelId}
184
+ aria-describedby={[
185
+ slots.InlineDescription && inlineDescriptionId,
186
+ slots.BlockDescription && blockDescriptionId
187
+ ]
188
+ .filter(Boolean)
189
+ .join(' ')}
190
+ {...props}
191
+ >
192
+ <_PrivateItemWrapper>
193
+ <Selection selected={selected} disabled={disabled} />
194
+ {slots.LeadingVisual}
195
+ <Box
196
+ data-component="ActionList.Item--DividerContainer"
197
+ sx={{display: 'flex', flexDirection: 'column', flexGrow: 1, minWidth: 0}}
198
+ >
199
+ <ConditionalBox if={Boolean(slots.TrailingVisual)} sx={{display: 'flex', flexGrow: 1}}>
200
+ <ConditionalBox
201
+ if={Boolean(slots.InlineDescription)}
202
+ sx={{display: 'flex', flexGrow: 1, alignItems: 'baseline', minWidth: 0}}
203
+ >
204
+ <Box as="span" id={labelId} sx={{flexGrow: slots.InlineDescription ? 0 : 1}}>
205
+ {props.children}
206
+ </Box>
207
+ {slots.InlineDescription}
208
+ </ConditionalBox>
209
+ {slots.TrailingVisual}
210
+ </ConditionalBox>
211
+ {slots.BlockDescription}
212
+ </Box>
213
+ </_PrivateItemWrapper>
214
+ </LiBox>
215
+ )}
216
+ </Slots>
217
+ )
218
+ }
219
+ ) as PolymorphicForwardRefComponent<'li', ItemProps>
220
+
221
+ Item.displayName = 'ActionList.Item'
222
+
223
+ const ConditionalBox: React.FC<{if: boolean} & BoxProps> = props => {
224
+ const {if: condition, ...rest} = props
225
+
226
+ if (condition) return <Box {...rest}>{props.children}</Box>
227
+ else return <>{props.children}</>
228
+ }
@@ -0,0 +1,49 @@
1
+ import React from 'react'
2
+ import {ForwardRefComponent as PolymorphicForwardRefComponent} from '@radix-ui/react-polymorphic'
3
+ import Link from '../Link'
4
+ import {SxProp, merge} from '../sx'
5
+ import {Item, ItemProps} from './Item'
6
+
7
+ // adopted from React.AnchorHTMLAttributes
8
+ type LinkProps = {
9
+ download?: string
10
+ href?: string
11
+ hrefLang?: string
12
+ media?: string
13
+ ping?: string
14
+ rel?: string
15
+ target?: string
16
+ type?: string
17
+ referrerPolicy?: React.AnchorHTMLAttributes<HTMLAnchorElement>['referrerPolicy']
18
+ }
19
+
20
+ // LinkItem does not support selected, variants, etc.
21
+ type LinkItemProps = Pick<ItemProps, 'children' | 'sx'> & LinkProps
22
+
23
+ export const LinkItem = React.forwardRef(({sx = {}, as: Component, ...props}, forwardedRef) => {
24
+ const styles = {
25
+ // occupy full size of Item
26
+ paddingX: 2,
27
+ paddingY: '6px', // custom value off the scale
28
+ display: 'flex',
29
+ flexGrow: 1, // full width
30
+ borderRadius: 2,
31
+
32
+ // inherit Item styles
33
+ color: 'inherit',
34
+ '&:hover': {color: 'inherit', textDecoration: 'none'}
35
+ }
36
+
37
+ return (
38
+ <Item
39
+ sx={{paddingY: 0, paddingX: 0}}
40
+ _PrivateItemWrapper={({children}) => (
41
+ <Link as={Component} sx={merge(styles, sx as SxProp)} {...props} ref={forwardedRef}>
42
+ {children}
43
+ </Link>
44
+ )}
45
+ >
46
+ {props.children}
47
+ </Item>
48
+ )
49
+ }) as PolymorphicForwardRefComponent<'a', LinkItemProps>
@@ -0,0 +1,55 @@
1
+ import React from 'react'
2
+ import {ForwardRefComponent as PolymorphicForwardRefComponent} from '@radix-ui/react-polymorphic'
3
+ import styled from 'styled-components'
4
+ import sx, {SxProp, merge} from '../sx'
5
+ import {AriaRole} from '../utils/types'
6
+
7
+ export type ListProps = {
8
+ /**
9
+ * `inset` children are offset (vertically and horizontally) from `List`’s edges, `full` children are flush (vertically and horizontally) with `List` edges
10
+ */
11
+ variant?: 'inset' | 'full'
12
+ /**
13
+ * Whether multiple Items or a single Item can be selected.
14
+ */
15
+ selectionVariant?: 'single' | 'multiple'
16
+ /**
17
+ * Display a divider above each `Item` in this `List` when it does not follow a `Header` or `Divider`.
18
+ */
19
+ showDividers?: boolean
20
+ /**
21
+ * The ARIA role describing the function of `List` component. `listbox` or `menu` are a common values.
22
+ */
23
+ role?: AriaRole
24
+ } & SxProp
25
+
26
+ type ContextProps = Omit<ListProps, 'sx'>
27
+ export const ListContext = React.createContext<ContextProps>({})
28
+
29
+ const ListBox = styled.ul<SxProp>(sx)
30
+
31
+ export const List = React.forwardRef<HTMLUListElement, ListProps>(
32
+ (
33
+ {variant = 'inset', selectionVariant, showDividers = false, sx: sxProp = {}, ...props},
34
+ forwardedRef
35
+ ): JSX.Element => {
36
+ const styles = {
37
+ margin: 0,
38
+ paddingInlineStart: 0, // reset ul styles
39
+ paddingY: variant === 'inset' ? 2 : 0
40
+ }
41
+
42
+ return (
43
+ <ListBox
44
+ sx={merge(styles, sxProp as SxProp)}
45
+ aria-multiselectable={selectionVariant === 'multiple' ? true : undefined}
46
+ {...props}
47
+ ref={forwardedRef}
48
+ >
49
+ <ListContext.Provider value={{variant, selectionVariant, showDividers}}>{props.children}</ListContext.Provider>
50
+ </ListBox>
51
+ )
52
+ }
53
+ ) as PolymorphicForwardRefComponent<'ul', ListProps>
54
+
55
+ List.displayName = 'ActionList'
@@ -0,0 +1,40 @@
1
+ import React from 'react'
2
+ import {CheckIcon} from '@primer/octicons-react'
3
+ import {ListContext} from './List'
4
+ import {GroupContext} from './Group'
5
+ import {ItemProps} from './Item'
6
+ import {LeadingVisualContainer} from './Visuals'
7
+
8
+ type SelectionProps = Pick<ItemProps, 'selected' | 'disabled'>
9
+ export const Selection: React.FC<SelectionProps> = ({selected, disabled}) => {
10
+ const {selectionVariant: listSelectionVariant} = React.useContext(ListContext)
11
+ const {selectionVariant: groupSelectionVariant} = React.useContext(GroupContext)
12
+
13
+ /** selectionVariant in Group can override the selectionVariant in List root */
14
+ const selectionVariant = typeof groupSelectionVariant !== 'undefined' ? groupSelectionVariant : listSelectionVariant
15
+
16
+ // if selectionVariant is not set on List, don't show selection
17
+ if (!selectionVariant) {
18
+ // to avoid confusion, fail loudly instead of silently ignoring
19
+ if (selected)
20
+ throw new Error(
21
+ 'For Item to be selected, ActionList or ActionList.Group needs to have a selectionVariant defined'
22
+ )
23
+ return null
24
+ }
25
+
26
+ if (selectionVariant === 'single') {
27
+ return <LeadingVisualContainer>{selected && <CheckIcon />}</LeadingVisualContainer>
28
+ }
29
+
30
+ /**
31
+ * selectionVariant is multiple
32
+ * readOnly is required because we are doing a one-way bind to `checked`
33
+ * aria-readonly="false" tells screen that they can still interact with the checkbox
34
+ */
35
+ return (
36
+ <LeadingVisualContainer sx={{input: {margin: 0, pointerEvents: 'none'}}}>
37
+ <input type="checkbox" checked={selected} disabled={disabled} tabIndex={-1} readOnly aria-readonly="false" />
38
+ </LeadingVisualContainer>
39
+ )
40
+ }
@@ -0,0 +1,76 @@
1
+ import React from 'react'
2
+ import Box from '../Box'
3
+ import {SxProp, merge} from '../sx'
4
+ import {get} from '../constants'
5
+ import {getVariantStyles, Slot, ItemContext, TEXT_ROW_HEIGHT} from './Item'
6
+
7
+ type VisualProps = SxProp & React.HTMLAttributes<HTMLSpanElement>
8
+
9
+ export const LeadingVisualContainer: React.FC<VisualProps> = ({sx = {}, ...props}) => {
10
+ return (
11
+ <Box
12
+ as="span"
13
+ sx={merge(
14
+ {
15
+ height: TEXT_ROW_HEIGHT, // match height of text row
16
+ minWidth: get('space.3'),
17
+ maxWidth: TEXT_ROW_HEIGHT, // square (same as height)
18
+ display: 'flex',
19
+ justifyContent: 'center',
20
+ alignItems: 'center',
21
+ flexShrink: 0,
22
+ marginRight: 2
23
+ },
24
+ sx as SxProp
25
+ )}
26
+ {...props}
27
+ />
28
+ )
29
+ }
30
+
31
+ export type LeadingVisualProps = VisualProps
32
+ export const LeadingVisual: React.FC<VisualProps> = ({sx = {}, ...props}) => {
33
+ return (
34
+ <Slot name="LeadingVisual">
35
+ {({variant, disabled}: ItemContext) => (
36
+ <LeadingVisualContainer
37
+ sx={merge(
38
+ {
39
+ color: getVariantStyles(variant, disabled).iconColor,
40
+ svg: {fontSize: 0}
41
+ },
42
+ sx as SxProp
43
+ )}
44
+ {...props}
45
+ >
46
+ {props.children}
47
+ </LeadingVisualContainer>
48
+ )}
49
+ </Slot>
50
+ )
51
+ }
52
+
53
+ export type TrailingVisualProps = VisualProps
54
+ export const TrailingVisual: React.FC<VisualProps> = ({sx = {}, ...props}) => {
55
+ return (
56
+ <Slot name="TrailingVisual">
57
+ {({variant, disabled}: ItemContext) => (
58
+ <Box
59
+ as="span"
60
+ sx={merge(
61
+ {
62
+ height: '20px', // match height of text row
63
+ flexShrink: 0,
64
+ color: getVariantStyles(variant, disabled).annotationColor,
65
+ marginLeft: 2
66
+ },
67
+ sx as SxProp
68
+ )}
69
+ {...props}
70
+ >
71
+ {props.children}
72
+ </Box>
73
+ )}
74
+ </Slot>
75
+ )
76
+ }