paris 0.2.2 → 0.4.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 +37 -0
- package/README.md +61 -5
- package/package.json +27 -15
- package/src/helpers/renderEnhancer.tsx +21 -0
- package/src/pages/_app.tsx +1 -1
- package/src/pages/index.tsx +1 -1
- package/src/stories/Pagination.mdx +73 -0
- package/src/stories/Tokens.mdx +0 -8
- package/src/stories/button/Button.module.scss +23 -7
- package/src/stories/button/Button.stories.ts +17 -0
- package/src/stories/button/Button.tsx +80 -20
- package/src/stories/card/Card.module.scss +14 -0
- package/src/stories/card/Card.stories.ts +33 -0
- package/src/stories/card/Card.tsx +60 -0
- package/src/stories/card/index.ts +1 -0
- package/src/stories/checkbox/Checkbox.module.scss +57 -0
- package/src/stories/checkbox/Checkbox.stories.ts +27 -0
- package/src/stories/checkbox/Checkbox.tsx +58 -0
- package/src/stories/checkbox/index.ts +1 -0
- package/src/stories/combobox/Combobox.module.scss +5 -0
- package/src/stories/combobox/Combobox.stories.ts +84 -0
- package/src/stories/combobox/Combobox.tsx +264 -0
- package/src/stories/combobox/index.ts +1 -0
- package/src/stories/dialog/Dialog.module.scss +187 -0
- package/src/stories/dialog/Dialog.stories.tsx +70 -0
- package/src/stories/dialog/Dialog.tsx +279 -0
- package/src/stories/dialog/index.ts +1 -0
- package/src/stories/drawer/Drawer.module.scss +284 -0
- package/src/stories/drawer/Drawer.stories.tsx +94 -0
- package/src/stories/drawer/Drawer.tsx +339 -0
- package/src/stories/drawer/index.ts +1 -0
- package/src/stories/dropdown/Dropdown.module.scss +23 -0
- package/src/stories/field/Field.module.scss +5 -0
- package/src/stories/field/Field.stories.ts +32 -0
- package/src/stories/field/Field.tsx +106 -0
- package/src/stories/field/index.ts +1 -0
- package/src/stories/icon/ChevronLeft.tsx +11 -0
- package/src/stories/icon/ChevronRight.tsx +11 -0
- package/src/stories/icon/Close.tsx +11 -0
- package/src/stories/icon/Icon.module.scss +5 -0
- package/src/stories/icon/Icon.stories.ts +28 -0
- package/src/stories/icon/Icon.tsx +46 -0
- package/src/stories/icon/index.ts +4 -0
- package/src/stories/input/Input.module.scss +135 -0
- package/src/stories/input/Input.stories.ts +89 -0
- package/src/stories/input/Input.tsx +137 -0
- package/src/stories/input/index.ts +1 -0
- package/src/stories/pagination/index.ts +1 -0
- package/src/stories/pagination/usePagination.ts +106 -0
- package/src/stories/select/Select.module.scss +74 -0
- package/src/stories/select/Select.stories.ts +73 -0
- package/src/stories/select/Select.tsx +176 -0
- package/src/stories/select/index.ts +1 -0
- package/src/stories/text/Text.module.scss +1 -1
- package/src/stories/text/Text.tsx +36 -14
- package/src/stories/textarea/TextArea.stories.ts +19 -0
- package/src/stories/textarea/TextArea.tsx +120 -0
- package/src/stories/textarea/index.ts +1 -0
- package/src/stories/theme/global.scss +2 -0
- package/src/stories/theme/index.ts +1 -0
- package/src/stories/theme/themes.ts +126 -7
- package/src/{styles → stories/theme}/tw-preflight.css +3 -1
- package/src/stories/theme/util.scss +8 -0
- package/src/stories/tilt/Tilt.module.scss +1 -0
- package/src/stories/tilt/Tilt.stories.tsx +43 -0
- package/src/stories/tilt/Tilt.tsx +63 -0
- package/src/stories/tilt/index.ts +1 -0
- package/src/stories/utility/RemoveFromDOM.tsx +19 -0
- package/src/stories/utility/TextWhenString.tsx +28 -0
- package/src/stories/utility/VisuallyHidden.tsx +25 -0
- package/src/types/Enhancer.ts +3 -0
- package/src/styles/util.scss +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# paris
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0a23074: Allow rendering `Button`s as anchors by passing `href` prop
|
|
8
|
+
- 6e5ea2c: `Tilt` component
|
|
9
|
+
- 50ab5e3: `Icon` component for included icons
|
|
10
|
+
- 50ab5e3: `Drawer` component
|
|
11
|
+
- 6e5ea2c: `Card` component
|
|
12
|
+
- dcd9bb6: `Combobox` component
|
|
13
|
+
- 05903f0: `Checkbox` component
|
|
14
|
+
- b758949: `Dialog` component
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- bbe60be: `Field` component for generic form fields with a label and description
|
|
19
|
+
- 50ab5e3: Drawer pagination
|
|
20
|
+
- bbe60be: Add label/description for Select
|
|
21
|
+
- 50ab5e3: Glassmorphic Dialog variant
|
|
22
|
+
|
|
23
|
+
## 0.3.0
|
|
24
|
+
|
|
25
|
+
### Minor Changes
|
|
26
|
+
|
|
27
|
+
- eef2375: `TextArea` component
|
|
28
|
+
- 2f6cd01: `Input` component
|
|
29
|
+
- eef2375: `Select` component
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- b61e950: Use `clsx` for cleaner class names
|
|
34
|
+
- b61e950: Add `global.scss` file and update docs to reflect global styles import
|
|
35
|
+
- 16c9c3e: Update docs to specify transpile requirement
|
|
36
|
+
- 2f6cd01: Memoized enhancers
|
|
37
|
+
- 646fccf: Add `status` for `Input` for success/error styling
|
|
38
|
+
- b61e950: Have `Text` use the theme fontFamily instead of inheriting
|
|
39
|
+
|
|
3
40
|
## 0.2.2
|
|
4
41
|
|
|
5
42
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -10,17 +10,73 @@ Paris 1.x styling is heavily inspired by Uber's [Base Web](https://baseweb.desig
|
|
|
10
10
|
|
|
11
11
|
## Getting started
|
|
12
12
|
|
|
13
|
-
First, install Paris
|
|
13
|
+
First, install Paris in your project:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
pnpm i paris
|
|
16
|
+
pnpm i paris
|
|
17
17
|
# or
|
|
18
|
-
yarn add paris
|
|
18
|
+
yarn add paris
|
|
19
19
|
# or
|
|
20
|
-
npm i paris
|
|
20
|
+
npm i paris
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
You'll need to tell your bundler to transpile files from Paris. This is easy in Next.js 13.1+ with the `transpilePackages` option in `next.config.js`:
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
// next.config.js
|
|
27
|
+
module.exports = {
|
|
28
|
+
// ...
|
|
29
|
+
transpilePackages: ['paris'],
|
|
30
|
+
};
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
For older versions of Next.js, you can use a plugin like [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules). Instructions for other bundlers are coming soon.
|
|
34
|
+
|
|
35
|
+
You'll need to configure your bundler to support Sass/SCSS and SCSS modules. In Next.js, support for SCSS modules is built-in and can be enabled by simply installing `sass` as a dependency.
|
|
36
|
+
|
|
37
|
+
Paris uses `pte` (our theming engine) for powering theming and initial styles through CSS variables. You can use `generateCSS` or `generateThemeInjection` from `paris/theme` to generate the CSS variables and inject them into your app through either a `style` or `script` tag in your document head respectively. Either method supports SSR and server components, since the initial theme is static.
|
|
38
|
+
|
|
39
|
+
Additionally, you need to import the static global styles from `paris/theme/global.scss`.
|
|
40
|
+
|
|
41
|
+
For example, with the Next.js 13 app directory, you can do all of this in your root `layout.tsx` file:
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
// app/layout.tsx
|
|
45
|
+
import { generateCSS, generateThemeInjection, theme } from 'paris/theme';
|
|
46
|
+
|
|
47
|
+
// Import Paris's static global styling
|
|
48
|
+
import 'paris/theme/global.scss';
|
|
49
|
+
|
|
50
|
+
export default function RootLayout({
|
|
51
|
+
children,
|
|
52
|
+
}: {
|
|
53
|
+
children: React.ReactNode
|
|
54
|
+
}) {
|
|
55
|
+
return (
|
|
56
|
+
<html lang="en">
|
|
57
|
+
<head>
|
|
58
|
+
{/* Using a `style` tag (MUST have the id `pte-vars` and be in the document head) */}
|
|
59
|
+
<style
|
|
60
|
+
id="pte-vars"
|
|
61
|
+
dangerouslySetInnerHTML={{
|
|
62
|
+
__html: generateCSS(theme),
|
|
63
|
+
}}
|
|
64
|
+
/>
|
|
65
|
+
|
|
66
|
+
{/* Or, use a `script` tag (can be located anywhere, should be loaded as early as possible to avoid an unstyled flash) */}
|
|
67
|
+
<script
|
|
68
|
+
dangerouslySetInnerHTML={{
|
|
69
|
+
__html: generateThemeInjection(theme),
|
|
70
|
+
}}
|
|
71
|
+
/>
|
|
72
|
+
</head>
|
|
73
|
+
<body className={inter.className}>{children}</body>
|
|
74
|
+
</html>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Finally, map our types in your `.tsconfig.json` file (this is a temporary workaround that we'll fix soon):
|
|
24
80
|
|
|
25
81
|
```json
|
|
26
82
|
{
|
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.4.0",
|
|
6
6
|
"homepage": "https://paris.slingshot.fm",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
|
@@ -31,7 +31,20 @@
|
|
|
31
31
|
"exports": {
|
|
32
32
|
"./*": "./src/stories/*"
|
|
33
33
|
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@ariakit/react": "^0.2.3",
|
|
36
|
+
"@headlessui/react": "^1.7.14",
|
|
37
|
+
"@radix-ui/react-checkbox": "^1.0.4",
|
|
38
|
+
"clsx": "^1.2.1",
|
|
39
|
+
"pte": "^0.4.9",
|
|
40
|
+
"react-parallax-tilt": "^1.7.144",
|
|
41
|
+
"ts-deepmerge": "^6.0.3"
|
|
42
|
+
},
|
|
34
43
|
"peerDependencies": {
|
|
44
|
+
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
|
45
|
+
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
|
46
|
+
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
|
47
|
+
"@fortawesome/react-fontawesome": "^0.2.0",
|
|
35
48
|
"react": "^18.x || ^17.x",
|
|
36
49
|
"react-dom": "^18.x || ^17.x",
|
|
37
50
|
"sass": "^1.x",
|
|
@@ -41,23 +54,25 @@
|
|
|
41
54
|
"@changesets/cli": "^2.26.1",
|
|
42
55
|
"@ssh/csstypes": "^1.1.0",
|
|
43
56
|
"@ssh/eslint-config": "^1.0.0",
|
|
44
|
-
"@storybook/addon-essentials": "^7.0.
|
|
45
|
-
"@storybook/addon-interactions": "^7.0.
|
|
46
|
-
"@storybook/addon-links": "^7.0.
|
|
47
|
-
"@storybook/
|
|
48
|
-
"@storybook/
|
|
49
|
-
"@storybook/
|
|
50
|
-
"@storybook/
|
|
57
|
+
"@storybook/addon-essentials": "^7.0.18",
|
|
58
|
+
"@storybook/addon-interactions": "^7.0.18",
|
|
59
|
+
"@storybook/addon-links": "^7.0.18",
|
|
60
|
+
"@storybook/addon-mdx-gfm": "^7.0.18",
|
|
61
|
+
"@storybook/addon-styling": "^1.0.8",
|
|
62
|
+
"@storybook/blocks": "^7.0.18",
|
|
63
|
+
"@storybook/manager-api": "^7.0.18",
|
|
64
|
+
"@storybook/nextjs": "^7.0.18",
|
|
65
|
+
"@storybook/react": "^7.0.18",
|
|
51
66
|
"@storybook/testing-library": "^0.1.0",
|
|
52
|
-
"@storybook/theming": "^7.0.
|
|
67
|
+
"@storybook/theming": "^7.0.18",
|
|
53
68
|
"@types/node": "18.15.11",
|
|
54
69
|
"@types/react": "18.0.31",
|
|
55
70
|
"@types/react-dom": "18.0.11",
|
|
56
71
|
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
|
57
72
|
"@typescript-eslint/parser": "^5.0.0",
|
|
73
|
+
"autoprefixer": "^10.4.14",
|
|
58
74
|
"change-case": "^4.1.2",
|
|
59
75
|
"csstype": "^3.1.2",
|
|
60
|
-
"esbuild-scss-modules-plugin": "^1.1.1",
|
|
61
76
|
"eslint": "^8.2.0",
|
|
62
77
|
"eslint-config-next": "13.4.2",
|
|
63
78
|
"eslint-plugin-css": "^0.8.0",
|
|
@@ -66,21 +81,18 @@
|
|
|
66
81
|
"jss": "^10.10.0",
|
|
67
82
|
"jss-preset-default": "^10.10.0",
|
|
68
83
|
"next": "^13.4.2",
|
|
84
|
+
"postcss": "^8.4.23",
|
|
69
85
|
"react": "^18.2.0",
|
|
70
86
|
"react-dom": "^18.2.0",
|
|
71
87
|
"sass": "^1.62.1",
|
|
72
|
-
"storybook": "^7.0.
|
|
88
|
+
"storybook": "^7.0.18",
|
|
73
89
|
"storybook-dark-mode": "^3.0.0",
|
|
74
90
|
"title-case": "^3.0.3",
|
|
75
91
|
"ts-node": "^10.9.1",
|
|
76
92
|
"tsconfig-paths-webpack-plugin": "^4.0.1",
|
|
77
|
-
"tsup": "^6.7.0",
|
|
78
93
|
"type-fest": "^3.10.0",
|
|
79
94
|
"typescript": "^5.0.2"
|
|
80
95
|
},
|
|
81
|
-
"dependencies": {
|
|
82
|
-
"pte": "^0.4.7"
|
|
83
|
-
},
|
|
84
96
|
"scripts": {
|
|
85
97
|
"dev": "next dev",
|
|
86
98
|
"build": "next build",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { isValidElement, memo } from 'react';
|
|
3
|
+
import type { Enhancer } from '../types/Enhancer';
|
|
4
|
+
|
|
5
|
+
export const renderEnhancer = (enhancer: Enhancer, size: number): ReactNode => {
|
|
6
|
+
if (isValidElement(enhancer)) {
|
|
7
|
+
return enhancer;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (typeof enhancer === 'function') {
|
|
11
|
+
return enhancer({ size });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return enhancer;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const MemoizedEnhancer = memo<{ enhancer: Enhancer, size: number }>(({ enhancer, size }) => (
|
|
18
|
+
<>
|
|
19
|
+
{ renderEnhancer(enhancer, size)}
|
|
20
|
+
</>
|
|
21
|
+
), (prev, next) => prev.enhancer === next.enhancer && prev.size === next.size);
|
package/src/pages/_app.tsx
CHANGED
package/src/pages/index.tsx
CHANGED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Meta } from '@storybook/blocks';
|
|
2
|
+
|
|
3
|
+
<Meta title="Surfaces/Pagination" />
|
|
4
|
+
|
|
5
|
+
# Paginating Drawers
|
|
6
|
+
|
|
7
|
+
Paris contains a mechanism for creating pagination within a single surface. This is useful for complex or multi-step processes that would otherwise require a lot of scrolling or complicate the UI.
|
|
8
|
+
|
|
9
|
+
> This API currently supports `Drawer`, and will soon be expanded to support pagination of any surface.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Paris provides a hook for pagination called `usePagination`. This hook functions as a router that allows you to control pagination state and navigate between pages.
|
|
14
|
+
|
|
15
|
+
To enable pagination:
|
|
16
|
+
|
|
17
|
+
1. Instantiate a pagination router using `usePagination`.
|
|
18
|
+
2. Pass the pagination object to the Drawer using the `pagination` prop.
|
|
19
|
+
3. Pass your pages as children to the Drawer. Each must have a key that is unique across all pages, which can then be passed to `pagination.open` to navigate to that page.
|
|
20
|
+
|
|
21
|
+
The pagination object returned by `usePagination` has the following properties:
|
|
22
|
+
|
|
23
|
+
- `open`: A function that takes a page key and navigates to that page.
|
|
24
|
+
- `currentPage`: The key of the current page.
|
|
25
|
+
- `history`: An array of page keys that are currently in the navigable history stack.
|
|
26
|
+
- `back`: A function that navigates to the previous page.
|
|
27
|
+
- `forward`: A function that navigates to the next page.
|
|
28
|
+
- `canGoBack`: A boolean indicating whether the user can go back.
|
|
29
|
+
- `canGoForward`: A boolean indicating whether the user can go forward.
|
|
30
|
+
|
|
31
|
+
The hook creates a shared state object between your parent component and the drawer. This means that you can use the `pagination` object's methods to control the drawer from your parent component, or from pages passed to the drawer.
|
|
32
|
+
|
|
33
|
+
## Example
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { Drawer } from 'paris/drawer';
|
|
37
|
+
import { Button } from 'paris/button';
|
|
38
|
+
import { usePagination } from 'paris/pagination';
|
|
39
|
+
|
|
40
|
+
const MyComponent = () => {
|
|
41
|
+
const pages = ['page1', 'page2', 'page3'] as const;
|
|
42
|
+
|
|
43
|
+
const pagination = usePagination<typeof pages>('step1');
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Drawer
|
|
47
|
+
title="User creation flow"
|
|
48
|
+
open={true}
|
|
49
|
+
onClose={() => {}}
|
|
50
|
+
// Pass the entire pagination object to the Drawer to enable pagination
|
|
51
|
+
pagination={pagination}
|
|
52
|
+
>
|
|
53
|
+
<div key={pages[0]}>
|
|
54
|
+
Page 1
|
|
55
|
+
<Button
|
|
56
|
+
onClick={() => {
|
|
57
|
+
// Navigate to the next page using `open`
|
|
58
|
+
pagination.open('page2');
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
Next Page
|
|
62
|
+
</Button>
|
|
63
|
+
</div>
|
|
64
|
+
<div key={pages[1]}>
|
|
65
|
+
Page 2
|
|
66
|
+
</div>
|
|
67
|
+
<div key={pages[2]}>
|
|
68
|
+
Page 3
|
|
69
|
+
</div>
|
|
70
|
+
</Drawer>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
```
|
package/src/stories/Tokens.mdx
CHANGED
|
@@ -44,11 +44,3 @@ Tokens are the smallest pieces of design language that can be used to build comp
|
|
|
44
44
|
))}
|
|
45
45
|
</div>
|
|
46
46
|
|
|
47
|
-
<br />
|
|
48
|
-
|
|
49
|
-
## Typography
|
|
50
|
-
|
|
51
|
-
<div>
|
|
52
|
-
<pre style={{ color: pvar('colors.contentPrimary') }}>{JSON.stringify(LightTheme.typography, null, 4).replaceAll('"', '').replaceAll(',', '\n').replaceAll('{', '\n').replaceAll('}', '\n').replaceAll('\\', '')}</pre>
|
|
53
|
-
</div>
|
|
54
|
-
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/*
|
|
2
|
+
* Main styles
|
|
3
|
+
*/
|
|
2
4
|
.button {
|
|
3
|
-
user-select:
|
|
5
|
+
user-select: var(--pte-utils-defaultUserSelect);
|
|
4
6
|
|
|
5
7
|
border: 1px solid;
|
|
6
8
|
|
|
@@ -12,17 +14,21 @@
|
|
|
12
14
|
|
|
13
15
|
transition: var(--pte-animations-interaction);
|
|
14
16
|
|
|
17
|
+
& > span {
|
|
18
|
+
margin-block-end: var(--pte-typography-verticalMetricsAdjust);
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
&:hover {
|
|
16
22
|
cursor: default;
|
|
17
23
|
background-color: var(--pte-colors-backgroundInverseTertiary);
|
|
18
24
|
}
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
&[aria-disabled=false]:active {
|
|
21
27
|
opacity: 0.9;
|
|
22
28
|
transform: scale(0.98);
|
|
23
29
|
}
|
|
24
30
|
|
|
25
|
-
&[disabled] {
|
|
31
|
+
&[aria-disabled=true] {
|
|
26
32
|
pointer-events: none;
|
|
27
33
|
}
|
|
28
34
|
}
|
|
@@ -41,7 +47,7 @@
|
|
|
41
47
|
border-color: var(--pte-colors-backgroundInverseTertiary);
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
&[disabled] {
|
|
50
|
+
&[aria-disabled=true] {
|
|
45
51
|
color: var(--pte-colors-contentDisabled);
|
|
46
52
|
background-color: var(--pte-colors-backgroundTertiary);
|
|
47
53
|
border-color: var(--pte-colors-backgroundTertiary);
|
|
@@ -53,7 +59,7 @@
|
|
|
53
59
|
background-color: transparent;
|
|
54
60
|
border-color: var(--pte-colors-contentPrimary);
|
|
55
61
|
|
|
56
|
-
&[disabled] {
|
|
62
|
+
&[aria-disabled=true] {
|
|
57
63
|
color: var(--pte-colors-contentDisabled);
|
|
58
64
|
border-color: var(--pte-colors-contentDisabled);
|
|
59
65
|
}
|
|
@@ -70,7 +76,7 @@
|
|
|
70
76
|
background-color: var(--pte-colors-backgroundTertiary);
|
|
71
77
|
}
|
|
72
78
|
|
|
73
|
-
&[disabled] {
|
|
79
|
+
&[aria-disabled=true] {
|
|
74
80
|
color: var(--pte-colors-contentDisabled);
|
|
75
81
|
}
|
|
76
82
|
}
|
|
@@ -90,6 +96,11 @@
|
|
|
90
96
|
padding: 4px 15px;
|
|
91
97
|
}
|
|
92
98
|
|
|
99
|
+
.xs {
|
|
100
|
+
height: 16px;
|
|
101
|
+
padding: 2px 8px;
|
|
102
|
+
}
|
|
103
|
+
|
|
93
104
|
|
|
94
105
|
/*
|
|
95
106
|
* SHAPE
|
|
@@ -125,4 +136,9 @@
|
|
|
125
136
|
width: 20px;
|
|
126
137
|
padding: 4px;
|
|
127
138
|
}
|
|
139
|
+
|
|
140
|
+
&.xs {
|
|
141
|
+
width: 16px;
|
|
142
|
+
padding: 2px;
|
|
143
|
+
}
|
|
128
144
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { createElement } from 'react';
|
|
3
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
4
|
+
import { faPlus } from '@fortawesome/free-solid-svg-icons';
|
|
2
5
|
import { Button } from './Button';
|
|
3
6
|
|
|
4
7
|
const meta: Meta<typeof Button> = {
|
|
@@ -40,3 +43,17 @@ export const Tertiary: Story = {
|
|
|
40
43
|
kind: 'tertiary',
|
|
41
44
|
},
|
|
42
45
|
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Enhancers can be used to add icons at the start (left) or end (right) of a button.
|
|
49
|
+
*/
|
|
50
|
+
export const WithEnhancer: Story = {
|
|
51
|
+
args: {
|
|
52
|
+
children: 'Button',
|
|
53
|
+
kind: 'primary',
|
|
54
|
+
startEnhancer: ({ size }) => createElement(FontAwesomeIcon, {
|
|
55
|
+
icon: faPlus,
|
|
56
|
+
width: `${size}px`,
|
|
57
|
+
}),
|
|
58
|
+
},
|
|
59
|
+
};
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
-
|
|
4
|
+
FC, HTMLAttributeAnchorTarget, MouseEventHandler, ReactNode,
|
|
5
5
|
} from 'react';
|
|
6
|
+
import type { ButtonProps as AriaButtonProps } from '@ariakit/react';
|
|
7
|
+
import { Button as AriaButton } from '@ariakit/react';
|
|
8
|
+
import clsx from 'clsx';
|
|
6
9
|
import styles from './Button.module.scss';
|
|
7
10
|
import { Text } from '../text';
|
|
11
|
+
import type { Enhancer } from '../../types/Enhancer';
|
|
12
|
+
import { MemoizedEnhancer, renderEnhancer } from '../../helpers/renderEnhancer';
|
|
13
|
+
|
|
14
|
+
const EnhancerSizes = {
|
|
15
|
+
large: 13,
|
|
16
|
+
small: 11,
|
|
17
|
+
xs: 9,
|
|
18
|
+
};
|
|
8
19
|
|
|
9
20
|
export type ButtonProps = {
|
|
10
21
|
/**
|
|
@@ -16,7 +27,7 @@ export type ButtonProps = {
|
|
|
16
27
|
* The size of the Button.
|
|
17
28
|
* @default large
|
|
18
29
|
*/
|
|
19
|
-
size?: 'large' | 'small';
|
|
30
|
+
size?: 'large' | 'small' | 'xs';
|
|
20
31
|
/**
|
|
21
32
|
* The shape of the Button.
|
|
22
33
|
* @default pill
|
|
@@ -27,11 +38,11 @@ export type ButtonProps = {
|
|
|
27
38
|
*
|
|
28
39
|
* When Button shape is `circle` or `square`, this element will be the only visible content.
|
|
29
40
|
*/
|
|
30
|
-
startEnhancer?:
|
|
41
|
+
startEnhancer?: Enhancer;
|
|
31
42
|
/**
|
|
32
43
|
* An icon or other element to render after the Button's text. A `size` argument is passed that should be used to determine the width & height of the content displayed.
|
|
33
44
|
*/
|
|
34
|
-
endEnhancer?:
|
|
45
|
+
endEnhancer?: Enhancer;
|
|
35
46
|
/**
|
|
36
47
|
* Disables the Button, disallowing user interaction.
|
|
37
48
|
* @default false
|
|
@@ -41,41 +52,90 @@ export type ButtonProps = {
|
|
|
41
52
|
* The interaction handler for the Button.
|
|
42
53
|
*/
|
|
43
54
|
onClick?: MouseEventHandler<HTMLButtonElement>;
|
|
55
|
+
/**
|
|
56
|
+
* Optionally, the Button can be rendered as an anchor element by passing an `href` prop. To use a Next.js Link component, use the `render` prop directly.
|
|
57
|
+
*/
|
|
58
|
+
href?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Optionally, the target of the anchor element can be specified (defaults to `_self`).
|
|
61
|
+
*/
|
|
62
|
+
hrefTarget?: HTMLAttributeAnchorTarget;
|
|
44
63
|
/**
|
|
45
64
|
* The contents of the Button.
|
|
46
65
|
*
|
|
47
66
|
* This should be text. When Button shape is `circle` or `square`, the action description should still be passed here for screen readers.
|
|
48
67
|
*/
|
|
49
68
|
children: string;
|
|
50
|
-
} & Omit<
|
|
69
|
+
} & Omit<AriaButtonProps, 'children' | 'disabled' | 'onClick'>;
|
|
51
70
|
|
|
52
71
|
/**
|
|
53
72
|
* A `Button` is used to trigger an action or event, such as submitting a form, opening a dialog, canceling an action, or performing a delete operation.
|
|
54
73
|
*
|
|
74
|
+
* <hr />
|
|
75
|
+
*
|
|
76
|
+
* To use the `Button` component, import it as follows:
|
|
77
|
+
*
|
|
78
|
+
* ```js
|
|
79
|
+
* import { Button } from 'paris/button';
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
55
82
|
* @constructor
|
|
56
83
|
*/
|
|
57
84
|
export const Button: FC<ButtonProps> = ({
|
|
58
|
-
kind,
|
|
59
|
-
size,
|
|
60
|
-
shape,
|
|
61
|
-
type,
|
|
85
|
+
kind = 'primary',
|
|
86
|
+
size = 'large',
|
|
87
|
+
shape = 'pill',
|
|
88
|
+
type = 'button',
|
|
62
89
|
startEnhancer,
|
|
63
90
|
endEnhancer,
|
|
64
91
|
onClick,
|
|
65
92
|
children,
|
|
93
|
+
disabled,
|
|
94
|
+
href,
|
|
66
95
|
...props
|
|
67
96
|
}) => (
|
|
68
|
-
<
|
|
69
|
-
className={`${props.className} ${styles[kind || 'primary']} ${styles[shape || 'pill']} ${styles[size || 'large']} ${styles.button}`}
|
|
70
|
-
type={type || 'button'}
|
|
71
|
-
aria-details={children}
|
|
72
|
-
onClick={onClick}
|
|
97
|
+
<AriaButton
|
|
73
98
|
{...props}
|
|
99
|
+
className={clsx(
|
|
100
|
+
styles.button,
|
|
101
|
+
styles[kind],
|
|
102
|
+
styles[shape],
|
|
103
|
+
styles[size],
|
|
104
|
+
props?.className,
|
|
105
|
+
)}
|
|
106
|
+
aria-disabled={disabled ?? false}
|
|
107
|
+
type={type}
|
|
108
|
+
aria-details={children}
|
|
109
|
+
onClick={!disabled && !href ? onClick : () => {}}
|
|
110
|
+
disabled={false}
|
|
111
|
+
{...href ? {
|
|
112
|
+
render: (properties) => (
|
|
113
|
+
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
|
114
|
+
<a
|
|
115
|
+
{...properties}
|
|
116
|
+
href={href}
|
|
117
|
+
target={props.hrefTarget ?? '_self'}
|
|
118
|
+
rel={props.hrefTarget === '_self' ? undefined : 'noreferrer'}
|
|
119
|
+
/>
|
|
120
|
+
),
|
|
121
|
+
} : {}}
|
|
74
122
|
>
|
|
75
|
-
{!!startEnhancer &&
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
123
|
+
{!!startEnhancer && (
|
|
124
|
+
<MemoizedEnhancer
|
|
125
|
+
enhancer={startEnhancer}
|
|
126
|
+
size={EnhancerSizes[size]}
|
|
127
|
+
/>
|
|
128
|
+
)}
|
|
129
|
+
{!['circle', 'square'].includes(shape) && (
|
|
130
|
+
<Text kind="labelXSmall">
|
|
131
|
+
{children || 'Button'}
|
|
132
|
+
</Text>
|
|
133
|
+
)}
|
|
134
|
+
{!!endEnhancer && (
|
|
135
|
+
<MemoizedEnhancer
|
|
136
|
+
enhancer={endEnhancer}
|
|
137
|
+
size={EnhancerSizes[size]}
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
</AriaButton>
|
|
81
141
|
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
.container {
|
|
2
|
+
border: 1px solid var(--pte-borders-dropdown-color);
|
|
3
|
+
border-radius: var(--pte-borders-radius-rounded);
|
|
4
|
+
box-shadow: var(--pte-lighting-subtlePopup);
|
|
5
|
+
|
|
6
|
+
user-select: var(--pte-utils-defaultUserSelect);
|
|
7
|
+
width: max-content;
|
|
8
|
+
padding: 16px;
|
|
9
|
+
|
|
10
|
+
&.flat {
|
|
11
|
+
box-shadow: none;
|
|
12
|
+
border-color: var(--pte-colors-borderOpaque);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Card } from './Card';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Card> = {
|
|
5
|
+
title: 'Surfaces/Card',
|
|
6
|
+
component: Card,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof Card>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* By default, the card has a shadow and parallax tilt effect. You may want to adjust the max tilt angles through the `overrides.tilt.tiltMaxAngle(X|Y)` props to provide a good experience depending on the size of the content. See the [Tilt component](/docs/surfaces-tilt--docs) for all available props.
|
|
15
|
+
*/
|
|
16
|
+
export const Default: Story = {
|
|
17
|
+
args: {
|
|
18
|
+
children: 'Revenue: $3000',
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Setting the `kind` prop to `flat` will remove the shadow from the card.
|
|
24
|
+
*
|
|
25
|
+
* This example also disables the `tilt` effect by setting the `tilt` prop to `false`, but by default the `tilt` prop will remain set to `true`.
|
|
26
|
+
*/
|
|
27
|
+
export const Flat: Story = {
|
|
28
|
+
args: {
|
|
29
|
+
kind: 'flat',
|
|
30
|
+
tilt: false,
|
|
31
|
+
children: 'Revenue: $3000',
|
|
32
|
+
},
|
|
33
|
+
};
|