paris 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +12 -1
- package/package.json +18 -7
- package/src/stories/accordion/Accordion.module.scss +53 -0
- package/src/stories/accordion/Accordion.stories.ts +18 -0
- package/src/stories/accordion/Accordion.tsx +90 -0
- package/src/stories/accordion/index.ts +1 -0
- package/src/stories/avatar/Avatar.module.scss +6 -0
- package/src/stories/avatar/Avatar.stories.ts +23 -0
- package/src/stories/avatar/Avatar.tsx +39 -0
- package/src/stories/avatar/index.ts +1 -0
- package/src/stories/button/Button.tsx +22 -1
- package/src/stories/callout/Callout.module.scss +43 -0
- package/src/stories/callout/Callout.stories.ts +53 -0
- package/src/stories/callout/Callout.tsx +42 -0
- package/src/stories/callout/index.ts +1 -0
- package/src/stories/checkbox/Checkbox.module.scss +1 -0
- package/src/stories/combobox/Combobox.tsx +1 -1
- package/src/stories/dialog/Dialog.tsx +46 -41
- package/src/stories/drawer/Drawer.module.scss +32 -11
- package/src/stories/drawer/Drawer.tsx +99 -100
- package/src/stories/field/Field.tsx +5 -2
- package/src/stories/icon/Ellipsis.tsx +8 -0
- package/src/stories/icon/Icon.stories.ts +1 -0
- package/src/stories/icon/index.ts +1 -0
- package/src/stories/input/Input.module.scss +2 -1
- package/src/stories/input/Input.tsx +9 -3
- package/src/stories/select/Select.tsx +3 -3
- package/src/stories/styledlink/StyledLink.module.scss +11 -0
- package/src/stories/styledlink/StyledLink.stories.ts +23 -0
- package/src/stories/styledlink/StyledLink.tsx +39 -0
- package/src/stories/styledlink/index.ts +1 -0
- package/src/stories/table/Table.module.scss +24 -0
- package/src/stories/table/Table.stories.ts +18 -5
- package/src/stories/table/Table.tsx +35 -16
- package/src/stories/tabs/Tabs.module.scss +51 -8
- package/src/stories/tabs/Tabs.stories.ts +21 -3
- package/src/stories/tabs/Tabs.tsx +49 -4
- package/src/stories/tag/Tag.module.scss +46 -0
- package/src/stories/tag/Tag.stories.ts +32 -0
- package/src/stories/tag/Tag.tsx +44 -0
- package/src/stories/tag/index.ts +1 -0
- package/src/stories/text/Text.module.scss +1 -0
- package/src/stories/text/Text.tsx +13 -1
- package/src/stories/text/Typography.module.css +10 -1
- package/src/stories/theme/global.scss +4 -0
- package/src/stories/theme/themes.ts +23 -1
- package/src/stories/toast/Toast.module.scss +20 -0
- package/src/stories/toast/Toast.stories.tsx +29 -0
- package/src/stories/toast/Toast.tsx +51 -0
- package/src/stories/toast/index.ts +1 -0
- package/src/stories/utility/EasingFunctions.ts +3 -0
- package/src/stories/utility/TextWhenString.tsx +2 -2
- package/src/stories/utility/index.ts +4 -0
- package/src/stories/field/Field.module.scss +0 -5
- /package/src/stories/{dropdown → utility}/Dropdown.module.scss +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# paris
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0234060: StyledLink component
|
|
8
|
+
- 0234060: Accordion component
|
|
9
|
+
- 0234060: Avatar component
|
|
10
|
+
- 0234060: Toast component
|
|
11
|
+
- 516e8ec: Tag component
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 0234060: Callout component
|
|
16
|
+
- 516e8ec: Icon: Ellipsis icon
|
|
17
|
+
- 0234060: Various stylistic and positional fixes
|
|
18
|
+
- 516e8ec: Theme: `labelXXSmall` typography + `backgroundInverseWarning` color
|
|
19
|
+
- 516e8ec: Drawer: stylistic updates
|
|
20
|
+
- 8e959b8: Table: support `hideBelow` for `max-width` breakpoint hiding of columns
|
|
21
|
+
- 8e959b8: Tabs: add `compact` styling option
|
|
22
|
+
|
|
3
23
|
## 0.5.0
|
|
4
24
|
|
|
5
25
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -48,6 +48,13 @@ Paris uses `pte` (our theming engine) for powering theming and initial styles th
|
|
|
48
48
|
|
|
49
49
|
Additionally, you need to import the static global styles from `paris/theme/global.scss`.
|
|
50
50
|
|
|
51
|
+
Paris also relies on CSS Container Queries for responsive changes on certain elements, like the Tabs component when `kind = auto`. Adding the Google polyfill for Container Queries is recommended to ensure legacy browser support:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
// Use the `Script` component instead in Next.js
|
|
55
|
+
<script src="https://cdn.jsdelivr.net/npm/container-query-polyfill@1/dist/container-query-polyfill.modern.js" />
|
|
56
|
+
```
|
|
57
|
+
|
|
51
58
|
For example, with the Next.js 13 app directory, you can do all of this in your root `layout.tsx` file:
|
|
52
59
|
|
|
53
60
|
```tsx
|
|
@@ -80,7 +87,11 @@ export default function RootLayout({
|
|
|
80
87
|
}}
|
|
81
88
|
/>
|
|
82
89
|
</head>
|
|
83
|
-
|
|
90
|
+
{/* Set the class "paris-container" to your root layout container */}
|
|
91
|
+
<body className="paris-container">
|
|
92
|
+
{children}
|
|
93
|
+
<Script src="https://cdn.jsdelivr.net/npm/container-query-polyfill@1/dist/container-query-polyfill.modern.js" />
|
|
94
|
+
</body>
|
|
84
95
|
</html>
|
|
85
96
|
);
|
|
86
97
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "paris",
|
|
3
3
|
"author": "Sanil Chawla <sanil@slingshot.fm> (https://sanil.co)",
|
|
4
4
|
"description": "Paris is Slingshot's React design system. It's a collection of reusable components, design tokens, and guidelines that help us build consistent, accessible, and performant user interfaces.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.6.0",
|
|
6
6
|
"homepage": "https://paris.slingshot.fm",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"url": "https://github.com/slingshot/paris"
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
|
+
"src/stories",
|
|
13
14
|
"src",
|
|
14
15
|
"README.md",
|
|
15
16
|
"CHANGELOG.md"
|
|
@@ -30,24 +31,29 @@
|
|
|
30
31
|
},
|
|
31
32
|
"exports": {
|
|
32
33
|
"./*": "./src/stories/*",
|
|
34
|
+
"./accordion": "./src/stories/accordion/index.ts",
|
|
35
|
+
"./avatar": "./src/stories/avatar/index.ts",
|
|
33
36
|
"./button": "./src/stories/button/index.ts",
|
|
37
|
+
"./callout": "./src/stories/callout/index.ts",
|
|
34
38
|
"./card": "./src/stories/card/index.ts",
|
|
35
39
|
"./checkbox": "./src/stories/checkbox/index.ts",
|
|
36
40
|
"./combobox": "./src/stories/combobox/index.ts",
|
|
37
41
|
"./dialog": "./src/stories/dialog/index.ts",
|
|
38
42
|
"./drawer": "./src/stories/drawer/index.ts",
|
|
39
|
-
"./dropdown": "./src/stories/dropdown/index.ts",
|
|
40
43
|
"./field": "./src/stories/field/index.ts",
|
|
41
44
|
"./icon": "./src/stories/icon/index.ts",
|
|
42
45
|
"./input": "./src/stories/input/index.ts",
|
|
43
46
|
"./pagination": "./src/stories/pagination/index.ts",
|
|
44
47
|
"./select": "./src/stories/select/index.ts",
|
|
48
|
+
"./styledlink": "./src/stories/styledlink/index.ts",
|
|
45
49
|
"./table": "./src/stories/table/index.ts",
|
|
46
50
|
"./tabs": "./src/stories/tabs/index.ts",
|
|
51
|
+
"./tag": "./src/stories/tag/index.ts",
|
|
47
52
|
"./text": "./src/stories/text/index.ts",
|
|
48
53
|
"./textarea": "./src/stories/textarea/index.ts",
|
|
49
54
|
"./theme": "./src/stories/theme/index.ts",
|
|
50
55
|
"./tilt": "./src/stories/tilt/index.ts",
|
|
56
|
+
"./toast": "./src/stories/toast/index.ts",
|
|
51
57
|
"./utility": "./src/stories/utility/index.ts"
|
|
52
58
|
},
|
|
53
59
|
"dependencies": {
|
|
@@ -55,7 +61,10 @@
|
|
|
55
61
|
"@headlessui/react": "^1.7.14",
|
|
56
62
|
"@radix-ui/react-checkbox": "^1.0.4",
|
|
57
63
|
"clsx": "^1.2.1",
|
|
64
|
+
"font-color-contrast": "^11.1.0",
|
|
65
|
+
"framer-motion": "^10.16.4",
|
|
58
66
|
"pte": "^0.4.9",
|
|
67
|
+
"react-hot-toast": "^2.4.1",
|
|
59
68
|
"react-parallax-tilt": "^1.7.144",
|
|
60
69
|
"ts-deepmerge": "^6.0.3"
|
|
61
70
|
},
|
|
@@ -64,10 +73,10 @@
|
|
|
64
73
|
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
|
65
74
|
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
|
66
75
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
|
67
|
-
"react": "^18.x
|
|
68
|
-
"react-dom": "^18.x
|
|
76
|
+
"react": "^18.x",
|
|
77
|
+
"react-dom": "^18.x",
|
|
69
78
|
"sass": "^1.x",
|
|
70
|
-
"typescript": "^5.0
|
|
79
|
+
"typescript": "^5.0"
|
|
71
80
|
},
|
|
72
81
|
"devDependencies": {
|
|
73
82
|
"@changesets/cli": "^2.26.1",
|
|
@@ -85,13 +94,14 @@
|
|
|
85
94
|
"@storybook/testing-library": "^0.2.2",
|
|
86
95
|
"@storybook/theming": "^7.5.1",
|
|
87
96
|
"@types/node": "18.15.11",
|
|
88
|
-
"@types/react": "18
|
|
89
|
-
"@types/react-dom": "18
|
|
97
|
+
"@types/react": "^18",
|
|
98
|
+
"@types/react-dom": "^18",
|
|
90
99
|
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
|
91
100
|
"@typescript-eslint/parser": "^5.0.0",
|
|
92
101
|
"autoprefixer": "^10.4.14",
|
|
93
102
|
"change-case": "^4.1.2",
|
|
94
103
|
"csstype": "^3.1.2",
|
|
104
|
+
"esbuild-sass-plugin": "^2.16.0",
|
|
95
105
|
"eslint": "^8.2.0",
|
|
96
106
|
"eslint-config-next": "13.4.2",
|
|
97
107
|
"eslint-plugin-css": "^0.8.0",
|
|
@@ -109,6 +119,7 @@
|
|
|
109
119
|
"title-case": "^3.0.3",
|
|
110
120
|
"ts-node": "^10.9.1",
|
|
111
121
|
"tsconfig-paths-webpack-plugin": "^4.0.1",
|
|
122
|
+
"tsup": "^7.2.0",
|
|
112
123
|
"type-fest": "^3.10.0",
|
|
113
124
|
"typescript": "^5.2.2"
|
|
114
125
|
},
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
.container {
|
|
2
|
+
color: var(--pte-colors-contentPrimary);
|
|
3
|
+
border-bottom: 1px solid var(--pte-colors-borderOpaque);
|
|
4
|
+
cursor: pointer;
|
|
5
|
+
transition: all var(--pte-animations-duration-relaxed) var(--pte-animations-timing-easeInOutExpo);
|
|
6
|
+
|
|
7
|
+
//&:hover {
|
|
8
|
+
// background-color: var(--pte-colors-backgroundSecondary);
|
|
9
|
+
//}
|
|
10
|
+
|
|
11
|
+
.title {
|
|
12
|
+
padding: 16px 0;
|
|
13
|
+
display: flex;
|
|
14
|
+
justify-content: space-between;
|
|
15
|
+
align-items: center;
|
|
16
|
+
font-size: 16px;
|
|
17
|
+
font-weight: var(--pte-typography-fontWeights-medium);
|
|
18
|
+
|
|
19
|
+
border-bottom: 1px solid transparent;
|
|
20
|
+
transition: border-bottom-color var(--pte-animations-duration-gradual) var(--pte-animations-timing-easeInOutExpo);
|
|
21
|
+
&.open {
|
|
22
|
+
border-bottom-color: var(--pte-colors-borderOpaque);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.plusIcon {
|
|
27
|
+
margin-left: 4px;
|
|
28
|
+
|
|
29
|
+
svg {
|
|
30
|
+
transition: transform var(--pte-animations-duration-gradual) var(--pte-animations-timing-easeInOutExpo);
|
|
31
|
+
transform: rotate(0);
|
|
32
|
+
width: 16px;
|
|
33
|
+
height: 16px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
svg.open {
|
|
37
|
+
transform: rotate(135deg) !important;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.dropdown {
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
cursor: auto;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.dropdownContent {
|
|
47
|
+
padding: 12px;
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
gap: 12px;
|
|
51
|
+
background-color: var(--pte-colors-backgroundSecondary);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Accordion } from './Accordion';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Accordion> = {
|
|
5
|
+
title: 'Content/Accordion',
|
|
6
|
+
component: Accordion,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof Accordion>;
|
|
12
|
+
|
|
13
|
+
export const Default: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
title: 'Where were we?',
|
|
16
|
+
children: 'In an alleyway, drinking champagne.',
|
|
17
|
+
},
|
|
18
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { FC, ReactNode } from 'react';
|
|
2
|
+
import { AnimatePresence, motion } from 'framer-motion';
|
|
3
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
4
|
+
import { faPlus } from '@fortawesome/free-solid-svg-icons';
|
|
5
|
+
import { useState } from 'react';
|
|
6
|
+
import clsx from 'clsx';
|
|
7
|
+
import styles from './Accordion.module.scss';
|
|
8
|
+
import { TextWhenString } from '../utility';
|
|
9
|
+
|
|
10
|
+
export type AccordionProps = {
|
|
11
|
+
/** The title of the Accordion. */
|
|
12
|
+
title?: ReactNode;
|
|
13
|
+
/** The collapsible contents of the Accordion. */
|
|
14
|
+
children?: ReactNode;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A Accordion component.
|
|
19
|
+
*
|
|
20
|
+
* <hr />
|
|
21
|
+
*
|
|
22
|
+
* To use this component, import it as follows:
|
|
23
|
+
*
|
|
24
|
+
* ```js
|
|
25
|
+
* import { Accordion } from 'paris/accordion';
|
|
26
|
+
* ```
|
|
27
|
+
* @constructor
|
|
28
|
+
*/
|
|
29
|
+
export const Accordion: FC<AccordionProps> = ({
|
|
30
|
+
title,
|
|
31
|
+
children,
|
|
32
|
+
}) => {
|
|
33
|
+
const [open, setOpen] = useState(false);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div
|
|
37
|
+
className={styles.container}
|
|
38
|
+
>
|
|
39
|
+
<div
|
|
40
|
+
className={clsx(styles.title, open && styles.open)}
|
|
41
|
+
onClick={() => setOpen((o) => !o)}
|
|
42
|
+
onKeyDown={(e) => {
|
|
43
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
setOpen((o) => !o);
|
|
46
|
+
}
|
|
47
|
+
}}
|
|
48
|
+
role="button"
|
|
49
|
+
tabIndex={0}
|
|
50
|
+
>
|
|
51
|
+
<div>
|
|
52
|
+
<TextWhenString kind="paragraphSmall" weight="medium">
|
|
53
|
+
{title}
|
|
54
|
+
</TextWhenString>
|
|
55
|
+
</div>
|
|
56
|
+
<div className={styles.plusIcon}>
|
|
57
|
+
<FontAwesomeIcon
|
|
58
|
+
icon={faPlus}
|
|
59
|
+
className={clsx(open && styles.open)}
|
|
60
|
+
/>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
<AnimatePresence>
|
|
64
|
+
{open && (
|
|
65
|
+
<motion.div
|
|
66
|
+
className={styles.dropdown}
|
|
67
|
+
key="content"
|
|
68
|
+
initial="collapsed"
|
|
69
|
+
animate="open"
|
|
70
|
+
exit="collapsed"
|
|
71
|
+
variants={{
|
|
72
|
+
open: { opacity: 1, height: 'auto', y: 0 },
|
|
73
|
+
collapsed: { opacity: 0, height: 0, y: -10 },
|
|
74
|
+
}}
|
|
75
|
+
transition={{
|
|
76
|
+
duration: 0.8,
|
|
77
|
+
ease: [0.87, 0, 0.13, 1],
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
<div className={styles.dropdownContent}>
|
|
81
|
+
<TextWhenString kind="paragraphXSmall">
|
|
82
|
+
{children}
|
|
83
|
+
</TextWhenString>
|
|
84
|
+
</div>
|
|
85
|
+
</motion.div>
|
|
86
|
+
)}
|
|
87
|
+
</AnimatePresence>
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Accordion';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Avatar } from './Avatar';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Avatar> = {
|
|
5
|
+
title: 'Uncategorized/Avatar',
|
|
6
|
+
component: Avatar,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof Avatar>;
|
|
12
|
+
|
|
13
|
+
export const Default: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
children: 'Hello world! This is a new Avatar component.',
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const Secondary: Story = {
|
|
20
|
+
args: {
|
|
21
|
+
children: 'Hello world! This is a secondary component.',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { CSSProperties, FC, ReactNode } from 'react';
|
|
2
|
+
import type { CSSLength } from '@ssh/csstypes';
|
|
3
|
+
import styles from './Avatar.module.scss';
|
|
4
|
+
import { pvar } from '../theme';
|
|
5
|
+
|
|
6
|
+
export type AvatarProps = {
|
|
7
|
+
frameColor?: string;
|
|
8
|
+
width?: CSSLength;
|
|
9
|
+
/** The contents of the Avatar. */
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* An Avatar component.
|
|
15
|
+
*
|
|
16
|
+
* <hr />
|
|
17
|
+
*
|
|
18
|
+
* To use this component, import it as follows:
|
|
19
|
+
*
|
|
20
|
+
* ```js
|
|
21
|
+
* import { Avatar } from 'paris/avatar';
|
|
22
|
+
* ```
|
|
23
|
+
* @constructor
|
|
24
|
+
*/
|
|
25
|
+
export const Avatar: FC<AvatarProps> = ({
|
|
26
|
+
frameColor = pvar('colors.borderOpaque'),
|
|
27
|
+
width = 'fit-content',
|
|
28
|
+
children,
|
|
29
|
+
}) => (
|
|
30
|
+
<div
|
|
31
|
+
style={{
|
|
32
|
+
width,
|
|
33
|
+
'--frame-color': frameColor,
|
|
34
|
+
} as CSSProperties}
|
|
35
|
+
className={styles.content}
|
|
36
|
+
>
|
|
37
|
+
{children}
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Avatar';
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
+
CSSProperties,
|
|
4
5
|
FC, HTMLAttributeAnchorTarget, MouseEventHandler, ReactNode,
|
|
5
6
|
} from 'react';
|
|
6
7
|
import type { ButtonProps as AriaButtonProps } from '@ariakit/react';
|
|
7
8
|
import { Button as AriaButton } from '@ariakit/react';
|
|
8
9
|
import clsx from 'clsx';
|
|
10
|
+
import fontColorContrast from 'font-color-contrast';
|
|
9
11
|
import styles from './Button.module.scss';
|
|
10
12
|
import { Text } from '../text';
|
|
11
13
|
import type { Enhancer } from '../../types/Enhancer';
|
|
12
|
-
import { MemoizedEnhancer
|
|
14
|
+
import { MemoizedEnhancer } from '../../helpers/renderEnhancer';
|
|
13
15
|
|
|
14
16
|
const EnhancerSizes = {
|
|
15
17
|
large: 13,
|
|
@@ -33,6 +35,17 @@ export type ButtonProps = {
|
|
|
33
35
|
* @default pill
|
|
34
36
|
*/
|
|
35
37
|
shape?: 'pill' | 'circle' | 'rectangle' | 'square' | 'rounded';
|
|
38
|
+
/**
|
|
39
|
+
* A color to apply for the Button. Provide an object with `primary` and `secondary` properties to set the primary and hover colors.
|
|
40
|
+
*/
|
|
41
|
+
colors?: {
|
|
42
|
+
/** The primary color of the Button. Primary buttons will use this as the background color, and secondary/tertiary buttons will use it for the text and border. */
|
|
43
|
+
primary: string;
|
|
44
|
+
/**
|
|
45
|
+
* The secondary color of the Button, used for hover/active states.
|
|
46
|
+
*/
|
|
47
|
+
secondary: string;
|
|
48
|
+
};
|
|
36
49
|
/**
|
|
37
50
|
* An icon or other element to render before the Button's text. A `size` argument is passed that should be used to determine the width & height of the content displayed.
|
|
38
51
|
*
|
|
@@ -85,6 +98,7 @@ export const Button: FC<ButtonProps> = ({
|
|
|
85
98
|
kind = 'primary',
|
|
86
99
|
size = 'large',
|
|
87
100
|
shape = 'pill',
|
|
101
|
+
colors,
|
|
88
102
|
type = 'button',
|
|
89
103
|
startEnhancer,
|
|
90
104
|
endEnhancer,
|
|
@@ -96,6 +110,13 @@ export const Button: FC<ButtonProps> = ({
|
|
|
96
110
|
}) => (
|
|
97
111
|
<AriaButton
|
|
98
112
|
{...props}
|
|
113
|
+
style={colors ? {
|
|
114
|
+
'--pte-colors-contentInversePrimary': fontColorContrast(colors.primary),
|
|
115
|
+
'--pte-colors-backgroundInversePrimary': colors.primary,
|
|
116
|
+
'--pte-colors-backgroundInverseTertiary': colors.secondary,
|
|
117
|
+
'--pte-colors-contentPrimary': colors.primary,
|
|
118
|
+
'--pte-colors-backgroundTertiary': colors.secondary,
|
|
119
|
+
} as CSSProperties : {}}
|
|
99
120
|
className={clsx(
|
|
100
121
|
styles.button,
|
|
101
122
|
styles[kind],
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
.content {
|
|
2
|
+
background-color: var(--pte-colors-backgroundSecondary);
|
|
3
|
+
border-radius: 6px;
|
|
4
|
+
width: 100%;
|
|
5
|
+
padding: 8px 10px;
|
|
6
|
+
color: var(--pte-colors-contentTertiary);
|
|
7
|
+
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: row;
|
|
10
|
+
justify-content: flex-start;
|
|
11
|
+
align-items: center;
|
|
12
|
+
gap: 8px;
|
|
13
|
+
|
|
14
|
+
.icon {
|
|
15
|
+
width: 16px;
|
|
16
|
+
height: 16px;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: row;
|
|
19
|
+
justify-content: flex-start;
|
|
20
|
+
align-items: center;
|
|
21
|
+
|
|
22
|
+
svg {
|
|
23
|
+
width: 13px !important;
|
|
24
|
+
height: 13px !important;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&.warning {
|
|
29
|
+
background-color: var(--pte-colors-backgroundWarning);
|
|
30
|
+
color: var(--pte-colors-contentWarning);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
&.negative {
|
|
34
|
+
background-color: var(--pte-colors-backgroundNegative);
|
|
35
|
+
color: var(--pte-colors-contentNegative);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&.positive {
|
|
39
|
+
.icon {
|
|
40
|
+
color: var(--pte-colors-contentPositive);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { createElement } from 'react';
|
|
3
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
4
|
+
import { faCheck, faCancel, faWarning } from '@fortawesome/free-solid-svg-icons';
|
|
5
|
+
import { Callout } from './Callout';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Callout> = {
|
|
8
|
+
title: 'Surfaces/Callout',
|
|
9
|
+
component: Callout,
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
type Story = StoryObj<typeof Callout>;
|
|
15
|
+
|
|
16
|
+
export const Default: Story = {
|
|
17
|
+
args: {
|
|
18
|
+
children: 'Receipts required for transactions over $25',
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const Negative: Story = {
|
|
23
|
+
args: {
|
|
24
|
+
variant: 'negative',
|
|
25
|
+
children: 'Please upload a receipt',
|
|
26
|
+
icon: createElement(
|
|
27
|
+
FontAwesomeIcon,
|
|
28
|
+
{ icon: faCancel },
|
|
29
|
+
),
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const Warning: Story = {
|
|
34
|
+
args: {
|
|
35
|
+
variant: 'warning',
|
|
36
|
+
children: 'Receipt does not match transaction',
|
|
37
|
+
icon: createElement(
|
|
38
|
+
FontAwesomeIcon,
|
|
39
|
+
{ icon: faWarning },
|
|
40
|
+
),
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const Positive: Story = {
|
|
45
|
+
args: {
|
|
46
|
+
variant: 'positive',
|
|
47
|
+
children: 'Receipt saved',
|
|
48
|
+
icon: createElement(
|
|
49
|
+
FontAwesomeIcon,
|
|
50
|
+
{ icon: faCheck },
|
|
51
|
+
),
|
|
52
|
+
},
|
|
53
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { FC, ReactElement, ReactNode } from 'react';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import styles from './Callout.module.scss';
|
|
4
|
+
import { RemoveFromDOM, TextWhenString } from '../utility';
|
|
5
|
+
|
|
6
|
+
export type CalloutProps = {
|
|
7
|
+
/** The variant of the Callout. */
|
|
8
|
+
variant?: 'default' | 'warning' | 'positive' | 'negative';
|
|
9
|
+
/** An icon to display in the Callout. For best results, use an SVG icon with fill set to `currentColor`. */
|
|
10
|
+
icon?: ReactElement;
|
|
11
|
+
/** The contents of the Callout. */
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A Callout component.
|
|
17
|
+
*
|
|
18
|
+
* <hr />
|
|
19
|
+
*
|
|
20
|
+
* To use this component, import it as follows:
|
|
21
|
+
*
|
|
22
|
+
* ```js
|
|
23
|
+
* import { Callout } from 'paris/callout';
|
|
24
|
+
* ```
|
|
25
|
+
* @constructor
|
|
26
|
+
*/
|
|
27
|
+
export const Callout: FC<CalloutProps> = ({
|
|
28
|
+
variant = 'default',
|
|
29
|
+
icon,
|
|
30
|
+
children,
|
|
31
|
+
}) => (
|
|
32
|
+
<div className={clsx(styles.content, styles[variant])}>
|
|
33
|
+
<RemoveFromDOM when={!icon}>
|
|
34
|
+
<div className={styles.icon}>
|
|
35
|
+
{icon}
|
|
36
|
+
</div>
|
|
37
|
+
</RemoveFromDOM>
|
|
38
|
+
<TextWhenString as="p" kind="paragraphXSmall">
|
|
39
|
+
{children}
|
|
40
|
+
</TextWhenString>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Callout';
|
|
@@ -7,7 +7,7 @@ import { useId, useState } from 'react';
|
|
|
7
7
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
8
8
|
import { faClose } from '@fortawesome/free-solid-svg-icons';
|
|
9
9
|
import inputStyles from '../input/Input.module.scss';
|
|
10
|
-
import dropdownStyles from '../
|
|
10
|
+
import dropdownStyles from '../utility/Dropdown.module.scss';
|
|
11
11
|
import styles from '../select/Select.module.scss';
|
|
12
12
|
import type { TextProps } from '../text';
|
|
13
13
|
import { Text } from '../text';
|