@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.
- package/dist/css/global.css +30 -0
- package/dist/index.d.ts +641 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +733 -0
- package/dist/index.js.map +1 -0
- package/dist/native.cjs +94 -0
- package/dist/native.cjs.map +1 -0
- package/dist/native.d.cts +304 -0
- package/dist/native.d.cts.map +1 -0
- package/dist/native.d.ts +304 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +94 -0
- package/dist/native.js.map +1 -0
- package/dist/next.cjs +949 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +270 -0
- package/dist/next.d.cts.map +1 -0
- package/dist/next.d.ts +270 -0
- package/dist/next.d.ts.map +1 -0
- package/dist/next.js +949 -0
- package/dist/next.js.map +1 -0
- package/native/index.d.ts +7 -0
- package/next/index.d.ts +7 -0
- package/package.json +126 -0
- package/src/GlobalStyle.tsx +111 -0
- package/src/base/badge/badge.tsx +50 -0
- package/src/base/badge/index.ts +1 -0
- package/src/base/button/button.styled.tsx +123 -0
- package/src/base/button/button.tsx +60 -0
- package/src/base/button/button.types.ts +20 -0
- package/src/base/button/button.utils.ts +36 -0
- package/src/base/button/index.tsx +2 -0
- package/src/base/checkbox/checkbox.styled.ts +52 -0
- package/src/base/checkbox/checkbox.tsx +26 -0
- package/src/base/checkbox/index.ts +1 -0
- package/src/base/icon-button/icon-button.styled.ts +8 -0
- package/src/base/icon-button/icon-button.tsx +15 -0
- package/src/base/icon-button/icon-button.types.ts +3 -0
- package/src/base/icon-button/index.ts +2 -0
- package/src/base/index.ts +11 -0
- package/src/base/label/index.ts +1 -0
- package/src/base/label/label.styled.ts +7 -0
- package/src/base/label/label.tsx +27 -0
- package/src/base/modal/index.ts +1 -0
- package/src/base/modal/modal.tsx +59 -0
- package/src/base/spinner/index.ts +2 -0
- package/src/base/spinner/spinner.styled.ts +25 -0
- package/src/base/spinner/spinner.tsx +36 -0
- package/src/base/spinner/spinner.types.ts +1 -0
- package/src/base/switch/index.ts +1 -0
- package/src/base/switch/switch.styled.tsx +49 -0
- package/src/base/switch/switch.tsx +29 -0
- package/src/base/text/index.ts +1 -0
- package/src/base/text/text.styled.ts +17 -0
- package/src/base/text/text.tsx +37 -0
- package/src/base/text-area/index.ts +2 -0
- package/src/base/text-area/text-area.styled.ts +16 -0
- package/src/base/text-area/text-area.tsx +29 -0
- package/src/base/text-area/text-area.types.ts +11 -0
- package/src/base/text-input/index.ts +2 -0
- package/src/base/text-input/text-input.styled.ts +40 -0
- package/src/base/text-input/text-input.tsx +59 -0
- package/src/base/text-input/text-input.types.ts +15 -0
- package/src/base/toast/index.ts +2 -0
- package/src/base/toast/toast.tsx +60 -0
- package/src/base/toast/toast.types.ts +5 -0
- package/src/constants.ts +1 -0
- package/src/contexts/ColorSchemeProvider.tsx +154 -0
- package/src/css/global.css +30 -0
- package/src/extensions/accordion/accordion.hooks.ts +11 -0
- package/src/extensions/accordion/accordion.tsx +80 -0
- package/src/extensions/accordion/index.ts +1 -0
- package/src/extensions/app-header/app-header.hooks.ts +94 -0
- package/src/extensions/app-header/app-header.tsx +31 -0
- package/src/extensions/app-header/app-header.types.ts +1 -0
- package/src/extensions/app-header/index.ts +8 -0
- package/src/extensions/app-logo/app-logo.tsx +40 -0
- package/src/extensions/app-logo/index.ts +1 -0
- package/src/extensions/app-store-button/app-store-button.tsx +64 -0
- package/src/extensions/app-store-button/index.ts +1 -0
- package/src/extensions/brand-icon/brand-icon.android.tsx +11 -0
- package/src/extensions/brand-icon/brand-icon.apple.tsx +11 -0
- package/src/extensions/brand-icon/brand-icon.google.tsx +11 -0
- package/src/extensions/brand-icon/brand-icon.tsx +22 -0
- package/src/extensions/brand-icon/index.ts +1 -0
- package/src/extensions/color-scheme-toggle/color-scheme-toggle.tsx +76 -0
- package/src/extensions/color-scheme-toggle/index.ts +1 -0
- package/src/extensions/dropdown/dropdown.menu-item.tsx +237 -0
- package/src/extensions/dropdown/dropdown.result-item.tsx +26 -0
- package/src/extensions/dropdown/dropdown.styled.tsx +48 -0
- package/src/extensions/dropdown/dropdown.trigger.tsx +72 -0
- package/src/extensions/dropdown/dropdown.tsx +222 -0
- package/src/extensions/dropdown/dropdown.types.ts +3 -0
- package/src/extensions/dropdown/dropdown.utils.ts +40 -0
- package/src/extensions/dropdown/index.ts +14 -0
- package/src/extensions/error-ui/index.ts +7 -0
- package/src/extensions/error-ui/network-error/index.ts +1 -0
- package/src/extensions/error-ui/network-error/network-error.styled.ts +16 -0
- package/src/extensions/error-ui/network-error/network-error.tsx +14 -0
- package/src/extensions/error-ui/unknown-error/index.ts +1 -0
- package/src/extensions/error-ui/unknown-error/unknown-error.styled.ts +16 -0
- package/src/extensions/error-ui/unknown-error/unknown-error.tsx +14 -0
- package/src/extensions/full-screen-modal/full-screen-modal.tsx +52 -0
- package/src/extensions/full-screen-modal/index.ts +1 -0
- package/src/extensions/grid-card-image/grid-card-image.tsx +11 -0
- package/src/extensions/grid-card-image/index.ts +1 -0
- package/src/extensions/grid-card-image-empty/grid-card-image-empty.tsx +11 -0
- package/src/extensions/grid-card-image-empty/index.ts +1 -0
- package/src/extensions/grid-card-item/grid-card-item.masonry.styled.tsx +95 -0
- package/src/extensions/grid-card-item/grid-card-item.masonry.tsx +63 -0
- package/src/extensions/grid-card-item/grid-card-item.styled.tsx +93 -0
- package/src/extensions/grid-card-item/grid-card-item.subscribe-btn-layout.tsx +30 -0
- package/src/extensions/grid-card-item/grid-card-item.tsx +81 -0
- package/src/extensions/grid-card-item/index.ts +2 -0
- package/src/extensions/grid-card-list/grid-card-list.masonry.styled.tsx +45 -0
- package/src/extensions/grid-card-list/grid-card-list.masonry.tsx +58 -0
- package/src/extensions/grid-card-list/grid-card-list.styled.tsx +40 -0
- package/src/extensions/grid-card-list/grid-card-list.tsx +59 -0
- package/src/extensions/grid-card-list/index.ts +2 -0
- package/src/extensions/grid-card-list-empty/grid-card-list-empty.tsx +38 -0
- package/src/extensions/grid-card-list-empty/index.ts +1 -0
- package/src/extensions/grid-card-list-load-more/grid-card-list-load-more.styled.tsx +15 -0
- package/src/extensions/grid-card-list-load-more/grid-card-list-load-more.tsx +43 -0
- package/src/extensions/grid-card-list-load-more/index.ts +1 -0
- package/src/extensions/index.ts +38 -0
- package/src/extensions/menu-item/index.ts +1 -0
- package/src/extensions/menu-item/menu-item.tsx +87 -0
- package/src/extensions/sns-icon/index.ts +1 -0
- package/src/extensions/sns-icon/sns-icon.facebook.tsx +11 -0
- package/src/extensions/sns-icon/sns-icon.instagram.tsx +11 -0
- package/src/extensions/sns-icon/sns-icon.tsx +24 -0
- package/src/extensions/sns-icon/sns-icon.x.tsx +11 -0
- package/src/extensions/sns-icon/sns-icon.youtube.tsx +11 -0
- package/src/index.ts +8 -0
- package/src/native/button/button.styled.tsx +99 -0
- package/src/native/button/button.tsx +42 -0
- package/src/native/button/index.ts +1 -0
- package/src/native/contexts/color-scheme-context/color-scheme-context.tsx +45 -0
- package/src/native/contexts/color-scheme-context/index.ts +1 -0
- package/src/native/contexts/index.ts +1 -0
- package/src/native/icon-button/icon-button.styled.ts +6 -0
- package/src/native/icon-button/icon-button.tsx +33 -0
- package/src/native/icon-button/icon-button.types.ts +14 -0
- package/src/native/icon-button/icon-button.utils.ts +114 -0
- package/src/native/icon-button/index.ts +1 -0
- package/src/native/index.ts +9 -0
- package/src/native/modal/index.ts +2 -0
- package/src/native/modal/modal.styled.ts +17 -0
- package/src/native/modal/modal.tsx +21 -0
- package/src/native/modal/modal.types.ts +8 -0
- package/src/native/profile-thumbnail/index.ts +1 -0
- package/src/native/profile-thumbnail/profile-thumbnail.tsx +91 -0
- package/src/native/spinner/index.ts +1 -0
- package/src/native/spinner/spinner.tsx +75 -0
- package/src/native/text/index.ts +2 -0
- package/src/native/text/text.tsx +51 -0
- package/src/native/text/text.types.ts +5 -0
- package/src/native/text-input/index.ts +2 -0
- package/src/native/text-input/text-input.tsx +72 -0
- package/src/native/text-input/text-input.types.ts +3 -0
- package/src/native/toast/index.ts +2 -0
- package/src/native/toast/toast.styled.ts +40 -0
- package/src/native/toast/toast.tsx +23 -0
- package/src/native/toast/toast.types.ts +10 -0
- package/src/next/app-footer/app-footer.tsx +250 -0
- package/src/next/app-footer/index.ts +1 -0
- package/src/next/app-header/app-header.fixed-header.tsx +83 -0
- package/src/next/app-header/app-header.full-screen-mobile-accordion-drawer.tsx +131 -0
- package/src/next/app-header/app-header.logo.tsx +50 -0
- package/src/next/app-header/app-header.modal-mobile-accordion-drawer.tsx +69 -0
- package/src/next/app-header/app-header.styled.ts +160 -0
- package/src/next/app-header/app-header.tsx +91 -0
- package/src/next/app-header/index.ts +13 -0
- package/src/next/global-link/global-link.store.ts +41 -0
- package/src/next/global-link/global-link.tsx +52 -0
- package/src/next/global-link/global-link.utils.ts +9 -0
- package/src/next/global-link/index.ts +3 -0
- package/src/next/grid-card-item/grid-card-item.masonry.tsx +23 -0
- package/src/next/grid-card-item/grid-card-item.tsx +23 -0
- package/src/next/grid-card-item/index.ts +2 -0
- package/src/next/index.ts +16 -0
- package/src/next/new-tab-link/index.ts +1 -0
- package/src/next/new-tab-link/new-tab-link.tsx +15 -0
- package/src/next/route-loading/index.ts +1 -0
- package/src/next/route-loading/route-loading.tsx +21 -0
- package/src/tokens/index.ts +2 -0
- package/src/tokens/tokens.ts +8 -0
- package/src/tokens/tokens.types.ts +7 -0
- package/src/utils/breakpoints.ts +9 -0
- package/src/utils/common-styles.ts +23 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/media.ts +23 -0
- package/src/utils/use-prevent-scroll-effect.ts +19 -0
- package/src/utils/with-id.ts +3 -0
- 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,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,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';
|