@coldsurf/ocean-road 1.13.2

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 (195) hide show
  1. package/dist/css/global.css +30 -0
  2. package/dist/index.d.ts +641 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +733 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/native.cjs +94 -0
  7. package/dist/native.cjs.map +1 -0
  8. package/dist/native.d.cts +304 -0
  9. package/dist/native.d.cts.map +1 -0
  10. package/dist/native.d.ts +304 -0
  11. package/dist/native.d.ts.map +1 -0
  12. package/dist/native.js +94 -0
  13. package/dist/native.js.map +1 -0
  14. package/dist/next.cjs +949 -0
  15. package/dist/next.cjs.map +1 -0
  16. package/dist/next.d.cts +270 -0
  17. package/dist/next.d.cts.map +1 -0
  18. package/dist/next.d.ts +270 -0
  19. package/dist/next.d.ts.map +1 -0
  20. package/dist/next.js +949 -0
  21. package/dist/next.js.map +1 -0
  22. package/native/index.d.ts +7 -0
  23. package/next/index.d.ts +7 -0
  24. package/package.json +126 -0
  25. package/src/GlobalStyle.tsx +111 -0
  26. package/src/base/badge/badge.tsx +50 -0
  27. package/src/base/badge/index.ts +1 -0
  28. package/src/base/button/button.styled.tsx +123 -0
  29. package/src/base/button/button.tsx +60 -0
  30. package/src/base/button/button.types.ts +20 -0
  31. package/src/base/button/button.utils.ts +36 -0
  32. package/src/base/button/index.tsx +2 -0
  33. package/src/base/checkbox/checkbox.styled.ts +52 -0
  34. package/src/base/checkbox/checkbox.tsx +26 -0
  35. package/src/base/checkbox/index.ts +1 -0
  36. package/src/base/icon-button/icon-button.styled.ts +8 -0
  37. package/src/base/icon-button/icon-button.tsx +15 -0
  38. package/src/base/icon-button/icon-button.types.ts +3 -0
  39. package/src/base/icon-button/index.ts +2 -0
  40. package/src/base/index.ts +11 -0
  41. package/src/base/label/index.ts +1 -0
  42. package/src/base/label/label.styled.ts +7 -0
  43. package/src/base/label/label.tsx +27 -0
  44. package/src/base/modal/index.ts +1 -0
  45. package/src/base/modal/modal.tsx +59 -0
  46. package/src/base/spinner/index.ts +2 -0
  47. package/src/base/spinner/spinner.styled.ts +25 -0
  48. package/src/base/spinner/spinner.tsx +36 -0
  49. package/src/base/spinner/spinner.types.ts +1 -0
  50. package/src/base/switch/index.ts +1 -0
  51. package/src/base/switch/switch.styled.tsx +49 -0
  52. package/src/base/switch/switch.tsx +29 -0
  53. package/src/base/text/index.ts +1 -0
  54. package/src/base/text/text.styled.ts +17 -0
  55. package/src/base/text/text.tsx +37 -0
  56. package/src/base/text-area/index.ts +2 -0
  57. package/src/base/text-area/text-area.styled.ts +16 -0
  58. package/src/base/text-area/text-area.tsx +29 -0
  59. package/src/base/text-area/text-area.types.ts +11 -0
  60. package/src/base/text-input/index.ts +2 -0
  61. package/src/base/text-input/text-input.styled.ts +40 -0
  62. package/src/base/text-input/text-input.tsx +59 -0
  63. package/src/base/text-input/text-input.types.ts +15 -0
  64. package/src/base/toast/index.ts +2 -0
  65. package/src/base/toast/toast.tsx +60 -0
  66. package/src/base/toast/toast.types.ts +5 -0
  67. package/src/constants.ts +1 -0
  68. package/src/contexts/ColorSchemeProvider.tsx +154 -0
  69. package/src/css/global.css +30 -0
  70. package/src/extensions/accordion/accordion.hooks.ts +11 -0
  71. package/src/extensions/accordion/accordion.tsx +80 -0
  72. package/src/extensions/accordion/index.ts +1 -0
  73. package/src/extensions/app-header/app-header.hooks.ts +94 -0
  74. package/src/extensions/app-header/app-header.tsx +31 -0
  75. package/src/extensions/app-header/app-header.types.ts +1 -0
  76. package/src/extensions/app-header/index.ts +8 -0
  77. package/src/extensions/app-logo/app-logo.tsx +40 -0
  78. package/src/extensions/app-logo/index.ts +1 -0
  79. package/src/extensions/app-store-button/app-store-button.tsx +64 -0
  80. package/src/extensions/app-store-button/index.ts +1 -0
  81. package/src/extensions/brand-icon/brand-icon.android.tsx +11 -0
  82. package/src/extensions/brand-icon/brand-icon.apple.tsx +11 -0
  83. package/src/extensions/brand-icon/brand-icon.google.tsx +11 -0
  84. package/src/extensions/brand-icon/brand-icon.tsx +22 -0
  85. package/src/extensions/brand-icon/index.ts +1 -0
  86. package/src/extensions/color-scheme-toggle/color-scheme-toggle.tsx +76 -0
  87. package/src/extensions/color-scheme-toggle/index.ts +1 -0
  88. package/src/extensions/dropdown/dropdown.menu-item.tsx +237 -0
  89. package/src/extensions/dropdown/dropdown.result-item.tsx +26 -0
  90. package/src/extensions/dropdown/dropdown.styled.tsx +48 -0
  91. package/src/extensions/dropdown/dropdown.trigger.tsx +72 -0
  92. package/src/extensions/dropdown/dropdown.tsx +222 -0
  93. package/src/extensions/dropdown/dropdown.types.ts +3 -0
  94. package/src/extensions/dropdown/dropdown.utils.ts +40 -0
  95. package/src/extensions/dropdown/index.ts +14 -0
  96. package/src/extensions/error-ui/index.ts +7 -0
  97. package/src/extensions/error-ui/network-error/index.ts +1 -0
  98. package/src/extensions/error-ui/network-error/network-error.styled.ts +16 -0
  99. package/src/extensions/error-ui/network-error/network-error.tsx +14 -0
  100. package/src/extensions/error-ui/unknown-error/index.ts +1 -0
  101. package/src/extensions/error-ui/unknown-error/unknown-error.styled.ts +16 -0
  102. package/src/extensions/error-ui/unknown-error/unknown-error.tsx +14 -0
  103. package/src/extensions/full-screen-modal/full-screen-modal.tsx +52 -0
  104. package/src/extensions/full-screen-modal/index.ts +1 -0
  105. package/src/extensions/grid-card-image/grid-card-image.tsx +11 -0
  106. package/src/extensions/grid-card-image/index.ts +1 -0
  107. package/src/extensions/grid-card-image-empty/grid-card-image-empty.tsx +11 -0
  108. package/src/extensions/grid-card-image-empty/index.ts +1 -0
  109. package/src/extensions/grid-card-item/grid-card-item.masonry.styled.tsx +95 -0
  110. package/src/extensions/grid-card-item/grid-card-item.masonry.tsx +63 -0
  111. package/src/extensions/grid-card-item/grid-card-item.styled.tsx +93 -0
  112. package/src/extensions/grid-card-item/grid-card-item.subscribe-btn-layout.tsx +30 -0
  113. package/src/extensions/grid-card-item/grid-card-item.tsx +81 -0
  114. package/src/extensions/grid-card-item/index.ts +2 -0
  115. package/src/extensions/grid-card-list/grid-card-list.masonry.styled.tsx +45 -0
  116. package/src/extensions/grid-card-list/grid-card-list.masonry.tsx +58 -0
  117. package/src/extensions/grid-card-list/grid-card-list.styled.tsx +40 -0
  118. package/src/extensions/grid-card-list/grid-card-list.tsx +59 -0
  119. package/src/extensions/grid-card-list/index.ts +2 -0
  120. package/src/extensions/grid-card-list-empty/grid-card-list-empty.tsx +38 -0
  121. package/src/extensions/grid-card-list-empty/index.ts +1 -0
  122. package/src/extensions/grid-card-list-load-more/grid-card-list-load-more.styled.tsx +15 -0
  123. package/src/extensions/grid-card-list-load-more/grid-card-list-load-more.tsx +43 -0
  124. package/src/extensions/grid-card-list-load-more/index.ts +1 -0
  125. package/src/extensions/index.ts +38 -0
  126. package/src/extensions/menu-item/index.ts +1 -0
  127. package/src/extensions/menu-item/menu-item.tsx +87 -0
  128. package/src/extensions/sns-icon/index.ts +1 -0
  129. package/src/extensions/sns-icon/sns-icon.facebook.tsx +11 -0
  130. package/src/extensions/sns-icon/sns-icon.instagram.tsx +11 -0
  131. package/src/extensions/sns-icon/sns-icon.tsx +24 -0
  132. package/src/extensions/sns-icon/sns-icon.x.tsx +11 -0
  133. package/src/extensions/sns-icon/sns-icon.youtube.tsx +11 -0
  134. package/src/index.ts +8 -0
  135. package/src/native/button/button.styled.tsx +99 -0
  136. package/src/native/button/button.tsx +42 -0
  137. package/src/native/button/index.ts +1 -0
  138. package/src/native/contexts/color-scheme-context/color-scheme-context.tsx +45 -0
  139. package/src/native/contexts/color-scheme-context/index.ts +1 -0
  140. package/src/native/contexts/index.ts +1 -0
  141. package/src/native/icon-button/icon-button.styled.ts +6 -0
  142. package/src/native/icon-button/icon-button.tsx +33 -0
  143. package/src/native/icon-button/icon-button.types.ts +14 -0
  144. package/src/native/icon-button/icon-button.utils.ts +114 -0
  145. package/src/native/icon-button/index.ts +1 -0
  146. package/src/native/index.ts +9 -0
  147. package/src/native/modal/index.ts +2 -0
  148. package/src/native/modal/modal.styled.ts +17 -0
  149. package/src/native/modal/modal.tsx +21 -0
  150. package/src/native/modal/modal.types.ts +8 -0
  151. package/src/native/profile-thumbnail/index.ts +1 -0
  152. package/src/native/profile-thumbnail/profile-thumbnail.tsx +91 -0
  153. package/src/native/spinner/index.ts +1 -0
  154. package/src/native/spinner/spinner.tsx +75 -0
  155. package/src/native/text/index.ts +2 -0
  156. package/src/native/text/text.tsx +51 -0
  157. package/src/native/text/text.types.ts +5 -0
  158. package/src/native/text-input/index.ts +2 -0
  159. package/src/native/text-input/text-input.tsx +72 -0
  160. package/src/native/text-input/text-input.types.ts +3 -0
  161. package/src/native/toast/index.ts +2 -0
  162. package/src/native/toast/toast.styled.ts +40 -0
  163. package/src/native/toast/toast.tsx +23 -0
  164. package/src/native/toast/toast.types.ts +10 -0
  165. package/src/next/app-footer/app-footer.tsx +250 -0
  166. package/src/next/app-footer/index.ts +1 -0
  167. package/src/next/app-header/app-header.fixed-header.tsx +83 -0
  168. package/src/next/app-header/app-header.full-screen-mobile-accordion-drawer.tsx +131 -0
  169. package/src/next/app-header/app-header.logo.tsx +50 -0
  170. package/src/next/app-header/app-header.modal-mobile-accordion-drawer.tsx +69 -0
  171. package/src/next/app-header/app-header.styled.ts +160 -0
  172. package/src/next/app-header/app-header.tsx +91 -0
  173. package/src/next/app-header/index.ts +13 -0
  174. package/src/next/global-link/global-link.store.ts +41 -0
  175. package/src/next/global-link/global-link.tsx +52 -0
  176. package/src/next/global-link/global-link.utils.ts +9 -0
  177. package/src/next/global-link/index.ts +3 -0
  178. package/src/next/grid-card-item/grid-card-item.masonry.tsx +23 -0
  179. package/src/next/grid-card-item/grid-card-item.tsx +23 -0
  180. package/src/next/grid-card-item/index.ts +2 -0
  181. package/src/next/index.ts +16 -0
  182. package/src/next/new-tab-link/index.ts +1 -0
  183. package/src/next/new-tab-link/new-tab-link.tsx +15 -0
  184. package/src/next/route-loading/index.ts +1 -0
  185. package/src/next/route-loading/route-loading.tsx +21 -0
  186. package/src/tokens/index.ts +2 -0
  187. package/src/tokens/tokens.ts +8 -0
  188. package/src/tokens/tokens.types.ts +7 -0
  189. package/src/utils/breakpoints.ts +9 -0
  190. package/src/utils/common-styles.ts +23 -0
  191. package/src/utils/index.ts +2 -0
  192. package/src/utils/media.ts +23 -0
  193. package/src/utils/use-prevent-scroll-effect.ts +19 -0
  194. package/src/utils/with-id.ts +3 -0
  195. package/src/utils/with-stop-propagation.ts +10 -0
