@soul-refiner/design-system 0.1.18 → 0.1.20

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 (33) hide show
  1. package/package.json +1 -1
  2. package/src/common/date.ts +30 -0
  3. package/src/common/index.ts +7 -0
  4. package/src/common/styles.ts +2 -2
  5. package/src/components/actions/rate/rate.tsx +1 -1
  6. package/src/components/atoms/box/box.tsx +1 -0
  7. package/src/components/atoms/typography/heading.tsx +1 -1
  8. package/src/components/atoms/typography/paragraph.tsx +0 -1
  9. package/src/components/card/action/action.tsx +106 -0
  10. package/src/components/card/action/index.ts +2 -0
  11. package/src/components/card/audio/audio.tsx +175 -0
  12. package/src/components/card/audio/index.ts +2 -0
  13. package/src/components/card/card.tsx +49 -0
  14. package/src/components/card/feature/feature.tsx +248 -0
  15. package/src/components/card/feature/index.ts +2 -0
  16. package/src/components/card/index.ts +2 -0
  17. package/src/components/card/person/index.ts +2 -0
  18. package/src/components/card/person/person.tsx +96 -0
  19. package/src/components/card/price/index.ts +2 -0
  20. package/src/components/card/price/price.tsx +110 -0
  21. package/src/components/card/regular/index.ts +2 -0
  22. package/src/components/card/regular/regular.tsx +52 -0
  23. package/src/components/card/video/index.ts +2 -0
  24. package/src/components/card/video/video.tsx +167 -0
  25. package/src/components/card/written/index.ts +2 -0
  26. package/src/components/card/written/written.tsx +176 -0
  27. package/src/components/iconography/avatar/avatar.tsx +4 -1
  28. package/src/components/index.ts +1 -0
  29. package/src/components/input/date/date.tsx +694 -0
  30. package/src/components/input/date/index.ts +2 -0
  31. package/src/components/input/index.ts +2 -0
  32. package/src/utils/date.ts +118 -0
  33. package/src/utils/index.ts +13 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soul-refiner/design-system",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "main": "src/index.ts",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -0,0 +1,30 @@
1
+ export const THIS_YEAR = +new Date().getFullYear();
2
+
3
+ export const THIS_MONTH = +new Date().getMonth() + 1;
4
+
5
+ export enum WEEKDAYS {
6
+ MON = 'Mon',
7
+ TUE = 'Tue',
8
+ WED = 'Wed',
9
+ THU = 'Thu',
10
+ FRI = 'Fri',
11
+ SAT = 'Sat',
12
+ SUN = 'Sun',
13
+ }
14
+
15
+ export enum MONTHS {
16
+ JAN = 'Jan',
17
+ FEB = 'Feb',
18
+ MAR = 'Mar',
19
+ APR = 'Apr',
20
+ MAY = 'May',
21
+ JUN = 'Jun',
22
+ JUL = 'Jul',
23
+ AUG = 'Aug',
24
+ SEP = 'Sep',
25
+ OCT = 'Oct',
26
+ NOV = 'Nov',
27
+ DEC = 'Dec',
28
+ }
29
+
30
+ export const CALENDAR_WEEKS = 6;
@@ -1,2 +1,9 @@
1
1
  export { colorReference, COLORS } from './colors';
2
+ export {
3
+ CALENDAR_WEEKS,
4
+ MONTHS,
5
+ THIS_MONTH,
6
+ THIS_YEAR,
7
+ WEEKDAYS,
8
+ } from './date';
2
9
  export { Styles } from './styles';
