paris 0.4.4 → 0.5.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 +17 -0
- package/LICENSE +21 -0
- package/README.md +10 -0
- package/package.json +17 -15
- package/src/stories/table/Table.module.scss +41 -0
- package/src/stories/table/Table.stories.ts +47 -0
- package/src/stories/table/Table.tsx +148 -0
- package/src/stories/table/index.ts +1 -0
- package/src/stories/tabs/Tabs.module.scss +61 -0
- package/src/stories/tabs/Tabs.stories.ts +21 -0
- package/src/stories/tabs/Tabs.tsx +80 -0
- package/src/stories/tabs/index.ts +1 -0
- package/src/stories/text/Text.module.scss +6 -6
- package/src/stories/text/Typography.module.css +9 -0
- package/src/stories/theme/themes.ts +16 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# paris
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8de0fe5: Table component
|
|
8
|
+
- 73fb594: Tabs component
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- 1f40b98: Text: add `!important` to ensure `weight` prop overrides other styles
|
|
13
|
+
|
|
14
|
+
## 0.4.5
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- ecfd77e: Text: change fontWeights capitalizations
|
|
19
|
+
|
|
3
20
|
## 0.4.4
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Slingshot Media, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -20,6 +20,16 @@ yarn add paris
|
|
|
20
20
|
npm i paris
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
Make sure your `tsconfig` module resolution is set to `nodenext`, `node16`, or `bundler` to support `paris` imports:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"compilerOptions": {
|
|
28
|
+
"moduleResolution": "bundler"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
23
33
|
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
34
|
|
|
25
35
|
```js
|
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.5.0",
|
|
6
6
|
"homepage": "https://paris.slingshot.fm",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
"./input": "./src/stories/input/index.ts",
|
|
43
43
|
"./pagination": "./src/stories/pagination/index.ts",
|
|
44
44
|
"./select": "./src/stories/select/index.ts",
|
|
45
|
+
"./table": "./src/stories/table/index.ts",
|
|
46
|
+
"./tabs": "./src/stories/tabs/index.ts",
|
|
45
47
|
"./text": "./src/stories/text/index.ts",
|
|
46
48
|
"./textarea": "./src/stories/textarea/index.ts",
|
|
47
49
|
"./theme": "./src/stories/theme/index.ts",
|
|
@@ -71,17 +73,17 @@
|
|
|
71
73
|
"@changesets/cli": "^2.26.1",
|
|
72
74
|
"@ssh/csstypes": "^1.1.0",
|
|
73
75
|
"@ssh/eslint-config": "^1.0.0",
|
|
74
|
-
"@storybook/addon-essentials": "^7.
|
|
75
|
-
"@storybook/addon-interactions": "^7.
|
|
76
|
-
"@storybook/addon-links": "^7.
|
|
77
|
-
"@storybook/addon-mdx-gfm": "^7.
|
|
78
|
-
"@storybook/addon-styling": "^1.
|
|
79
|
-
"@storybook/blocks": "^7.
|
|
80
|
-
"@storybook/manager-api": "^7.
|
|
81
|
-
"@storybook/nextjs": "^7.
|
|
82
|
-
"@storybook/react": "^7.
|
|
83
|
-
"@storybook/testing-library": "^0.
|
|
84
|
-
"@storybook/theming": "^7.
|
|
76
|
+
"@storybook/addon-essentials": "^7.5.1",
|
|
77
|
+
"@storybook/addon-interactions": "^7.5.1",
|
|
78
|
+
"@storybook/addon-links": "^7.5.1",
|
|
79
|
+
"@storybook/addon-mdx-gfm": "^7.5.1",
|
|
80
|
+
"@storybook/addon-styling": "^1.3.7",
|
|
81
|
+
"@storybook/blocks": "^7.5.1",
|
|
82
|
+
"@storybook/manager-api": "^7.5.1",
|
|
83
|
+
"@storybook/nextjs": "^7.5.1",
|
|
84
|
+
"@storybook/react": "^7.5.1",
|
|
85
|
+
"@storybook/testing-library": "^0.2.2",
|
|
86
|
+
"@storybook/theming": "^7.5.1",
|
|
85
87
|
"@types/node": "18.15.11",
|
|
86
88
|
"@types/react": "18.0.31",
|
|
87
89
|
"@types/react-dom": "18.0.11",
|
|
@@ -93,7 +95,7 @@
|
|
|
93
95
|
"eslint": "^8.2.0",
|
|
94
96
|
"eslint-config-next": "13.4.2",
|
|
95
97
|
"eslint-plugin-css": "^0.8.0",
|
|
96
|
-
"eslint-plugin-storybook": "^0.6.
|
|
98
|
+
"eslint-plugin-storybook": "^0.6.15",
|
|
97
99
|
"husky": "^8.0.0",
|
|
98
100
|
"jss": "^10.10.0",
|
|
99
101
|
"jss-preset-default": "^10.10.0",
|
|
@@ -102,8 +104,8 @@
|
|
|
102
104
|
"react": "^18.2.0",
|
|
103
105
|
"react-dom": "^18.2.0",
|
|
104
106
|
"sass": "^1.62.1",
|
|
105
|
-
"storybook": "^7.
|
|
106
|
-
"storybook-dark-mode": "^3.0.
|
|
107
|
+
"storybook": "^7.5.1",
|
|
108
|
+
"storybook-dark-mode": "^3.0.1",
|
|
107
109
|
"title-case": "^3.0.3",
|
|
108
110
|
"ts-node": "^10.9.1",
|
|
109
111
|
"tsconfig-paths-webpack-plugin": "^4.0.1",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
.table {
|
|
2
|
+
width: 100%;
|
|
3
|
+
|
|
4
|
+
&, & * {
|
|
5
|
+
cursor: default;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
thead {
|
|
9
|
+
text-align: left;
|
|
10
|
+
user-select: none;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
th {
|
|
14
|
+
color: var(--pte-colors-contentSecondary);
|
|
15
|
+
padding: 10px 12px;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
td {
|
|
19
|
+
padding: 8px 12px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
tr {
|
|
23
|
+
border-top: 1px solid var(--pte-colors-borderOpaque);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
tr:last-child {
|
|
27
|
+
border-bottom: 1px solid var(--pte-colors-borderOpaque);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
tbody tr.clickable {
|
|
31
|
+
transition: background-color var(--pte-animations-interaction);
|
|
32
|
+
|
|
33
|
+
&:hover {
|
|
34
|
+
background-color: var(--pte-colors-backgroundSecondary);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&, & * {
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import type { TableProps } from './Table';
|
|
3
|
+
import { Table } from './Table';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Table> = {
|
|
6
|
+
title: 'Content/Table',
|
|
7
|
+
component: Table,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof Table>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {
|
|
15
|
+
args: {
|
|
16
|
+
headers: [
|
|
17
|
+
'Name',
|
|
18
|
+
'Type',
|
|
19
|
+
'Email',
|
|
20
|
+
],
|
|
21
|
+
rows: [
|
|
22
|
+
{
|
|
23
|
+
id: 1,
|
|
24
|
+
name: 'Billie Eilish',
|
|
25
|
+
type: 'Artist',
|
|
26
|
+
email: 'billie@slingshot.fm',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 2,
|
|
30
|
+
name: 'Taylor Swift',
|
|
31
|
+
type: 'Artist',
|
|
32
|
+
email: 'taylor@slingshot.fm',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 3,
|
|
36
|
+
name: 'Valkyrae',
|
|
37
|
+
type: 'Creator',
|
|
38
|
+
email: 'valkyrae@slingshot.fm',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
rowRenderFn: (row) => ({
|
|
42
|
+
key: `${row.id}`,
|
|
43
|
+
cells: [row.name, row.type, row.email],
|
|
44
|
+
}),
|
|
45
|
+
onRowClick: (row) => console.log('Row clicked', row),
|
|
46
|
+
} satisfies TableProps,
|
|
47
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef, FC, ReactNode } from 'react';
|
|
2
|
+
import { useId, useMemo } from 'react';
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
import styles from './Table.module.scss';
|
|
5
|
+
import typography from '../text/Typography.module.css';
|
|
6
|
+
|
|
7
|
+
export type TableLineData = [NonNullable<ReactNode>, ...ReactNode[]];
|
|
8
|
+
export type RowRenderData = {
|
|
9
|
+
key: string,
|
|
10
|
+
cells: TableLineData,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type TableProps<
|
|
14
|
+
RowData extends Record<string, any> = Record<string, any>,
|
|
15
|
+
HeaderNodeArray extends TableLineData = TableLineData,
|
|
16
|
+
> = {
|
|
17
|
+
/**
|
|
18
|
+
* The table headers. Must include at least one header.
|
|
19
|
+
*/
|
|
20
|
+
headers: HeaderNodeArray;
|
|
21
|
+
/**
|
|
22
|
+
* The table rows, as an array of objects.
|
|
23
|
+
*/
|
|
24
|
+
rows: RowData[];
|
|
25
|
+
/**
|
|
26
|
+
* A function that will be called for each row to compute the row's content. If omitted, the row will render the `Object.values` of the row data.
|
|
27
|
+
*
|
|
28
|
+
* The function should return an object containing a property named `key` for the row's React key (should be a unique id), and a property named `nodes` containing an array of React nodes to be rendered as cells in the row.
|
|
29
|
+
* @param row - The data for the row being rendered.
|
|
30
|
+
* @returns An object containing a property named `key` for the row's React key (should be a unique id), and a property named `nodes` containing an array of React nodes to be rendered as cells in the row.
|
|
31
|
+
* @see RowRenderData
|
|
32
|
+
*/
|
|
33
|
+
rowRenderFn?: (row: RowData) => RowRenderData;
|
|
34
|
+
/**
|
|
35
|
+
* A function that will be called when a row is clicked.
|
|
36
|
+
* @param row - The data for the row being clicked.
|
|
37
|
+
*/
|
|
38
|
+
onRowClick?: (row: RowData) => void | Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Whether the rows should be clickable.
|
|
41
|
+
*/
|
|
42
|
+
clickableRows?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Prop overrides for rendered elements.
|
|
45
|
+
*/
|
|
46
|
+
overrides?: {
|
|
47
|
+
table?: ComponentPropsWithoutRef<'table'>;
|
|
48
|
+
thead?: ComponentPropsWithoutRef<'thead'>;
|
|
49
|
+
tbody?: ComponentPropsWithoutRef<'tbody'>;
|
|
50
|
+
trHead?: ComponentPropsWithoutRef<'tr'>;
|
|
51
|
+
th?: ComponentPropsWithoutRef<'th'>;
|
|
52
|
+
trBody?: ComponentPropsWithoutRef<'tr'>;
|
|
53
|
+
td?: ComponentPropsWithoutRef<'td'>;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A Table component.
|
|
59
|
+
*
|
|
60
|
+
* <hr />
|
|
61
|
+
*
|
|
62
|
+
* To use this component, import it as follows:
|
|
63
|
+
*
|
|
64
|
+
* ```js
|
|
65
|
+
* import { Table } from 'paris/table';
|
|
66
|
+
* ```
|
|
67
|
+
* @constructor
|
|
68
|
+
*/
|
|
69
|
+
export const Table: FC<TableProps> = ({
|
|
70
|
+
headers,
|
|
71
|
+
rows,
|
|
72
|
+
rowRenderFn,
|
|
73
|
+
onRowClick,
|
|
74
|
+
clickableRows = true,
|
|
75
|
+
overrides,
|
|
76
|
+
}) => {
|
|
77
|
+
const id = useId();
|
|
78
|
+
const rowsRenderData = useMemo(() => (
|
|
79
|
+
rows.map(
|
|
80
|
+
rowRenderFn
|
|
81
|
+
?? ((row) => ({
|
|
82
|
+
key: Object.values(row).join('-'),
|
|
83
|
+
cells: Object.values(row),
|
|
84
|
+
})),
|
|
85
|
+
)
|
|
86
|
+
), [rows, rowRenderFn]);
|
|
87
|
+
|
|
88
|
+
const renderedRows = useMemo(() => (
|
|
89
|
+
rowsRenderData.map(({ key, cells }, index) => (
|
|
90
|
+
<tr
|
|
91
|
+
key={`${id}-row-${key}`}
|
|
92
|
+
onClick={() => {
|
|
93
|
+
if (clickableRows) onRowClick?.(rows[index]);
|
|
94
|
+
}}
|
|
95
|
+
{...overrides?.trBody}
|
|
96
|
+
className={clsx(
|
|
97
|
+
clickableRows && styles.clickable,
|
|
98
|
+
overrides?.trBody?.className,
|
|
99
|
+
)}
|
|
100
|
+
>
|
|
101
|
+
{cells.map((cell) => (
|
|
102
|
+
<td
|
|
103
|
+
key={`${id}-cell-${cell.toString()}`}
|
|
104
|
+
{...overrides?.td}
|
|
105
|
+
className={clsx(
|
|
106
|
+
typography.paragraphXSmall,
|
|
107
|
+
)}
|
|
108
|
+
>
|
|
109
|
+
{cell}
|
|
110
|
+
</td>
|
|
111
|
+
))}
|
|
112
|
+
</tr>
|
|
113
|
+
))
|
|
114
|
+
), [clickableRows, id, onRowClick, overrides?.td, overrides?.trBody, rows, rowsRenderData]);
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<table
|
|
118
|
+
{...{
|
|
119
|
+
...overrides?.table,
|
|
120
|
+
className: clsx(
|
|
121
|
+
styles.table,
|
|
122
|
+
overrides?.table?.className,
|
|
123
|
+
),
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
<thead {...overrides?.thead}>
|
|
127
|
+
<tr {...overrides?.trHead}>
|
|
128
|
+
{headers.map((header) => (
|
|
129
|
+
<th
|
|
130
|
+
{...{
|
|
131
|
+
...overrides?.th,
|
|
132
|
+
className: clsx(
|
|
133
|
+
typography.labelXSmall,
|
|
134
|
+
),
|
|
135
|
+
}}
|
|
136
|
+
key={`${id}-header-${header}`}
|
|
137
|
+
>
|
|
138
|
+
{header}
|
|
139
|
+
</th>
|
|
140
|
+
))}
|
|
141
|
+
</tr>
|
|
142
|
+
</thead>
|
|
143
|
+
<tbody {...overrides?.tbody}>
|
|
144
|
+
{renderedRows}
|
|
145
|
+
</tbody>
|
|
146
|
+
</table>
|
|
147
|
+
);
|
|
148
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Table';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
.tabList {
|
|
2
|
+
//border-bottom: 4px solid var(--pte-colors-borderOpaque);
|
|
3
|
+
padding-bottom: 4px;
|
|
4
|
+
overflow-x: scroll;
|
|
5
|
+
//overflow-y: visible;
|
|
6
|
+
position: relative;
|
|
7
|
+
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: row;
|
|
10
|
+
|
|
11
|
+
scrollbar-width: none;
|
|
12
|
+
&::-webkit-scrollbar {
|
|
13
|
+
display: none;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.tabListBorder {
|
|
18
|
+
border-bottom: 4px solid var(--pte-colors-borderOpaque);
|
|
19
|
+
width: 100%;
|
|
20
|
+
margin-top: -4px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.activeTabBorder {
|
|
24
|
+
position: absolute;
|
|
25
|
+
bottom: 0;
|
|
26
|
+
width: var(--tab-width);
|
|
27
|
+
height: 4px;
|
|
28
|
+
background-color: var(--pte-colors-contentPrimary);
|
|
29
|
+
|
|
30
|
+
transition: transform var(--pte-animations-duration-slow) var(--pte-animations-timing-easeInOutExpo);
|
|
31
|
+
|
|
32
|
+
//noinspection CssInvalidFunction
|
|
33
|
+
transform: translateX(calc(var(--tab-width) * var(--tab-index)));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.tab {
|
|
37
|
+
font-weight: 500;
|
|
38
|
+
padding: 9px 30px;
|
|
39
|
+
width: var(--tab-width);
|
|
40
|
+
flex-shrink: 0;
|
|
41
|
+
overflow: visible;
|
|
42
|
+
cursor: default;
|
|
43
|
+
|
|
44
|
+
transition: background-color var(--pte-animations-duration-normal) var(--pte-animations-timing-easeInOut);
|
|
45
|
+
|
|
46
|
+
&[aria-selected="true"] {
|
|
47
|
+
background-color: var(--pte-colors-backgroundSecondary);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&:hover {
|
|
51
|
+
background-color: var(--pte-colors-backgroundSecondary);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
&:active {
|
|
55
|
+
background-color: var(--pte-colors-backgroundTertiary);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.panel {
|
|
60
|
+
padding-top: 16px;
|
|
61
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Tabs } from './Tabs';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Tabs> = {
|
|
5
|
+
title: 'Surfaces/Tabs',
|
|
6
|
+
component: Tabs,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof Tabs>;
|
|
12
|
+
|
|
13
|
+
export const Default: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
tabs: [
|
|
16
|
+
{ title: 'Tab 1', content: 'Tab 1 content' },
|
|
17
|
+
{ title: 'Tab 2', content: 'Tab 2 content' },
|
|
18
|
+
{ title: 'Tab 3', content: 'Tab 3 content' },
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { CSSProperties, FC, ReactNode } from 'react';
|
|
4
|
+
import { Tab } from '@headlessui/react';
|
|
5
|
+
import { useId, useState } from 'react';
|
|
6
|
+
import clsx from 'clsx';
|
|
7
|
+
import type { CSSLength } from '@ssh/csstypes';
|
|
8
|
+
import styles from './Tabs.module.scss';
|
|
9
|
+
import typography from '../text/Typography.module.css';
|
|
10
|
+
|
|
11
|
+
export type TabsProps = {
|
|
12
|
+
tabs: {
|
|
13
|
+
title: string;
|
|
14
|
+
content: ReactNode;
|
|
15
|
+
}[];
|
|
16
|
+
tabWidth?: CSSLength;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A Tabs component.
|
|
21
|
+
*
|
|
22
|
+
* <hr />
|
|
23
|
+
*
|
|
24
|
+
* To use this component, import it as follows:
|
|
25
|
+
*
|
|
26
|
+
* ```js
|
|
27
|
+
* import { Tabs } from 'paris/tabs';
|
|
28
|
+
* ```
|
|
29
|
+
* @constructor
|
|
30
|
+
*/
|
|
31
|
+
export const Tabs: FC<TabsProps> = ({
|
|
32
|
+
tabs,
|
|
33
|
+
tabWidth = '150px',
|
|
34
|
+
}) => {
|
|
35
|
+
const id = useId();
|
|
36
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Tab.Group
|
|
40
|
+
selectedIndex={selectedIndex}
|
|
41
|
+
onChange={setSelectedIndex}
|
|
42
|
+
>
|
|
43
|
+
<Tab.List
|
|
44
|
+
style={{
|
|
45
|
+
'--tab-width': tabWidth,
|
|
46
|
+
'--tab-index': `${selectedIndex}`,
|
|
47
|
+
} as CSSProperties}
|
|
48
|
+
className={clsx(
|
|
49
|
+
styles.tabList,
|
|
50
|
+
)}
|
|
51
|
+
>
|
|
52
|
+
{tabs.map(({ title }) => (
|
|
53
|
+
<Tab
|
|
54
|
+
key={`${id}-tab-${title}`}
|
|
55
|
+
className={clsx(
|
|
56
|
+
typography.paragraphXSmall,
|
|
57
|
+
styles.tab,
|
|
58
|
+
)}
|
|
59
|
+
>
|
|
60
|
+
{title}
|
|
61
|
+
</Tab>
|
|
62
|
+
))}
|
|
63
|
+
|
|
64
|
+
<div key={`${id}-tab-active-border`} className={styles.activeTabBorder} />
|
|
65
|
+
</Tab.List>
|
|
66
|
+
<div className={styles.tabListBorder} />
|
|
67
|
+
|
|
68
|
+
<Tab.Panels>
|
|
69
|
+
{tabs.map(({ title, content }) => (
|
|
70
|
+
<Tab.Panel
|
|
71
|
+
key={`${id}-tab-${title}-content`}
|
|
72
|
+
className={styles.panel}
|
|
73
|
+
>
|
|
74
|
+
{content}
|
|
75
|
+
</Tab.Panel>
|
|
76
|
+
))}
|
|
77
|
+
</Tab.Panels>
|
|
78
|
+
</Tab.Group>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Tabs';
|
|
@@ -5,22 +5,22 @@
|
|
|
5
5
|
/* Font weight theme values */
|
|
6
6
|
$text-weights: (
|
|
7
7
|
"thin": var(--pte-typography-fontWeights-thin),
|
|
8
|
-
"
|
|
8
|
+
"extralight": var(--pte-typography-fontWeights-extralight),
|
|
9
9
|
"light": var(--pte-typography-fontWeights-light),
|
|
10
10
|
"normal": var(--pte-typography-fontWeights-normal),
|
|
11
11
|
"medium": var(--pte-typography-fontWeights-medium),
|
|
12
|
-
"
|
|
12
|
+
"semibold": var(--pte-typography-fontWeights-semibold),
|
|
13
13
|
"bold": var(--pte-typography-fontWeights-bold),
|
|
14
|
-
"
|
|
14
|
+
"extrabold": var(--pte-typography-fontWeights-extrabold),
|
|
15
15
|
"black": var(--pte-typography-fontWeights-black),
|
|
16
|
-
"
|
|
16
|
+
"extrablack": var(--pte-typography-fontWeights-extrablack),
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
@each $weight-name,
|
|
21
21
|
$weight-value in $text-weights {
|
|
22
22
|
.weight-#{$weight-name} {
|
|
23
|
-
font-weight: $weight-value;
|
|
23
|
+
font-weight: $weight-value !important;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -35,4 +35,4 @@ $style-value in $font-styles {
|
|
|
35
35
|
.fontStyle-#{$style-name} {
|
|
36
36
|
font-style: $style-value;
|
|
37
37
|
}
|
|
38
|
-
}
|
|
38
|
+
}
|
|
@@ -19,6 +19,15 @@
|
|
|
19
19
|
text-transform: var(--pte-typography-styles-displayMedium-textTransform);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
.display {
|
|
23
|
+
font-size: var(--pte-typography-styles-display-fontSize);
|
|
24
|
+
font-style: var(--pte-typography-styles-display-fontStyle);
|
|
25
|
+
font-weight: var(--pte-typography-styles-display-fontWeight);
|
|
26
|
+
line-height: var(--pte-typography-styles-display-lineHeight);
|
|
27
|
+
letter-spacing: var(--pte-typography-styles-display-letterSpacing);
|
|
28
|
+
text-transform: var(--pte-typography-styles-display-textTransform);
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
.displaySmall {
|
|
23
32
|
font-size: var(--pte-typography-styles-displaySmall-fontSize);
|
|
24
33
|
font-style: var(--pte-typography-styles-displaySmall-fontStyle);
|
|
@@ -40,6 +40,7 @@ const TimingFunctions: Omit<Theme['animations']['timing'], 'default'> = {
|
|
|
40
40
|
easeIn: 'cubic-bezier(0.42, 0.0, 1.0, 1.0)',
|
|
41
41
|
easeOutQuad: 'cubic-bezier(0.5, 1, 0.89, 1)',
|
|
42
42
|
easeInQuad: 'cubic-bezier(0.11, 0, 0.5, 0)',
|
|
43
|
+
easeInOutExpo: 'cubic-bezier(0.87, 0, 0.13, 1)',
|
|
43
44
|
};
|
|
44
45
|
|
|
45
46
|
export type Theme = {
|
|
@@ -124,15 +125,15 @@ export type Theme = {
|
|
|
124
125
|
|
|
125
126
|
fontWeights: {
|
|
126
127
|
thin: number,
|
|
127
|
-
|
|
128
|
+
extralight: number,
|
|
128
129
|
light: number,
|
|
129
130
|
normal: number,
|
|
130
131
|
medium: number,
|
|
131
|
-
|
|
132
|
+
semibold: number,
|
|
132
133
|
bold: number,
|
|
133
|
-
|
|
134
|
+
extrabold: number,
|
|
134
135
|
black: number,
|
|
135
|
-
|
|
136
|
+
extrablack: number,
|
|
136
137
|
},
|
|
137
138
|
|
|
138
139
|
fontStyles: {
|
|
@@ -145,6 +146,7 @@ export type Theme = {
|
|
|
145
146
|
|
|
146
147
|
displayLarge: FontDefinition,
|
|
147
148
|
displayMedium: FontDefinition,
|
|
149
|
+
display: FontDefinition,
|
|
148
150
|
displaySmall: FontDefinition,
|
|
149
151
|
|
|
150
152
|
// Heading
|
|
@@ -218,6 +220,7 @@ export type Theme = {
|
|
|
218
220
|
easeIn: TimingFunction,
|
|
219
221
|
easeOutQuad: TimingFunction,
|
|
220
222
|
easeInQuad: TimingFunction,
|
|
223
|
+
easeInOutExpo: TimingFunction,
|
|
221
224
|
default: TimingFunction,
|
|
222
225
|
},
|
|
223
226
|
duration: {
|
|
@@ -343,15 +346,15 @@ export const LightTheme: Theme = {
|
|
|
343
346
|
|
|
344
347
|
fontWeights: {
|
|
345
348
|
thin: 100,
|
|
346
|
-
|
|
349
|
+
extralight: 200,
|
|
347
350
|
light: 300,
|
|
348
351
|
normal: 400,
|
|
349
352
|
medium: 500,
|
|
350
|
-
|
|
353
|
+
semibold: 600,
|
|
351
354
|
bold: 700,
|
|
352
|
-
|
|
355
|
+
extrabold: 800,
|
|
353
356
|
black: 900,
|
|
354
|
-
|
|
357
|
+
extrablack: 950,
|
|
355
358
|
},
|
|
356
359
|
|
|
357
360
|
fontStyles: {
|
|
@@ -372,6 +375,11 @@ export const LightTheme: Theme = {
|
|
|
372
375
|
fontSize: '52px',
|
|
373
376
|
lineHeight: '62px',
|
|
374
377
|
},
|
|
378
|
+
display: {
|
|
379
|
+
...DisplayFontClass,
|
|
380
|
+
fontSize: '42px',
|
|
381
|
+
lineHeight: '50.4px',
|
|
382
|
+
},
|
|
375
383
|
displaySmall: {
|
|
376
384
|
...DisplayFontClass,
|
|
377
385
|
fontSize: '34px',
|