@@ -0,0 +1,14 @@
1
+ import { Button } from '@/base';
2
+ import { memo } from 'react';
3
+ import { StyledErrorContainer, StyledErrorText } from './network-error.styled';
4
+
5
+ export const NetworkError = memo(({ onClickRetry }: { onClickRetry: () => void }) => {
6
+ return (
7
+ <StyledErrorContainer>
8
+ <StyledErrorText as="h1">네트워크 에러가 발생했어요 🗯</StyledErrorText>
9
+ <Button onClick={onClickRetry}>다시 시도하기</Button>
10
+ </StyledErrorContainer>
11
+ );
12
+ });
13
+
14
+ NetworkError.displayName = 'ErrorUI.NetworkError';
@@ -0,0 +1 @@
1
+ export * from './unknown-error';
@@ -0,0 +1,16 @@
1
+ import { Text } from '@/base';
2
+ import styled from '@emotion/styled';
3
+
4
+ export const StyledErrorContainer = styled.div`
5
+ height: 100vh;
6
+ display: flex;
7
+ flex-direction: column;
8
+ align-items: center;
9
+ justify-content: center;
10
+ `;
11
+
12
+ export const StyledErrorText = styled(Text)`
13
+ font-weight: bold;
14
+ margin: unset;
15
+ margin-bottom: 1rem;
16
+ `;
@@ -0,0 +1,14 @@
1
+ import { Button } from '@/base';
2
+ import { memo } from 'react';
3
+ import { StyledErrorContainer, StyledErrorText } from './unknown-error.styled';
4
+
5
+ export const UnknownError = memo(({ onClickRetry }: { onClickRetry: () => void }) => {
6
+ return (
7
+ <StyledErrorContainer>
8
+ <StyledErrorText as="h1">알 수 없는 오류가 발생했어요 🗯</StyledErrorText>
9
+ <Button onClick={onClickRetry}>다시 시도하기</Button>
10
+ </StyledErrorContainer>
11
+ );
12
+ });
13
+
14
+ UnknownError.displayName = 'ErrorUI.UnknownError';
@@ -0,0 +1,52 @@
1
+ import { Modal } from '@/base';
2
+ import { semantics } from '@/tokens';
3
+ import { usePreventScrollEffect } from '@/utils/use-prevent-scroll-effect';
4
+ import styled from '@emotion/styled';
5
+ import { XIcon } from 'lucide-react';
6
+ import { type PropsWithChildren, memo } from 'react';
7
+
8
+ const StyledFullScreenModalContent = styled.div`
9
+ position: fixed;
10
+ top: 0;
11
+ left: 0;
12
+ right: 0;
13
+ bottom: 0;
14
+ background: ${semantics.color.background[2]};
15
+ padding: 1rem;
16
+
17
+ display: flex;
18
+ flex-direction: column;
19
+
20
+ overflow-y: auto;
21
+ `;
22
+
23
+ const CloseIcon = styled(XIcon)`
24
+ color: ${semantics.color.foreground[1]};
25
+ margin-left: auto;
26
+ flex-shrink: 0;
27
+
28
+ cursor: pointer;
29
+ `;
30
+
31
+ type Props = PropsWithChildren<{
32
+ visible: boolean;
33
+ onClose: () => void;
34
+ zIndex?: number;
35
+ }>;
36
+
37
+ export const FullScreenModal = memo(({ visible, onClose, children, zIndex }: Props) => {
38
+ usePreventScrollEffect({
39
+ shouldPrevent: visible,
40
+ });
41
+
42
+ return (
43
+ <Modal visible={visible} onClose={onClose} zIndex={zIndex}>
44
+ <StyledFullScreenModalContent>
45
+ <CloseIcon onClick={onClose} />
46
+ {children}
47
+ </StyledFullScreenModalContent>
48
+ </Modal>
49
+ );
50
+ });
51
+
52
+ FullScreenModal.displayName = 'FullScreenModal';
@@ -0,0 +1 @@
1
+ export * from './full-screen-modal';
@@ -0,0 +1,11 @@
1
+ import { semantics } from '@/tokens';
2
+ import styled from '@emotion/styled';
3
+
4
+ export const GridCardImage = styled.img`
5
+ width: 100%;
6
+ aspect-ratio: 1 / 1;
7
+ object-fit: cover;
8
+ object-position: 50%;
9
+ border-radius: 12px;
10
+ background-color: ${semantics.color.background[3]};
11
+ `;
@@ -0,0 +1 @@
1
+ export * from './grid-card-image';
@@ -0,0 +1,11 @@
1
+ import { semantics } from '@/tokens';
2
+ import styled from '@emotion/styled';
3
+
4
+ export const GridCardImageEmpty = styled.div`
5
+ width: 100%;
6
+ aspect-ratio: 1 / 1;
7
+ object-fit: cover;
8
+ object-position: 50%;
9
+ border-radius: 12px;
10
+ background-color: ${semantics.color.background[3]};
11
+ `;
@@ -0,0 +1 @@
1
+ export * from './grid-card-image-empty';
@@ -0,0 +1,95 @@
1
+ import { Text } from '@/base';
2
+ import { semantics } from '@/tokens';
3
+ import { media } from '@/utils';
4
+ import { css } from '@emotion/react';
5
+ import styled from '@emotion/styled';
6
+
7
+ export const StyledGridDate = styled(Text)`
8
+ margin: unset;
9
+ margin-top: 0.25rem;
10
+ font-size: 20px;
11
+ font-weight: 500;
12
+ color: ${semantics.color.foreground[3]};
13
+ margin-bottom: 0.875rem;
14
+
15
+ ${media.large(css`
16
+ font-size: 17.5px;
17
+ `)}
18
+ ${media.small(css`
19
+ font-size: 14px;
20
+ `)}
21
+ `;
22
+
23
+ export const StyledGridImageEmptyContainer = styled.div`
24
+ width: 100%;
25
+ aspect-ratio: 1 / 1;
26
+ object-fit: cover;
27
+ object-position: 50%;
28
+ border-radius: 12px;
29
+ background-color: ${semantics.color.background[1]};
30
+
31
+ display: flex;
32
+
33
+ align-items: center;
34
+ justify-content: center;
35
+ `;
36
+
37
+ export const StyledGridImageEmptyText = styled(Text)`
38
+ text-align: center;
39
+ font-weight: 600;
40
+ font-size: 16px;
41
+
42
+ padding-left: 1rem;
43
+ padding-right: 1rem;
44
+ `;
45
+
46
+ export const StyledGridItem = styled.div`
47
+ display: flex;
48
+ flex-direction: column;
49
+ margin-bottom: 0.875rem;
50
+
51
+ ${media.medium(css`
52
+ margin-bottom: 0.5rem;
53
+ `)}
54
+ `;
55
+
56
+ export const StyledGridTop = styled.div`
57
+ position: relative;
58
+ `;
59
+
60
+ export const StyledGridTextContainer = styled.div`
61
+ margin-top: 0.5rem;
62
+ `;
63
+
64
+ export const StyledGridTitle = styled(Text)`
65
+ margin: unset;
66
+ font-size: 22px;
67
+ color: ${semantics.color.foreground[1]};
68
+ font-weight: 600;
69
+
70
+ overflow-wrap: break-word;
71
+ word-wrap: break-word;
72
+ word-break: break-all;
73
+
74
+ ${media.large(css`
75
+ font-size: 1.5rem;
76
+ font-size: 20px;
77
+ `)}
78
+ ${media.small(css`
79
+ font-size: 1.25rem;
80
+ font-size: 16.5px;
81
+ `)}
82
+ `;
83
+
84
+ export const StyledVenueText = styled(Text)`
85
+ margin: unset;
86
+ margin-top: 0.25rem;
87
+ font-size: 1.125rem;
88
+ font-weight: 500;
89
+ color: ${semantics.color.foreground[4]};
90
+ text-align: right;
91
+
92
+ ${media.small(css`
93
+ font-size: 14.5px;
94
+ `)}
95
+ `;
@@ -0,0 +1,63 @@
1
+ import { memo } from 'react';
2
+ import { match } from 'ts-pattern';
3
+ import { GridCardImage } from '../grid-card-image';
4
+ import type { GridCardListItemProps } from './grid-card-item';
5
+ import {
6
+ StyledGridDate,
7
+ StyledGridImageEmptyContainer,
8
+ StyledGridImageEmptyText,
9
+ StyledGridItem,
10
+ StyledGridTextContainer,
11
+ StyledGridTitle,
12
+ StyledGridTop,
13
+ StyledVenueText,
14
+ } from './grid-card-item.masonry.styled';
15
+ import { FixedSubscribeEventButtonLayout } from './grid-card-item.subscribe-btn-layout';
16
+
17
+ export const MasonryGridCardItem = memo(
18
+ ({
19
+ isSubscribed,
20
+ thumbnailUrl,
21
+ titleText,
22
+ subText,
23
+ bottomText,
24
+ rightBottomSlot,
25
+ renderThumbnail,
26
+ }: GridCardListItemProps) => {
27
+ return (
28
+ <StyledGridItem>
29
+ <StyledGridTitle as="p">{titleText}</StyledGridTitle>
30
+ <StyledGridDate as="p">{subText}</StyledGridDate>
31
+ <StyledGridTop>
32
+ {thumbnailUrl ? (
33
+ renderThumbnail ? (
34
+ renderThumbnail(thumbnailUrl)
35
+ ) : (
36
+ <GridCardImage src={thumbnailUrl} alt={titleText} />
37
+ )
38
+ ) : (
39
+ <StyledGridImageEmptyContainer>
40
+ <StyledGridImageEmptyText>{titleText}</StyledGridImageEmptyText>
41
+ </StyledGridImageEmptyContainer>
42
+ )}
43
+ {match(rightBottomSlot)
44
+ .with(
45
+ { type: 'subscribe' },
46
+ (value) =>
47
+ typeof isSubscribed === 'boolean' && (
48
+ <FixedSubscribeEventButtonLayout>
49
+ {value.subscribeEventBtn}
50
+ </FixedSubscribeEventButtonLayout>
51
+ )
52
+ )
53
+ .otherwise(() => null)}
54
+ </StyledGridTop>
55
+ <StyledGridTextContainer>
56
+ {bottomText && <StyledVenueText as="p">{bottomText}</StyledVenueText>}
57
+ </StyledGridTextContainer>
58
+ </StyledGridItem>
59
+ );
60
+ }
61
+ );
62
+
63
+ MasonryGridCardItem.displayName = 'GridCardList.MasonryItem';
@@ -0,0 +1,93 @@
1
+ import { Text } from '@/base';
2
+ import { semantics } from '@/tokens';
3
+ import { media } from '@/utils';
4
+ import { css } from '@emotion/react';
5
+ import styled from '@emotion/styled';
6
+ import { motion } from 'framer-motion';
7
+
8
+ export const StyledGridItem = styled.div`
9
+ display: flex;
10
+ flex-direction: column;
11
+ `;
12
+
13
+ export const StyledGridTop = styled.div`
14
+ position: relative;
15
+ `;
16
+
17
+ export const StyledGridImageEmptyContainer = styled.div`
18
+ width: 100%;
19
+ aspect-ratio: 1 / 1;
20
+ object-fit: cover;
21
+ object-position: 50%;
22
+ border-radius: 12px;
23
+ background-color: ${semantics.color.background[1]};
24
+
25
+ display: flex;
26
+
27
+ align-items: center;
28
+ justify-content: center;
29
+ `;
30
+
31
+ export const StyledGridImageEmptyText = styled(Text)`
32
+ text-align: center;
33
+ font-weight: 600;
34
+ font-size: 16px;
35
+
36
+ padding-left: 1rem;
37
+ padding-right: 1rem;
38
+ `;
39
+
40
+ export const StyledGridTextContainer = styled.div`
41
+ margin-top: 0.5rem;
42
+ `;
43
+
44
+ export const StyledGridTitle = styled(Text)`
45
+ margin: unset;
46
+ font-size: 16px;
47
+ color: ${semantics.color.foreground[1]};
48
+ font-weight: 600;
49
+
50
+ overflow-wrap: break-word;
51
+ word-wrap: break-word;
52
+ word-break: break-all;
53
+
54
+ ${media.medium(css`
55
+ font-size: 14px;
56
+ `)}
57
+ `;
58
+
59
+ export const StyledGridDate = styled(Text)`
60
+ margin: unset;
61
+ margin-top: 0.25rem;
62
+ font-size: 14px;
63
+ color: ${semantics.color.foreground[3]};
64
+
65
+ ${media.medium(css`
66
+ font-size: 12px;
67
+ `)}
68
+ `;
69
+
70
+ export const StyledVenueText = styled(Text)`
71
+ margin: unset;
72
+ margin-top: 0.25rem;
73
+ font-size: 14px;
74
+ color: ${semantics.color.foreground[3]};
75
+
76
+ ${media.medium(css`
77
+ font-size: 12px;
78
+ `)}
79
+ `;
80
+
81
+ export const StyledFixedSubscribeEventButtonLayoutContainer = styled(motion.div)`
82
+ position: absolute;
83
+ background: transparent;
84
+
85
+ display: flex;
86
+ align-items: center;
87
+ justify-content: center;
88
+ background: ${semantics.color.background[4]};
89
+ border-radius: 50%;
90
+
91
+ box-shadow: 0 1px 3px ${semantics.color.border[1]}, 0 1px 2px ${semantics.color.border[2]};
92
+ transition: all 0.3s cubic-bezier(.25,.8,.25,1);
93
+ `;
@@ -0,0 +1,30 @@
1
+ import { withStopPropagation } from '@/utils/with-stop-propagation';
2
+ import { type PropsWithChildren, memo } from 'react';
3
+ import { StyledFixedSubscribeEventButtonLayoutContainer } from './grid-card-item.styled';
4
+
5
+ export const FixedSubscribeEventButtonLayout = memo(
6
+ ({
7
+ children,
8
+ customRight = 12,
9
+ customBottom = 12,
10
+ }: PropsWithChildren<{
11
+ customRight?: number;
12
+ customBottom?: number;
13
+ }>) => {
14
+ return (
15
+ <StyledFixedSubscribeEventButtonLayoutContainer
16
+ onClick={withStopPropagation()}
17
+ whileHover={{
18
+ scale: 1.1,
19
+ }}
20
+ transition={{ type: 'spring', stiffness: 300 }}
21
+ style={{
22
+ bottom: customBottom,
23
+ right: customRight,
24
+ }}
25
+ >
26
+ {children}
27
+ </StyledFixedSubscribeEventButtonLayoutContainer>
28
+ );
29
+ }
30
+ );
@@ -0,0 +1,81 @@
1
+ import type { WithId } from '@/utils/with-id';
2
+ import { type MouseEventHandler, type ReactNode, memo } from 'react';
3
+ import { match } from 'ts-pattern';
4
+ import { GridCardImage } from '../grid-card-image';
5
+ import {
6
+ StyledGridDate,
7
+ StyledGridImageEmptyContainer,
8
+ StyledGridImageEmptyText,
9
+ StyledGridItem,
10
+ StyledGridTextContainer,
11
+ StyledGridTitle,
12
+ StyledGridTop,
13
+ StyledVenueText,
14
+ } from './grid-card-item.styled';
15
+ import { FixedSubscribeEventButtonLayout } from './grid-card-item.subscribe-btn-layout';
16
+
17
+ export type GridCardListItemProps = WithId<{
18
+ thumbnailUrl: string;
19
+ titleText: string;
20
+ subText: string;
21
+ bottomText?: string;
22
+ isSubscribed?: boolean;
23
+ rightBottomSlot?: {
24
+ type: 'subscribe';
25
+ subscribeEventBtn: ReactNode;
26
+ };
27
+ renderThumbnail?: (url: string) => ReactNode;
28
+ href?: string;
29
+ onClick?: MouseEventHandler<HTMLDivElement | HTMLAnchorElement>;
30
+ }>;
31
+
32
+ export const GridCardItem = memo(
33
+ ({
34
+ thumbnailUrl,
35
+ titleText,
36
+ subText,
37
+ bottomText,
38
+ rightBottomSlot,
39
+ renderThumbnail,
40
+ isSubscribed,
41
+ onClick,
42
+ }: GridCardListItemProps) => {
43
+ return (
44
+ <StyledGridItem onClick={onClick}>
45
+ <StyledGridTop>
46
+ {thumbnailUrl ? (
47
+ renderThumbnail ? (
48
+ renderThumbnail(thumbnailUrl)
49
+ ) : (
50
+ <GridCardImage src={thumbnailUrl} alt={titleText} />
51
+ )
52
+ ) : (
53
+ <StyledGridImageEmptyContainer>
54
+ <StyledGridImageEmptyText>{titleText}</StyledGridImageEmptyText>
55
+ </StyledGridImageEmptyContainer>
56
+ )}
57
+ {match(rightBottomSlot)
58
+ .with(
59
+ { type: 'subscribe' },
60
+ (value) =>
61
+ typeof isSubscribed === 'boolean' && (
62
+ <FixedSubscribeEventButtonLayout>
63
+ {value.subscribeEventBtn}
64
+ </FixedSubscribeEventButtonLayout>
65
+ )
66
+ )
67
+ .otherwise(() => null)}
68
+ </StyledGridTop>
69
+ <StyledGridTextContainer>
70
+ <StyledGridTitle as="p" numberOfLines={2}>
71
+ {titleText}
72
+ </StyledGridTitle>
73
+ <StyledGridDate as="p">{subText}</StyledGridDate>
74
+ {bottomText && <StyledVenueText as="p">{bottomText}</StyledVenueText>}
75
+ </StyledGridTextContainer>
76
+ </StyledGridItem>
77
+ );
78
+ }
79
+ );
80
+
81
+ GridCardItem.displayName = 'GridCardList.Item';
@@ -0,0 +1,2 @@
1
+ export * from './grid-card-item';
2
+ export * from './grid-card-item.masonry';
@@ -0,0 +1,45 @@
1
+ import { Text } from '@/base';
2
+ import { semantics } from '@/tokens';
3
+ import { media } from '@/utils';
4
+ import { css } from '@emotion/react';
5
+ import styled from '@emotion/styled';
6
+
7
+ export const StyledListContainer = styled.div`
8
+ margin-top: 1.5rem;
9
+
10
+ display: flex;
11
+ flex-direction: column;
12
+ `;
13
+
14
+ export const StyledListHeader = styled.div`
15
+ width: 100%;
16
+ margin-top: 3.5rem;
17
+
18
+ ${media.medium(css`
19
+ margin-top: 1.5rem;
20
+ margin-bottom: 2.25rem;
21
+ `)}
22
+ `;
23
+
24
+ export const StyledListHeaderText = styled(Text)`
25
+ text-align: center;
26
+ color: ${semantics.color.foreground[2]};
27
+
28
+ font-size: 2rem;
29
+
30
+ ${media.medium(css`
31
+ font-size: 1.875rem;
32
+ `)}
33
+ `;
34
+
35
+ export const StyledLoadMoreContainer = styled.div`
36
+ display: flex;
37
+ justify-content: center;
38
+ margin-top: 4rem;
39
+ margin-bottom: 4rem;
40
+
41
+ ${media.medium(css`
42
+ margin-top: 2rem;
43
+ margin-bottom: 2rem;
44
+ `)}
45
+ `;
@@ -0,0 +1,58 @@
1
+ import { Fragment, type ReactNode, memo } from 'react';
2
+ import type { GridCardListItemProps } from '../grid-card-item';
3
+ import { GridCardListLoadMore } from '../grid-card-list-load-more';
4
+ import {
5
+ StyledListContainer,
6
+ StyledListHeader,
7
+ StyledListHeaderText,
8
+ } from './grid-card-list.masonry.styled';
9
+ import { StyledGridContainer, StyledNavigationContainer } from './grid-card-list.styled';
10
+
11
+ type Props = {
12
+ items: GridCardListItemProps[];
13
+ renderItem: (item: GridCardListItemProps) => ReactNode;
14
+ onLoadMore: () => void;
15
+ headerText?: string;
16
+ hasNextPage?: boolean;
17
+ isEmpty?: boolean;
18
+ emptyComponent?: ReactNode;
19
+ navigationComponent?: ReactNode;
20
+ };
21
+
22
+ export const MasonryGridCardList = memo(
23
+ ({
24
+ items,
25
+ renderItem,
26
+ onLoadMore,
27
+ headerText,
28
+ hasNextPage,
29
+ isEmpty,
30
+ emptyComponent,
31
+ navigationComponent,
32
+ }: Props) => {
33
+ return (
34
+ <StyledListContainer>
35
+ {headerText && (
36
+ <StyledListHeader>
37
+ <StyledListHeaderText as="h1">{headerText}</StyledListHeaderText>
38
+ </StyledListHeader>
39
+ )}
40
+ <StyledNavigationContainer>
41
+ {navigationComponent && navigationComponent}
42
+ </StyledNavigationContainer>
43
+ {isEmpty ? (
44
+ emptyComponent
45
+ ) : (
46
+ <StyledGridContainer>
47
+ {items.map((item) => (
48
+ <Fragment key={item.id}>{renderItem(item)}</Fragment>
49
+ ))}
50
+ </StyledGridContainer>
51
+ )}
52
+ {hasNextPage && <GridCardListLoadMore onLoadMore={onLoadMore} />}
53
+ </StyledListContainer>
54
+ );
55
+ }
56
+ );
57
+
58
+ MasonryGridCardList.displayName = 'GridCardList.MasonryList';
@@ -0,0 +1,40 @@
1
+ import { Text } from '@/base';
2
+ import { media } from '@/utils';
3
+ import { commonHorizontalLayoutCss } from '@/utils/common-styles';
4
+ import { css } from '@emotion/react';
5
+ import styled from '@emotion/styled';
6
+
7
+ export const StyledGridContainer = styled.div`
8
+ display: grid;
9
+ grid-template-columns: repeat(4, minmax(0px, 1fr));
10
+ gap: 1.25rem;
11
+
12
+ ${commonHorizontalLayoutCss(['left', 'right'])}
13
+
14
+ ${media.large(css`
15
+ grid-template-columns: repeat(3, minmax(0px, 1fr));
16
+ gap: 1rem;
17
+ `)}
18
+ ${media.medium(css`
19
+ grid-template-columns: repeat(2, minmax(0px, 1fr));
20
+ gap: 0.85rem;
21
+ `)}
22
+ ${media.small(css`
23
+ grid-template-columns: repeat(2, minmax(0px, 1fr));
24
+ `)}
25
+ `;
26
+
27
+ export const StyledListContainer = styled.div``;
28
+
29
+ export const StyledListHeader = styled.div`
30
+ width: 100%;
31
+ `;
32
+
33
+ export const StyledListHeaderText = styled(Text)``;
34
+
35
+ export const StyledNavigationContainer = styled.div`
36
+ margin-bottom: 3rem;
37
+ ${media.small(css`
38
+ margin-bottom: 1.875rem;
39
+ `)}
40
+ `;