@@ -61,11 +61,11 @@ export namespace Styles {
61
61
  export const padding = css<Padding>`
62
62
  padding: ${({ $padding }) =>
63
63
  $padding && typeof $padding === 'number'
64
- ? `${$padding}px`
64
+ ? `${$padding}px;`
65
65
  : $padding && typeof $padding === 'string' && $padding.includes('px')
66
66
  ? $padding
67
67
  : $padding && typeof $padding === 'string'
68
- ? `var(--${$padding})`
68
+ ? `var(--${$padding});`
69
69
  : '0px'};
70
70
  `;
71
71
 
@@ -84,5 +84,5 @@ const SVG = styled.svg<{
84
84
  $isRateable?: number;
85
85
  }>`
86
86
  fill: url(#MyGradient);
87
- cursor: pointer;
87
+ cursor: ${({ $isRateable }) => ($isRateable ? 'pointer' : 'initial')};
88
88
  `;
@@ -15,6 +15,7 @@ import {
15
15
  type NativeProps = Pick<
16
16
  React.HTMLAttributes<HTMLDivElement>,
17
17
  | 'children'
18
+ | 'className'
18
19
  | 'dangerouslySetInnerHTML'
19
20
  | 'onBlur'
20
21
  | 'onBlurCapture'
@@ -8,7 +8,7 @@ export interface CommonProps extends TypographyProps {
8
8
 
9
9
  export interface HeadingProps extends CommonProps {
10
10
  as?: React.ElementType;
11
- $fontSize?: 18 | 20 | 24 | 28 | 32 | 36 | 40 | 48;
11
+ $fontSize?: 16 | 18 | 20 | 24 | 28 | 32 | 36 | 40 | 48;
12
12
  $lineHeight?: 64 | 60 | 54 | 48 | 36 | 28 | 26;
13
13
  }
14
14
 
@@ -19,7 +19,6 @@ export const P = ({
19
19
  $fontSize = 16,
20
20
  $lineHeight = 24,
21
21
  $textAlign = 'left',
22
-
23
22
  ...rest
24
23
  }: ParagraphProps) => (
25
24
  <StyledP
@@ -0,0 +1,106 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import { Box, H, P } from 'components';
4
+
5
+ export interface ActionCardProps {
6
+ actions: React.ReactElement | React.ReactElement[];
7
+ content: string;
8
+ image: string;
9
+ logo: React.ReactElement;
10
+ subtitle: string;
11
+ sx?: string;
12
+ title: string;
13
+ }
14
+
15
+ export const ActionCard = ({
16
+ actions,
17
+ content,
18
+ image,
19
+ logo,
20
+ subtitle,
21
+ sx,
22
+ title,
23
+ }: ActionCardProps) => {
24
+ return (
25
+ <Action $backgroundImage={image} $sx={sx}>
26
+ <img src={image} alt="" />
27
+ <Figcaption>
28
+ <LogoWrapper>{logo}</LogoWrapper>
29
+ <H
30
+ as="span"
31
+ $mediumBold
32
+ $color="grey-2"
33
+ $fontSize={18}
34
+ $lineHeight={26}
35
+ >
36
+ {title}
37
+ </H>
38
+ <P as="span" $color="grey-4" $fontSize={12} $lineHeight={16}>
39
+ {subtitle}
40
+ </P>
41
+ <P
42
+ as="span"
43
+ $color="grey-3"
44
+ $fontSize={14}
45
+ $lineHeight={20}
46
+ $textAlign="center"
47
+ sx="margin-top: 12px;"
48
+ >
49
+ {content}
50
+ </P>
51
+ <Box
52
+ $display="flex"
53
+ $dir="row"
54
+ $gap={8}
55
+ sx="margin-top: 8px; width: 100%;"
56
+ >
57
+ {React.Children.toArray(actions)}
58
+ </Box>
59
+ </Figcaption>
60
+ </Action>
61
+ );
62
+ };
63
+
64
+ const Action = styled.figure<{
65
+ $backgroundImage: string;
66
+ $sx?: string;
67
+ }>`
68
+ position: relative;
69
+ aspect-ratio: 310 / 374;
70
+ border-radius: 16px;
71
+ max-width: 374px;
72
+ width: 100%;
73
+ margin: 0px;
74
+ display: flex;
75
+ flex-flow: column nowrap;
76
+ justify-content: flex-end;
77
+ box-shadow: var(--elevation-1);
78
+
79
+ img {
80
+ aspect-ratio: 310 / 160;
81
+ border-radius: 16px 16px 0px 0px;
82
+ }
83
+
84
+ ${({ $sx }) => ($sx ? $sx : '')};
85
+ `;
86
+
87
+ const Figcaption = styled.figcaption`
88
+ position: relative;
89
+ display: flex;
90
+ align-items: center;
91
+ flex-flow: column nowrap;
92
+ aspect-ratio: 310 / 198;
93
+ padding: 56px 16px 16px;
94
+ box-sizing: border-box;
95
+ background-color: var(--color-white);
96
+ border-radius: 0px 0px 15px 15px;
97
+ text-align: center;
98
+ `;
99
+
100
+ const LogoWrapper = styled.div`
101
+ position: absolute;
102
+ top: -28px;
103
+ right: calc(50% - 28px);
104
+ border: 4px solid var(--color-white);
105
+ border-radius: 50%;
106
+ `;
@@ -0,0 +1,2 @@
1
+ export { ActionCard } from './action';
2
+ export type { ActionCardProps } from './action';
@@ -0,0 +1,175 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import { Box, H, P, Rate } from 'components';
4
+
5
+ export interface AudioEpisodeCardProps {
6
+ actions: React.ReactElement[];
7
+ actionDropdown?: React.ReactElement;
8
+ content: string;
9
+ cta: React.ReactElement;
10
+ href: string;
11
+ image: string;
12
+ logo?: React.ReactElement;
13
+ numRating: number;
14
+ rating: number;
15
+ subtitle: string;
16
+ supportLabel?: string;
17
+ sx?: string;
18
+ title: string;
19
+ }
20
+
21
+ export const AudioEpisodeCard = ({
22
+ actions,
23
+ actionDropdown,
24
+ content,
25
+ cta,
26
+ href,
27
+ image,
28
+ logo,
29
+ numRating,
30
+ rating,
31
+ subtitle,
32
+ supportLabel,
33
+ sx,
34
+ title,
35
+ }: AudioEpisodeCardProps) => {
36
+ const hasLogo = !!logo;
37
+
38
+ return (
39
+ <Wrapper $sx={sx} href={href}>
40
+ <Figure>
41
+ {actionDropdown && <DropdownWrapper>{actionDropdown}</DropdownWrapper>}
42
+ <img className="thumbnail" src={image} alt="" />
43
+ <Figcaption>
44
+ <Box $display="block" className="hidden">
45
+ {cta}
46
+ </Box>
47
+ <Box $align="center" $dir="row" $gap={16}>
48
+ {logo}
49
+ <Box $dir="column">
50
+ <H $color="grey-2" $semiBold $fontSize={hasLogo ? 18 : 16}>
51
+ {title}
52
+ </H>
53
+ <P $color="grey-4" $fontSize={12} $lineHeight={16}>
54
+ {subtitle}
55
+ </P>
56
+ </Box>
57
+ {supportLabel && (
58
+ <P
59
+ className="not-hover"
60
+ $color="grey-4"
61
+ $fontSize={12}
62
+ $lineHeight={16}
63
+ sx="margin-left: auto; align-self: flex-end; margin-bottom: 2.5px;"
64
+ >
65
+ {supportLabel}
66
+ </P>
67
+ )}
68
+ </Box>
69
+ <P
70
+ $color="grey-5"
71
+ $fontSize={14}
72
+ $lineHeight={20}
73
+ $mediumBold
74
+ $textClampLines={6}
75
+ className="hidden inline"
76
+ >
77
+ {content}
78
+ </P>
79
+ <Box className="hidden" $align="center" $dir="row">
80
+ <Rate isRateable={false} percent={rating} size="small" />
81
+ <P
82
+ $color="grey-4"
83
+ $fontSize={12}
84
+ $lineHeight={16}
85
+ sx="margin: 0px auto 0px 4px;"
86
+ >
87
+ ({numRating})
88
+ </P>
89
+ <Box $align="center" $dir="row" $gap={8}>
90
+ {React.Children.toArray(actions)}
91
+ </Box>
92
+ </Box>
93
+ </Figcaption>
94
+ </Figure>
95
+ </Wrapper>
96
+ );
97
+ };
98
+
99
+ const Wrapper = styled.a<{ $sx?: string }>`
100
+ cursor: pointer;
101
+ display: block;
102
+ position: relative;
103
+ user-select: none;
104
+
105
+ figure {
106
+ max-width: 228px;
107
+ }
108
+
109
+ .hidden {
110
+ display: none;
111
+ }
112
+
113
+ &:hover {
114
+ .hidden {
115
+ display: flex;
116
+
117
+ &.inline {
118
+ display: -webkit-box;
119
+ }
120
+ }
121
+
122
+ .not-hover {
123
+ display: none;
124
+ }
125
+
126
+ .thumbnail {
127
+ border-radius: 16px 16px 0px 0px;
128
+ }
129
+
130
+ figure {
131
+ max-width: 280px;
132
+ aspect-ratio: 280 / 434;
133
+ box-shadow: var(--elevation-3);
134
+ background: linear-gradient(
135
+ 0deg,
136
+ #ffffff 50%,
137
+ rgba(255, 255, 255, 0.85) 55%,
138
+ rgba(255, 255, 255, 0.1) 80%
139
+ );
140
+ }
141
+
142
+ figcaption {
143
+ margin-top: -80px;
144
+ padding: 24px;
145
+ }
146
+ }
147
+
148
+ ${({ $sx }) => ($sx ? $sx : '')};
149
+ `;
150
+
151
+ const Figure = styled.figure`
152
+ margin: 0px;
153
+ border-radius: 16px;
154
+
155
+ .thumbnail {
156
+ border-radius: 16px;
157
+ position: relative;
158
+ z-index: -1;
159
+ width: 100%;
160
+ aspect-ratio: 228 / 228;
161
+ }
162
+ `;
163
+
164
+ const Figcaption = styled.figcaption`
165
+ display: flex;
166
+ flex-flow: column nowrap;
167
+ gap: 8px;
168
+ padding: 8px;
169
+ `;
170
+
171
+ const DropdownWrapper = styled.div`
172
+ position: absolute;
173
+ top: 8px;
174
+ right: 8px;
175
+ `;
@@ -0,0 +1,2 @@
1
+ export { AudioEpisodeCard } from './audio';
2
+ export type { AudioEpisodeCardProps } from './audio';
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import { ActionCard, ActionCardProps } from './action';
3
+ import { AudioEpisodeCard, AudioEpisodeCardProps } from './audio';
4
+ import { FeatureCard, FeatureCardProps } from './feature';
5
+ import { PersonCard, PersonCardProps } from './person';
6
+ import { PriceCard, PriceCardProps } from './price';
7
+ import { RegularCard, RegularCardProps } from './regular';
8
+ import { VideoEpisodeCard, VideoEpisodeCardProps } from './video';
9
+ import { WrittenEpisodeCard, WrittenEpisodeCardProps } from './written';
10
+
11
+ type CardType =
12
+ | 'action'
13
+ | 'audio-episode'
14
+ | 'feature'
15
+ | 'person'
16
+ | 'price'
17
+ | 'regular'
18
+ | 'video-episode'
19
+ | 'written-episode';
20
+
21
+ export type CardProps = (
22
+ | ActionCardProps
23
+ | AudioEpisodeCardProps
24
+ | FeatureCardProps
25
+ | PersonCardProps
26
+ | PriceCardProps
27
+ | RegularCardProps
28
+ | VideoEpisodeCardProps
29
+ | WrittenEpisodeCardProps
30
+ ) & {
31
+ type: CardType;
32
+ };
33
+
34
+ export const Card = ({ type = 'regular', ...rest }: CardProps) => {
35
+ const lookup: Record<CardType, React.ReactElement> = {
36
+ action: <ActionCard {...(rest as ActionCardProps)} />,
37
+ 'audio-episode': <AudioEpisodeCard {...(rest as AudioEpisodeCardProps)} />,
38
+ feature: <FeatureCard {...(rest as FeatureCardProps)} />,
39
+ person: <PersonCard {...(rest as PersonCardProps)} />,
40
+ price: <PriceCard {...(rest as PriceCardProps)} />,
41
+ regular: <RegularCard {...(rest as RegularCardProps)} />,
42
+ 'video-episode': <VideoEpisodeCard {...(rest as VideoEpisodeCardProps)} />,
43
+ 'written-episode': (
44
+ <WrittenEpisodeCard {...(rest as WrittenEpisodeCardProps)} />
45
+ ),
46
+ };
47
+
48
+ return lookup[type];
49
+ };
@@ -0,0 +1,248 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import { Box, Button, Modal } from 'components';
4
+ import { Camera, Checkmark, Close, Pencil } from 'icons';
5
+
6
+ export interface FeatureCardProps {
7
+ content: string;
8
+ editable?: boolean;
9
+ href?: string;
10
+ image: string;
11
+ onSave?: (title: string, content: string) => void;
12
+ sx?: string;
13
+ title: string;
14
+ uploadModal?: React.ReactElement;
15
+ }
16
+
17
+ export const FeatureCard = ({
18
+ content = '',
19
+ editable,
20
+ href,
21
+ image,
22
+ onSave,
23
+ sx,
24
+ title = '',
25
+ uploadModal,
26
+ }: FeatureCardProps) => {
27
+ const [editing, setEditing] = React.useState(false);
28
+ const [editingTitle, setTitle] = React.useState(() => title);
29
+ const [editingContent, setContent] = React.useState(() => content);
30
+
31
+ const handleSetEditing = () => setEditing(true);
32
+
33
+ const handleCancelEdit = () => {
34
+ setTitle(title);
35
+ setContent(content);
36
+ setEditing(false);
37
+ };
38
+
39
+ const handleSave = () => {
40
+ setEditing(false);
41
+ onSave && onSave(editingTitle, editingContent);
42
+ };
43
+
44
+ const handleTitleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
45
+ setTitle(event.target.value);
46
+ };
47
+
48
+ const handleContentChange = (event: React.ChangeEvent<HTMLInputElement>) => {
49
+ setContent(event.target.value);
50
+ };
51
+
52
+ const showEditIcon = !editing && editable;
53
+ const isLink = !!href;
54
+
55
+ return (
56
+ <Wrapper as={isLink ? 'div' : 'a'} href={href}>
57
+ <Figure $editing={editing} $isLink={isLink} $sx={sx}>
58
+ <img alt={title} src={image} />
59
+ {!editing && (
60
+ <Title>
61
+ {editingTitle}
62
+ <figure>
63
+ <Content>{editingContent}</Content>
64
+ </figure>
65
+ {showEditIcon && (
66
+ <Pencil className="edit" onClick={handleSetEditing} />
67
+ )}
68
+ {editable && uploadModal && (
69
+ <Modal>
70
+ <Modal.Trigger>
71
+ <Button
72
+ icon={<Camera />}
73
+ type="outlined"
74
+ size="small"
75
+ sx="position: absolute; right: 8px; top: -72px;"
76
+ >
77
+ Edit
78
+ </Button>
79
+ </Modal.Trigger>
80
+ <Modal.Content>{uploadModal}</Modal.Content>
81
+ </Modal>
82
+ )}
83
+ </Title>
84
+ )}
85
+ {editing && (
86
+ <Box
87
+ $align="flex-end"
88
+ $display="flex"
89
+ $dir="column"
90
+ $gap={8}
91
+ sx="position: relative;"
92
+ >
93
+ <EditTitle onChange={handleTitleChange} value={editingTitle} />
94
+ <EditContent
95
+ onChange={handleContentChange}
96
+ value={editingContent}
97
+ />
98
+ <Box $gap={8}>
99
+ <Checkmark
100
+ onClick={handleSave}
101
+ sx={`
102
+ padding: 8px;
103
+ border-radius: 4px;
104
+ cursor: pointer;
105
+ background-color: var(--color-white);
106
+ box-shadow: var(--elevation-1);
107
+ &:hover {
108
+ background-color: var(--color-grey-10);
109
+ }
110
+ `}
111
+ />
112
+ <Close
113
+ onClick={handleCancelEdit}
114
+ sx={`
115
+ padding: 8px;
116
+ border-radius: 4px;
117
+ cursor: pointer;
118
+ background-color: var(--color-white);
119
+ box-shadow: var(--elevation-1);
120
+ &:hover {
121
+ background-color: var(--color-grey-10);
122
+ }
123
+ `}
124
+ />
125
+ </Box>
126
+ </Box>
127
+ )}
128
+ </Figure>
129
+ </Wrapper>
130
+ );
131
+ };
132
+
133
+ const Wrapper = styled.div`
134
+ text-decoration: none;
135
+ `;
136
+
137
+ const Figure = styled.figure<{
138
+ $editing: boolean;
139
+ $isLink: boolean;
140
+ $sx?: string;
141
+ }>`
142
+ max-width: 352px;
143
+ display: flex;
144
+ flex-flow: column nowrap;
145
+ gap: 16px;
146
+ padding: 16px;
147
+ margin: 0px;
148
+ border-radius: 16px;
149
+ box-shadow: var(--elevation-1);
150
+ border: 1px solid var(--color-white);
151
+
152
+ &:hover {
153
+ ${({ $isLink }) =>
154
+ $isLink
155
+ ? 'border: 1px solid var(--color-primary-blue-3); cursor: pointer;'
156
+ : 'cursor: arrow;'}
157
+
158
+ button {
159
+ display: flex;
160
+ }
161
+
162
+ .edit {
163
+ display: block;
164
+ }
165
+ }
166
+
167
+ img {
168
+ max-width: 352px;
169
+ aspect-ratio: 1/1;
170
+ }
171
+
172
+ figure {
173
+ display: flex;
174
+ flex-flow: column nowrap;
175
+ gap: 4px;
176
+ margin: 0px;
177
+ padding: 0px;
178
+ }
179
+
180
+ button {
181
+ display: none;
182
+ }
183
+
184
+ .edit {
185
+ position: absolute;
186
+ right: 0px;
187
+ top: 0px;
188
+ display: none;
189
+ padding: 8px;
190
+ border-radius: 50%;
191
+ cursor: pointer;
192
+ &:hover {
193
+ background-color: var(--color-grey-10);
194
+ }
195
+ }
196
+
197
+ ${({ $editing }) =>
198
+ $editing
199
+ ? `
200
+ background-color: var(--color-grey-9);`
201
+ : `
202
+ background-color: var(--color-white);`}
203
+
204
+ ${({ $sx }) => ($sx ? $sx : '')};
205
+ `;
206
+
207
+ const Title = styled.figcaption`
208
+ position: relative;
209
+ padding-right: 32px;
210
+ color: var(--color-grey-2);
211
+ font-size: 18px;
212
+ font-family: Inter;
213
+ font-weight: 500;
214
+ line-height: 26px;
215
+ letter-spacing: -0.252px;
216
+ user-select: none;
217
+ `;
218
+
219
+ const Content = styled.figcaption`
220
+ font-size: 14px;
221
+ font-family: Inter;
222
+ font-weight: 400;
223
+ line-height: 20px;
224
+ letter-spacing: -0.084px;
225
+ `;
226
+
227
+ const EditTitle = styled.input`
228
+ width: calc(100% - 24px);
229
+ padding: 12px;
230
+ background-color: var(--color-white);
231
+ font-size: 18px;
232
+ font-family: Inter;
233
+ font-weight: 500;
234
+ line-height: 26px;
235
+ letter-spacing: -0.252px;
236
+ border: none;
237
+ border-radius: 8px;
238
+ outline: none;
239
+ `;
240
+
241
+ const EditContent = styled(EditTitle)`
242
+ padding: 6px 12px;
243
+ font-size: 14px;
244
+ font-family: Inter;
245
+ font-weight: 400;
246
+ line-height: 20px;
247
+ letter-spacing: -0.084px;
248
+ `;
@@ -0,0 +1,2 @@
1
+ export { FeatureCard } from './feature';
2
+ export type { FeatureCardProps } from './feature';
@@ -0,0 +1,2 @@
1
+ export { Card } from './card';
2
+ export type { CardProps } from './card';
@@ -0,0 +1,2 @@
1
+ export { PersonCard } from './person';
2
+ export type { PersonCardProps } from './person';