@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,59 @@
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
+ StyledGridContainer,
6
+ StyledListContainer,
7
+ StyledListHeader,
8
+ StyledListHeaderText,
9
+ StyledNavigationContainer,
10
+ } from './grid-card-list.styled';
11
+
12
+ type Props = {
13
+ items: GridCardListItemProps[];
14
+ renderItem: (item: GridCardListItemProps) => ReactNode;
15
+ onLoadMore: () => void;
16
+ headerText?: string;
17
+ hasNextPage?: boolean;
18
+ isEmpty?: boolean;
19
+ emptyComponent?: ReactNode;
20
+ navigationComponent?: ReactNode;
21
+ };
22
+
23
+ export const GridCardList = memo(
24
+ ({
25
+ items,
26
+ renderItem,
27
+ onLoadMore,
28
+ headerText,
29
+ hasNextPage,
30
+ isEmpty,
31
+ emptyComponent,
32
+ navigationComponent,
33
+ }: Props) => {
34
+ return (
35
+ <StyledListContainer>
36
+ {headerText && (
37
+ <StyledListHeader>
38
+ <StyledListHeaderText as="h1">{headerText}</StyledListHeaderText>
39
+ </StyledListHeader>
40
+ )}
41
+ <StyledNavigationContainer>
42
+ {navigationComponent && navigationComponent}
43
+ </StyledNavigationContainer>
44
+ {isEmpty ? (
45
+ emptyComponent
46
+ ) : (
47
+ <StyledGridContainer>
48
+ {items.map((item) => (
49
+ <Fragment key={item.id}>{renderItem(item)}</Fragment>
50
+ ))}
51
+ </StyledGridContainer>
52
+ )}
53
+ {hasNextPage && <GridCardListLoadMore onLoadMore={onLoadMore} />}
54
+ </StyledListContainer>
55
+ );
56
+ }
57
+ );
58
+
59
+ GridCardList.displayName = 'GridCardList.List';
@@ -0,0 +1,2 @@
1
+ export * from './grid-card-list';
2
+ export * from './grid-card-list.masonry';
@@ -0,0 +1,38 @@
1
+ import { Text } from '@/base';
2
+ import { WEB_APP_HEADER_HEIGHT } from '@/constants';
3
+ import { semantics } from '@/tokens';
4
+ import { media } from '@/utils';
5
+ import { css } from '@emotion/react';
6
+ import styled from '@emotion/styled';
7
+ import { memo } from 'react';
8
+
9
+ const StyledEmptyContainer = styled.div`
10
+ width: 100%;
11
+ height: calc(80vh - ${WEB_APP_HEADER_HEIGHT});
12
+ display: flex;
13
+ flex-direction: column;
14
+ align-items: center;
15
+ justify-content: center;
16
+
17
+ ${media.medium(css`
18
+ height: calc(60vh - ${WEB_APP_HEADER_HEIGHT});
19
+ `)}
20
+ `;
21
+
22
+ const StyledEmptyText = styled(Text)`
23
+ font-size: 2.125rem;
24
+ font-weight: 500;
25
+ color: ${semantics.color.foreground[1]};
26
+ text-align: center;
27
+ ${media.medium(css`
28
+ font-size: 24px;
29
+ `)}
30
+ `;
31
+
32
+ export const GridCardListEmpty = memo(({ text }: { text: string }) => {
33
+ return (
34
+ <StyledEmptyContainer>
35
+ <StyledEmptyText as="p">{text}</StyledEmptyText>
36
+ </StyledEmptyContainer>
37
+ );
38
+ });
@@ -0,0 +1 @@
1
+ export * from './grid-card-list-empty';
@@ -0,0 +1,15 @@
1
+ import { media } from '@/utils';
2
+ import { css } from '@emotion/react';
3
+ import styled from '@emotion/styled';
4
+
5
+ export const StyledLoadMoreContainer = styled.div`
6
+ display: flex;
7
+ justify-content: center;
8
+ margin-top: 4rem;
9
+ margin-bottom: 4rem;
10
+
11
+ ${media.medium(css`
12
+ margin-top: 2rem;
13
+ margin-bottom: 2rem;
14
+ `)}
15
+ `;
@@ -0,0 +1,43 @@
1
+ import { Spinner } from '@/base';
2
+ import { memo, useEffect, useRef } from 'react';
3
+ import { StyledLoadMoreContainer } from './grid-card-list-load-more.styled';
4
+
5
+ type Props = {
6
+ onLoadMore: () => void;
7
+ };
8
+
9
+ export const GridCardListLoadMore = memo(({ onLoadMore }: Props) => {
10
+ const loadMoreRef = useRef<HTMLDivElement | null>(null);
11
+ useEffect(() => {
12
+ const intersectionObserver = new IntersectionObserver(
13
+ (entries) => {
14
+ entries.forEach((entry) => {
15
+ if (entry.isIntersecting && entry.intersectionRatio > 0) {
16
+ onLoadMore();
17
+ }
18
+ });
19
+ },
20
+ {
21
+ threshold: 0.5,
22
+ }
23
+ );
24
+ const refToObserve = loadMoreRef.current;
25
+ if (refToObserve) {
26
+ intersectionObserver.observe(refToObserve);
27
+ }
28
+
29
+ return () => {
30
+ if (refToObserve) {
31
+ intersectionObserver.unobserve(refToObserve);
32
+ }
33
+ };
34
+ }, [onLoadMore]);
35
+
36
+ return (
37
+ <StyledLoadMoreContainer ref={loadMoreRef}>
38
+ <Spinner />
39
+ </StyledLoadMoreContainer>
40
+ );
41
+ });
42
+
43
+ GridCardListLoadMore.displayName = 'GridCardList.LoadMore';
@@ -0,0 +1 @@
1
+ export * from './grid-card-list-load-more';
@@ -0,0 +1,38 @@
1
+ import { GridCardImage } from './grid-card-image';
2
+ import { GridCardImageEmpty } from './grid-card-image-empty';
3
+ import { GridCardItem, type GridCardListItemProps, MasonryGridCardItem } from './grid-card-item';
4
+ import { GridCardList as GridCardListUI, MasonryGridCardList } from './grid-card-list';
5
+ import { GridCardListEmpty } from './grid-card-list-empty';
6
+ import { GridCardListLoadMore } from './grid-card-list-load-more';
7
+
8
+ export const GridCardList = {
9
+ List: GridCardListUI,
10
+ MasonryList: MasonryGridCardList,
11
+ Item: GridCardItem,
12
+ MasonryItem: MasonryGridCardItem,
13
+ LoadMore: GridCardListLoadMore,
14
+ Empty: GridCardListEmpty,
15
+ ImageEmpty: GridCardImageEmpty,
16
+ Image: GridCardImage,
17
+ };
18
+
19
+ export type { GridCardListItemProps };
20
+
21
+ export { ErrorUI } from './error-ui';
22
+
23
+ export { AppLogo } from './app-logo';
24
+
25
+ export { AppStoreButton } from './app-store-button';
26
+ export { BrandIcon } from './brand-icon';
27
+ export { SNSIcon } from './sns-icon';
28
+
29
+ export * from './dropdown';
30
+ export * from './menu-item';
31
+
32
+ export * from './app-header';
33
+
34
+ export * from './color-scheme-toggle';
35
+
36
+ export * from './full-screen-modal';
37
+
38
+ export * from './accordion';
@@ -0,0 +1 @@
1
+ export * from './menu-item';
@@ -0,0 +1,87 @@
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 { type MotionProps, motion } from 'framer-motion';
7
+ import { type HTMLAttributes, type PropsWithChildren, forwardRef } from 'react';
8
+
9
+ const StyledMenuText = styled(Text)`
10
+ color: ${semantics.color.foreground[3]};
11
+ `;
12
+
13
+ const StyledMenuItem = styled.div<{ $isHighlighted?: boolean }>`
14
+ padding: 11px 16px;
15
+ border-radius: 8px;
16
+
17
+ color: ${semantics.color.foreground[3]};
18
+
19
+ flex-shrink: 0;
20
+
21
+ cursor: pointer;
22
+
23
+ align-self: flex-start;
24
+
25
+ display: flex;
26
+ align-items: center;
27
+ gap: 0.5rem;
28
+
29
+ &:hover {
30
+ background-color: ${semantics.color.background[4]};
31
+ transition-duration: 0.2s;
32
+ transition-timing-function: ease-in-out;
33
+ }
34
+
35
+ ${(props) =>
36
+ props.$isHighlighted &&
37
+ css`
38
+ background-color: ${semantics.color.background[3]};
39
+ `}
40
+ `;
41
+
42
+ const AppHeaderMenuTextSkeleton = styled.div`
43
+ width: 80px;
44
+ height: 36px;
45
+ border-radius: 4px;
46
+ background-color: ${semantics.color.background[4]};
47
+
48
+ margin-right: 0.75rem;
49
+
50
+ ${media.medium(css`
51
+ width: 120px;
52
+ height: 24px;
53
+ `)}
54
+ `;
55
+
56
+ const MenuItemComponent = forwardRef<HTMLDivElement, { $isHighlighted?: boolean }>((props, ref) => {
57
+ return <StyledMenuItem ref={ref} {...props} />;
58
+ });
59
+
60
+ const MenuItemMotion = motion.create(MenuItemComponent);
61
+
62
+ type Props = HTMLAttributes<HTMLDivElement> &
63
+ MotionProps & {
64
+ isLoading?: boolean;
65
+ isCurrent?: boolean;
66
+ icon?: React.ReactNode;
67
+ };
68
+
69
+ export const MenuItem = forwardRef<HTMLDivElement, PropsWithChildren<Props>>(
70
+ ({ children, isLoading, isCurrent = false, icon, ...otherProps }, ref) => {
71
+ if (isLoading) {
72
+ return <AppHeaderMenuTextSkeleton />;
73
+ }
74
+ return (
75
+ <MenuItemMotion ref={ref} $isHighlighted={isCurrent} {...otherProps}>
76
+ {icon}
77
+ {typeof children === 'string' ? (
78
+ <StyledMenuText as="span">{children}</StyledMenuText>
79
+ ) : (
80
+ children
81
+ )}
82
+ </MenuItemMotion>
83
+ );
84
+ }
85
+ );
86
+
87
+ MenuItem.displayName = 'MenuItem';
@@ -0,0 +1 @@
1
+ export * from './sns-icon';
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ import { type Ref, type SVGProps, forwardRef, memo } from 'react';
3
+ const SvgComponent = (props: SVGProps<SVGSVGElement>, ref: Ref<SVGSVGElement>) => (
4
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" ref={ref} {...props}>
5
+ <title>{'Facebook'}</title>
6
+ <path d="M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 2.103-.287 1.564h-3.246v8.245C19.396 23.238 24 18.179 24 12.044c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.628 3.874 10.35 9.101 11.647Z" />
7
+ </svg>
8
+ );
9
+ const ForwardRef = forwardRef(SvgComponent);
10
+ const Memo = memo(ForwardRef);
11
+ export default Memo;
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ import { type Ref, type SVGProps, forwardRef, memo } from 'react';
3
+ const SvgComponent = (props: SVGProps<SVGSVGElement>, ref: Ref<SVGSVGElement>) => (
4
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" ref={ref} {...props}>
5
+ <title>{'Instagram'}</title>
6
+ <path d="M7.03.084c-1.277.06-2.149.264-2.91.563a5.874 5.874 0 0 0-2.124 1.388 5.878 5.878 0 0 0-1.38 2.127C.321 4.926.12 5.8.064 7.076.008 8.354-.005 8.764.001 12.023c.007 3.259.021 3.667.083 4.947.061 1.277.264 2.149.563 2.911.308.789.72 1.457 1.388 2.123a5.872 5.872 0 0 0 2.129 1.38c.763.295 1.636.496 2.913.552 1.278.056 1.689.069 4.947.063 3.257-.007 3.668-.021 4.947-.082 1.28-.06 2.147-.265 2.91-.563a5.881 5.881 0 0 0 2.123-1.388 5.881 5.881 0 0 0 1.38-2.129c.295-.763.496-1.636.551-2.912.056-1.28.07-1.69.063-4.948-.006-3.258-.02-3.667-.081-4.947-.06-1.28-.264-2.148-.564-2.911a5.892 5.892 0 0 0-1.387-2.123 5.857 5.857 0 0 0-2.128-1.38C19.074.322 18.202.12 16.924.066 15.647.009 15.236-.006 11.977 0 8.718.008 8.31.021 7.03.084m.14 21.693c-1.17-.05-1.805-.245-2.228-.408a3.736 3.736 0 0 1-1.382-.895 3.695 3.695 0 0 1-.9-1.378c-.165-.423-.363-1.058-.417-2.228-.06-1.264-.072-1.644-.08-4.848-.006-3.204.006-3.583.061-4.848.05-1.169.246-1.805.408-2.228.216-.561.477-.96.895-1.382a3.705 3.705 0 0 1 1.379-.9c.423-.165 1.057-.361 2.227-.417 1.265-.06 1.644-.072 4.848-.08 3.203-.006 3.583.006 4.85.062 1.168.05 1.804.244 2.227.408.56.216.96.475 1.382.895.421.42.681.817.9 1.378.165.422.362 1.056.417 2.227.06 1.265.074 1.645.08 4.848.005 3.203-.006 3.583-.061 4.848-.051 1.17-.245 1.805-.408 2.23-.216.56-.477.96-.896 1.38a3.705 3.705 0 0 1-1.378.9c-.422.165-1.058.362-2.226.418-1.266.06-1.645.072-4.85.079-3.204.007-3.582-.006-4.848-.06m9.783-16.192a1.44 1.44 0 1 0 1.437-1.442 1.44 1.44 0 0 0-1.437 1.442M5.839 12.012a6.161 6.161 0 1 0 12.323-.024 6.162 6.162 0 0 0-12.323.024M8 12.008A4 4 0 1 1 12.008 16 4 4 0 0 1 8 12.008" />
7
+ </svg>
8
+ );
9
+ const ForwardRef = forwardRef(SvgComponent);
10
+ const Memo = memo(ForwardRef);
11
+ export default Memo;
@@ -0,0 +1,24 @@
1
+ import { type Ref, memo } from 'react';
2
+ import { match } from 'ts-pattern';
3
+ import FacebookLogo from './sns-icon.facebook';
4
+ import InstaLogo from './sns-icon.instagram';
5
+ import XLogo from './sns-icon.x';
6
+ import YoutubeLogo from './sns-icon.youtube';
7
+
8
+ interface Props extends React.SVGProps<SVGSVGElement> {
9
+ social: 'instagram' | 'x' | 'facebook' | 'youtube';
10
+ ref?: Ref<SVGSVGElement>;
11
+ }
12
+
13
+ export const SNSIcon = memo(({ social, ...svgProps }: Props) => {
14
+ const Component = match(social)
15
+ .with('instagram', () => <InstaLogo {...svgProps} />)
16
+ .with('x', () => <XLogo {...svgProps} />)
17
+ .with('facebook', () => <FacebookLogo {...svgProps} />)
18
+ .with('youtube', () => <YoutubeLogo {...svgProps} />)
19
+ .exhaustive();
20
+
21
+ return Component;
22
+ });
23
+
24
+ SNSIcon.displayName = 'SNSIcon';
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ import { type Ref, type SVGProps, forwardRef, memo } from 'react';
3
+ const SvgComponent = (props: SVGProps<SVGSVGElement>, ref: Ref<SVGSVGElement>) => (
4
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" ref={ref} {...props}>
5
+ <title>{'X'}</title>
6
+ <path d="M14.234 10.162 22.977 0h-2.072l-7.591 8.824L7.251 0H.258l9.168 13.343L.258 24H2.33l8.016-9.318L16.749 24h6.993zm-2.837 3.299-.929-1.329L3.076 1.56h3.182l5.965 8.532.929 1.329 7.754 11.09h-3.182z" />
7
+ </svg>
8
+ );
9
+ const ForwardRef = forwardRef(SvgComponent);
10
+ const Memo = memo(ForwardRef);
11
+ export default Memo;
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ import { type Ref, type SVGProps, forwardRef, memo } from 'react';
3
+ const SvgComponent = (props: SVGProps<SVGSVGElement>, ref: Ref<SVGSVGElement>) => (
4
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" ref={ref} {...props}>
5
+ <title>{'YouTube'}</title>
6
+ <path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
7
+ </svg>
8
+ );
9
+ const ForwardRef = forwardRef(SvgComponent);
10
+ const Memo = memo(ForwardRef);
11
+ export default Memo;
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ export * from '@/contexts/ColorSchemeProvider';
2
+ export { default as ColorSchemeProvider, useColorScheme } from '@/contexts/ColorSchemeProvider';
3
+ export { default as GlobalStyle } from '@/GlobalStyle';
4
+ export * from '@/base';
5
+ export * from '@/tokens';
6
+ export * from '@/utils';
7
+ export * from '@/extensions';
8
+ export * from './constants';
@@ -0,0 +1,99 @@
1
+ import color from '@coldsurfers/ocean-road-design-tokens/js/color/variables';
2
+ import styled, { css } from '@emotion/native';
3
+ import { icons as Icons } from 'lucide-react-native';
4
+ import type { ButtonTheme } from '../../base/button';
5
+ import { getButtonBackgroundColor, getButtonForegroundColor } from '../../base/button/button.utils';
6
+ import { colors } from '../../tokens';
7
+ import { Text } from '../text';
8
+
9
+ export const StyledButton = styled.TouchableOpacity<{
10
+ colorTheme: ButtonTheme;
11
+ size: 'lg' | 'md' | 'sm';
12
+ }>`
13
+ flex-direction: row;
14
+ align-items: center;
15
+ justify-content: center;
16
+ background-color: ${({ colorTheme, disabled }) =>
17
+ disabled ? color.oc.gray[4].value : getButtonBackgroundColor(colorTheme)};
18
+ padding: ${(props) => {
19
+ switch (props.size) {
20
+ case 'lg':
21
+ return '14px';
22
+ case 'md':
23
+ return '10px';
24
+ default:
25
+ return '6px';
26
+ }
27
+ }};
28
+ border-radius: ${(props) => {
29
+ switch (props.size) {
30
+ case 'lg':
31
+ return '22px';
32
+ case 'md':
33
+ return '16px';
34
+ default:
35
+ return '10px';
36
+ }
37
+ }};
38
+ ${(props) => css`
39
+ border-width: ${props.colorTheme === 'border' && '2px'};
40
+ border-color: ${props.colorTheme === 'border' && colors.oc.black.value};
41
+ `}
42
+
43
+ font-family: inherit;
44
+
45
+ opacity: ${(props) => (props.colorTheme === 'transparentDarkGray' ? 0.5 : 1.0)};
46
+ `;
47
+
48
+ export const StyledButtonText = styled(Text)<{ colorTheme: ButtonTheme; size: 'md' | 'sm' | 'lg' }>`
49
+ font-weight: 700;
50
+ color: ${({ colorTheme }) => getButtonForegroundColor(colorTheme)};
51
+ font-family: inherit;
52
+ font-size: ${(props) => {
53
+ switch (props.size) {
54
+ case 'lg':
55
+ return '14px';
56
+ case 'md':
57
+ return '12px';
58
+ default:
59
+ return '10px';
60
+ }
61
+ }};
62
+ `;
63
+
64
+ const iconSize = (size: 'lg' | 'md' | 'sm') => {
65
+ switch (size) {
66
+ case 'lg':
67
+ return 18;
68
+ case 'md':
69
+ return 16;
70
+ default:
71
+ return 14;
72
+ }
73
+ };
74
+
75
+ export const createStyledIconNative = (
76
+ icon: keyof typeof Icons,
77
+ size: 'lg' | 'md' | 'sm',
78
+ position: 'left' | 'right'
79
+ ) => {
80
+ const TargetIcon = styled(Icons[icon])`
81
+ color: ${colors.oc.white.value};
82
+ `;
83
+
84
+ return (
85
+ <TargetIcon
86
+ size={iconSize(size)}
87
+ strokeWidth={3}
88
+ style={{
89
+ marginLeft: position === 'right' ? 4 : undefined,
90
+ marginRight: position === 'left' ? 4 : undefined,
91
+ }}
92
+ />
93
+ );
94
+ };
95
+
96
+ export const StyledIconWrapper = styled.View<{ $position: 'left' | 'right' }>`
97
+ margin-right: ${({ $position }) => ($position === 'left' ? '4px' : '0px')};
98
+ margin-left: ${({ $position }) => ($position === 'right' ? '4px' : '0px')};
99
+ `;
@@ -0,0 +1,42 @@
1
+ import type { icons as Icons } from 'lucide-react-native';
2
+ import { type ElementRef, forwardRef } from 'react';
3
+ import type { TouchableOpacity, TouchableOpacityProps } from 'react-native';
4
+ import type { ButtonProps } from '../../base/button';
5
+ import {
6
+ StyledButton,
7
+ StyledButtonText,
8
+ StyledIconWrapper,
9
+ createStyledIconNative,
10
+ } from './button.styled';
11
+
12
+ export const Button = forwardRef<
13
+ ElementRef<typeof TouchableOpacity>,
14
+ ButtonProps & TouchableOpacityProps
15
+ >(
16
+ (
17
+ { children, onPress, variant = 'indigo', size = 'lg', leftIcon, rightIcon, ...otherProps },
18
+ ref
19
+ ) => (
20
+ <StyledButton ref={ref} onPress={onPress} colorTheme={variant} size={size} {...otherProps}>
21
+ {leftIcon && typeof leftIcon === 'string' ? (
22
+ createStyledIconNative(leftIcon as keyof typeof Icons, size, 'left')
23
+ ) : (
24
+ <StyledIconWrapper $position="left">{leftIcon}</StyledIconWrapper>
25
+ )}
26
+ {typeof children === 'string' ? (
27
+ <StyledButtonText colorTheme={variant} size={size}>
28
+ {children}
29
+ </StyledButtonText>
30
+ ) : (
31
+ children
32
+ )}
33
+ {rightIcon && typeof rightIcon === 'string' ? (
34
+ createStyledIconNative(rightIcon as keyof typeof Icons, size, 'right')
35
+ ) : (
36
+ <StyledIconWrapper $position="right">{rightIcon}</StyledIconWrapper>
37
+ )}
38
+ </StyledButton>
39
+ )
40
+ );
41
+
42
+ Button.displayName = 'Button';
@@ -0,0 +1 @@
1
+ export * from './button';
@@ -0,0 +1,45 @@
1
+ import { type PropsWithChildren, createContext, useContext, useMemo, useState } from 'react';
2
+ import type { ColorScheme } from '../../../contexts/ColorSchemeProvider';
3
+ import { semanticVariables } from '../../../tokens/tokens';
4
+
5
+ export const ColorSchemeContext = createContext<{
6
+ colorScheme: ColorScheme;
7
+ semantics:
8
+ | (typeof semanticVariables)['light']['color']
9
+ | (typeof semanticVariables)['dark']['color'];
10
+ setColorScheme: (colorScheme: ColorScheme) => void;
11
+ }>({
12
+ colorScheme: 'light',
13
+ semantics: semanticVariables.light.color,
14
+ setColorScheme: (colorScheme) => {
15
+ console.log(colorScheme);
16
+ },
17
+ });
18
+
19
+ export const ColorSchemeProvider = ({
20
+ children,
21
+ initialColorScheme,
22
+ }: PropsWithChildren<{
23
+ initialColorScheme?: ColorScheme;
24
+ }>) => {
25
+ const [colorScheme, setColorScheme] = useState<ColorScheme>(initialColorScheme ?? 'light');
26
+ const semantics = useMemo(() => {
27
+ switch (colorScheme) {
28
+ case 'light':
29
+ return semanticVariables.light.color;
30
+ case 'dark':
31
+ return semanticVariables.dark.color;
32
+ case 'userPreference':
33
+ return semanticVariables.light.color;
34
+ }
35
+ }, [colorScheme]);
36
+ return (
37
+ <ColorSchemeContext.Provider value={{ colorScheme, setColorScheme, semantics }}>
38
+ {children}
39
+ </ColorSchemeContext.Provider>
40
+ );
41
+ };
42
+
43
+ export const useColorScheme = () => {
44
+ return useContext(ColorSchemeContext);
45
+ };
@@ -0,0 +1 @@
1
+ export * from './color-scheme-context';
@@ -0,0 +1 @@
1
+ export * from './color-scheme-context';
@@ -0,0 +1,6 @@
1
+ import styled from '@emotion/native';
2
+
3
+ export const StyledIconButtonContainer = styled.TouchableOpacity`
4
+ justify-content: center;
5
+ align-items: center;
6
+ `;
@@ -0,0 +1,33 @@
1
+ import { icons as Icons } from 'lucide-react-native';
2
+ import { colors } from '../../tokens';
3
+ import { StyledIconButtonContainer } from './icon-button.styled';
4
+ import type { IconButtonProps } from './icon-button.types';
5
+ import { getIconButtonBackgroundStyles, getIconButtonSizeStyles, sizes } from './icon-button.utils';
6
+
7
+ export const IconButton = ({
8
+ icon,
9
+ onPress,
10
+ variant = 'transparent',
11
+ size = 'md',
12
+ style,
13
+ color,
14
+ strokeWidth,
15
+ fill,
16
+ ...otherProps
17
+ }: IconButtonProps) => {
18
+ const TargetIcon = Icons[icon];
19
+ return (
20
+ <StyledIconButtonContainer
21
+ {...otherProps}
22
+ style={[getIconButtonSizeStyles(size), getIconButtonBackgroundStyles(variant), style]}
23
+ onPress={onPress}
24
+ >
25
+ <TargetIcon
26
+ fill={fill}
27
+ size={sizes[size] - 12}
28
+ strokeWidth={strokeWidth ?? 4}
29
+ color={color ?? colors.oc.white.value}
30
+ />
31
+ </StyledIconButtonContainer>
32
+ );
33
+ };
@@ -0,0 +1,14 @@
1
+ import type { icons } from 'lucide-react-native';
2
+ import type { ColorValue, TouchableOpacityProps } from 'react-native';
3
+ import type { ButtonTheme } from '../../base/button';
4
+
5
+ export interface IconButtonProps extends TouchableOpacityProps {
6
+ icon: keyof typeof icons;
7
+ size?: IconButtonSize;
8
+ variant?: ButtonTheme;
9
+ color?: ColorValue;
10
+ strokeWidth?: number;
11
+ fill?: ColorValue;
12
+ }
13
+
14
+ export type IconButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';