@team-monolith/cds 1.63.3 → 1.64.1
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/@types/emotion.d.ts +14 -0
- package/dist/CodleDesignSystemProvider.js +28 -0
- package/dist/components/Book/Book.d.ts +28 -0
- package/dist/components/Book/Book.js +246 -0
- package/dist/components/Book/index.d.ts +2 -0
- package/dist/components/Book/index.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +2 -1
package/@types/emotion.d.ts
CHANGED
|
@@ -106,6 +106,20 @@ interface CodleColors {
|
|
|
106
106
|
ebookAlt: string;
|
|
107
107
|
makecode: string;
|
|
108
108
|
makecodeAlt: string;
|
|
109
|
+
decorativeRed: string;
|
|
110
|
+
decorativePink: string;
|
|
111
|
+
decorativeAmber: string;
|
|
112
|
+
decorativeGrass: string;
|
|
113
|
+
decorativeGreen: string;
|
|
114
|
+
decorativeBlue: string;
|
|
115
|
+
decorativeIndigo: string;
|
|
116
|
+
decorativePurple: string;
|
|
117
|
+
decorativeBrown: string;
|
|
118
|
+
decorativeCopper: string;
|
|
119
|
+
decorativeGold: string;
|
|
120
|
+
decorativeSilver: string;
|
|
121
|
+
decorativeGray: string;
|
|
122
|
+
decorativeBlack: string;
|
|
109
123
|
};
|
|
110
124
|
blanket: {
|
|
111
125
|
neutral: string;
|
|
@@ -111,6 +111,20 @@ export const light = {
|
|
|
111
111
|
ebookAlt: "#E0F2FF",
|
|
112
112
|
makecode: "#AA278F",
|
|
113
113
|
makecodeAlt: "#FFE0F9",
|
|
114
|
+
decorativeRed: "#D73152",
|
|
115
|
+
decorativePink: "#D13D78",
|
|
116
|
+
decorativeAmber: "#CC4827",
|
|
117
|
+
decorativeGrass: "#6E7B3F",
|
|
118
|
+
decorativeGreen: "#26864B",
|
|
119
|
+
decorativeBlue: "#0076D0",
|
|
120
|
+
decorativeIndigo: "#5863FF",
|
|
121
|
+
decorativePurple: "#9A3BBC",
|
|
122
|
+
decorativeBrown: "#996C49",
|
|
123
|
+
decorativeCopper: "#8A6470",
|
|
124
|
+
decorativeGold: "#876E3F",
|
|
125
|
+
decorativeSilver: "#6A7781",
|
|
126
|
+
decorativeGray: "#747474",
|
|
127
|
+
decorativeBlack: "#474658",
|
|
114
128
|
},
|
|
115
129
|
blanket: {
|
|
116
130
|
neutral: COLOR.alpha030,
|
|
@@ -228,6 +242,20 @@ export const dark = {
|
|
|
228
242
|
ebookAlt: "#475966",
|
|
229
243
|
makecode: "#AA278F",
|
|
230
244
|
makecodeAlt: "#664760",
|
|
245
|
+
decorativeRed: "#D73152",
|
|
246
|
+
decorativePink: "#D13D78",
|
|
247
|
+
decorativeAmber: "#CC4827",
|
|
248
|
+
decorativeGrass: "#6E7B3F",
|
|
249
|
+
decorativeGreen: "#26864B",
|
|
250
|
+
decorativeBlue: "#0076D0",
|
|
251
|
+
decorativeIndigo: "#5863FF",
|
|
252
|
+
decorativePurple: "#9A3BBC",
|
|
253
|
+
decorativeBrown: "#996C49",
|
|
254
|
+
decorativeCopper: "#8A6470",
|
|
255
|
+
decorativeGold: "#876E3F",
|
|
256
|
+
decorativeSilver: "#6A7781",
|
|
257
|
+
decorativeGray: "#747474",
|
|
258
|
+
decorativeBlack: "#474658",
|
|
231
259
|
},
|
|
232
260
|
blanket: {
|
|
233
261
|
neutral: COLOR.alphaf20,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface BookProps {
|
|
3
|
+
className?: string;
|
|
4
|
+
/** 제목 영역 (하단에 노출되는) 텍스트 */
|
|
5
|
+
title?: string;
|
|
6
|
+
/** subtitle 영역 (상단) 텍스트 */
|
|
7
|
+
subtitle?: string;
|
|
8
|
+
/** subtitleBold 영역 (상단, subtitle 영역보다 1줄 낮음) 텍스트 */
|
|
9
|
+
subtitleBold?: string;
|
|
10
|
+
/** 숨김 상태인지 여부 */
|
|
11
|
+
isHidden?: boolean;
|
|
12
|
+
/** 버튼 영역 (최하단에 노출되는) */
|
|
13
|
+
buttons?: React.ReactNode;
|
|
14
|
+
/** 전달하면 controlled component로 애니메이션을 제어할 수 있다 */
|
|
15
|
+
open?: boolean;
|
|
16
|
+
/** 커버 hover 시 발생하는 이벤트 */
|
|
17
|
+
onHoverStart?: () => void;
|
|
18
|
+
/** 커버 hover 종료 시 발생하는 이벤트 */
|
|
19
|
+
onHoverEnd?: () => void;
|
|
20
|
+
/** 커버 클릭 시 발생하는 이벤트 */
|
|
21
|
+
onCoverClick?: () => void;
|
|
22
|
+
/** 커버에 노출되는 아이콘 */
|
|
23
|
+
icon?: React.ReactNode;
|
|
24
|
+
/** 커버 색상 */
|
|
25
|
+
coverColor: string;
|
|
26
|
+
}
|
|
27
|
+
declare const _default: React.ForwardRefExoticComponent<BookProps & React.RefAttributes<HTMLDivElement>>;
|
|
28
|
+
export default _default;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
|
+
/** @jsxImportSource @emotion/react */
|
|
3
|
+
import { css, useTheme } from "@emotion/react";
|
|
4
|
+
import styled from "@emotion/styled";
|
|
5
|
+
import { forwardRef, useState } from "react";
|
|
6
|
+
import shadows from "../../foundation/shadows";
|
|
7
|
+
import { EyeOffFillIcon } from "../../icons";
|
|
8
|
+
import { motion } from "framer-motion";
|
|
9
|
+
// 이 ZINDEX는 Book 컴포넌트 내의 Stacking Context 에서 활용되므로
|
|
10
|
+
// 다른 글로벌 ZINDEX와 충돌을 고려할 필요가 없습니다.
|
|
11
|
+
const ZINDEX_BUTTONS = 5;
|
|
12
|
+
const ZINDEX_HIDE = 4;
|
|
13
|
+
const ZINDEX_TEXT = 3;
|
|
14
|
+
const ZINDEX_COVER = 2;
|
|
15
|
+
const ZINDEX_ICON = 1;
|
|
16
|
+
const BOOK_HEIGHT = 235;
|
|
17
|
+
const HOVER_Y_OFFSET = 12;
|
|
18
|
+
const SHELF_HEIGHT = 16;
|
|
19
|
+
const BOOK_SHELF_GAP = 10;
|
|
20
|
+
const BUTTONS_HEIGHT = 36; // Button의 높이는 36px (small)로 가정합니다.
|
|
21
|
+
const coverMotion = {
|
|
22
|
+
closed: {
|
|
23
|
+
y: 0,
|
|
24
|
+
boxShadow: shadows.shadow04,
|
|
25
|
+
transition: { duration: 0.3 },
|
|
26
|
+
},
|
|
27
|
+
open: {
|
|
28
|
+
y: -HOVER_Y_OFFSET,
|
|
29
|
+
boxShadow: shadows.shadow16,
|
|
30
|
+
transition: { duration: 0.3 },
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
const textMotion = {
|
|
34
|
+
closed: {
|
|
35
|
+
paddingBottom: 12,
|
|
36
|
+
transition: { duration: 0.3 },
|
|
37
|
+
},
|
|
38
|
+
open: {
|
|
39
|
+
paddingBottom: 12 + BUTTONS_HEIGHT + 4,
|
|
40
|
+
transition: { duration: 0.3 },
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
const buttonsMotion = {
|
|
44
|
+
closed: {
|
|
45
|
+
opacity: 0,
|
|
46
|
+
pointerEvents: "none",
|
|
47
|
+
transition: { duration: 0.3 },
|
|
48
|
+
},
|
|
49
|
+
open: {
|
|
50
|
+
opacity: 1,
|
|
51
|
+
pointerEvents: "auto",
|
|
52
|
+
transition: { duration: 0.3 },
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
const CARD_COLOR_PALETTE = (theme) => ({
|
|
56
|
+
border: theme.color.foreground.primaryDisabled,
|
|
57
|
+
text: theme.color.foreground.neutralAlt,
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* Framer Motion의 whileHover를 사용하지 않고 직접 애니메이션 상태를 관리합니다.
|
|
61
|
+
* 내부적으로는 간단한 애니메이션 상태 관리가 있으며, 필요한 경우
|
|
62
|
+
* open 상태를 전달하여 외부에서 관리할 수 있습니다.
|
|
63
|
+
*
|
|
64
|
+
* 사유1)
|
|
65
|
+
* 모바일에서는 hover가 없기 때문에 whileHover를 사용할 수 없습니다.
|
|
66
|
+
* 클릭(탭)을 통해 열고 닫을 수 있어야 합니다.
|
|
67
|
+
*
|
|
68
|
+
* 사유2)
|
|
69
|
+
* 관리/배부 드랍다운 Modal이 Card 위로 그려져 hoverEnd를 유발하기 때문에
|
|
70
|
+
* 특정 상황에서는 hoverEnd가 발생했어도 Card를 열고 있어야 합니다.
|
|
71
|
+
* @param props
|
|
72
|
+
* @param ref
|
|
73
|
+
* @returns
|
|
74
|
+
*/
|
|
75
|
+
function Book(props, ref) {
|
|
76
|
+
const { className, title, subtitle, subtitleBold, coverColor, isHidden = false, buttons, open: controlledOpen, onHoverStart, onHoverEnd, onCoverClick, icon, } = props;
|
|
77
|
+
const theme = useTheme();
|
|
78
|
+
const [open, setOpen] = useState(false);
|
|
79
|
+
return (_jsxs(Container, Object.assign({ initial: "closed", animate: (controlledOpen !== null && controlledOpen !== void 0 ? controlledOpen : open) ? "open" : "closed", ref: ref, onHoverStart: () => {
|
|
80
|
+
setOpen(true);
|
|
81
|
+
onHoverStart === null || onHoverStart === void 0 ? void 0 : onHoverStart();
|
|
82
|
+
}, onHoverEnd: () => {
|
|
83
|
+
setOpen(false);
|
|
84
|
+
onHoverEnd === null || onHoverEnd === void 0 ? void 0 : onHoverEnd();
|
|
85
|
+
} }, { children: [_jsxs(Cover, Object.assign({ className: className, css: onCoverClick &&
|
|
86
|
+
css `
|
|
87
|
+
cursor: pointer;
|
|
88
|
+
`, backgroundColor: coverColor, variants: coverMotion, onClick: () => {
|
|
89
|
+
setOpen(!open);
|
|
90
|
+
onCoverClick === null || onCoverClick === void 0 ? void 0 : onCoverClick();
|
|
91
|
+
} }, { children: [icon && _jsx(CoverIcon, { children: icon }), isHidden && (_jsxs(_Fragment, { children: [_jsx(Hide, {}), _jsx(EyeOffFillIcon, { color: theme.color.foreground.neutralAlt, css: css `
|
|
92
|
+
position: absolute;
|
|
93
|
+
z-index: ${ZINDEX_HIDE};
|
|
94
|
+
left: 50%;
|
|
95
|
+
top: 50%;
|
|
96
|
+
transform: translate(-50%, -50%);
|
|
97
|
+
opacity: 0.8;
|
|
98
|
+
` })] })), _jsxs(CoverTexts, Object.assign({ variants: buttons ? textMotion : undefined }, { children: [_jsx(Subtitle, { children: subtitle }), _jsx(Subtitle, Object.assign({ css: css `
|
|
99
|
+
font-weight: 800;
|
|
100
|
+
` }, { children: subtitleBold })), _jsx(CoverTitle, { children: title })] })), _jsx(BookGradient, {}), _jsx(Buttons, Object.assign({ variants: buttonsMotion, onClick: (e) => {
|
|
101
|
+
// Button 클릭 시에도 CoverClick 이벤트가 발생하지 않도록 막습니다.
|
|
102
|
+
e.stopPropagation();
|
|
103
|
+
} }, { children: buttons }))] })), _jsx(Shelf, {})] })));
|
|
104
|
+
}
|
|
105
|
+
export default forwardRef(Book);
|
|
106
|
+
const Container = styled(motion.div) `
|
|
107
|
+
position: relative;
|
|
108
|
+
|
|
109
|
+
width: 200px;
|
|
110
|
+
height: ${BOOK_HEIGHT + HOVER_Y_OFFSET + SHELF_HEIGHT + BOOK_SHELF_GAP}px;
|
|
111
|
+
|
|
112
|
+
padding-top: ${HOVER_Y_OFFSET}px;
|
|
113
|
+
`;
|
|
114
|
+
const Cover = styled(motion.div)(({ backgroundColor }) => css `
|
|
115
|
+
height: ${BOOK_HEIGHT}px;
|
|
116
|
+
|
|
117
|
+
border-radius: 8px;
|
|
118
|
+
background: ${backgroundColor};
|
|
119
|
+
box-shadow: ${shadows.shadow04};
|
|
120
|
+
|
|
121
|
+
display: flex;
|
|
122
|
+
flex-direction: row;
|
|
123
|
+
gap: 12px;
|
|
124
|
+
overflow: hidden;
|
|
125
|
+
|
|
126
|
+
position: relative;
|
|
127
|
+
bottom: 0px;
|
|
128
|
+
z-index: ${ZINDEX_COVER};
|
|
129
|
+
`);
|
|
130
|
+
const CoverTexts = styled(motion.div) `
|
|
131
|
+
width: 100%;
|
|
132
|
+
height: 100%;
|
|
133
|
+
padding: 12px 12px 12px 24px;
|
|
134
|
+
|
|
135
|
+
position: absolute;
|
|
136
|
+
top: 0;
|
|
137
|
+
left: 0;
|
|
138
|
+
z-index: ${ZINDEX_TEXT};
|
|
139
|
+
|
|
140
|
+
display: flex;
|
|
141
|
+
flex-direction: column;
|
|
142
|
+
gap: 4px;
|
|
143
|
+
`;
|
|
144
|
+
const CoverTitle = styled.h1(({ theme }) => css `
|
|
145
|
+
word-wrap: break-word;
|
|
146
|
+
word-break: keep-all;
|
|
147
|
+
|
|
148
|
+
margin: auto 0 8px 0;
|
|
149
|
+
color: ${CARD_COLOR_PALETTE(theme).text};
|
|
150
|
+
|
|
151
|
+
/* Alt/Paragraph/16px-Eb */
|
|
152
|
+
font-family: ${theme.fontFamily.title};
|
|
153
|
+
font-size: 16px;
|
|
154
|
+
font-style: normal;
|
|
155
|
+
font-weight: 800;
|
|
156
|
+
line-height: 24px;
|
|
157
|
+
|
|
158
|
+
/* multiline 말줄임말 적용 css */
|
|
159
|
+
overflow: hidden;
|
|
160
|
+
text-overflow: ellipsis;
|
|
161
|
+
display: -webkit-box;
|
|
162
|
+
-webkit-line-clamp: 7;
|
|
163
|
+
-webkit-box-orient: vertical;
|
|
164
|
+
`);
|
|
165
|
+
const BookGradient = styled.div `
|
|
166
|
+
width: 200px;
|
|
167
|
+
height: ${BOOK_HEIGHT}px;
|
|
168
|
+
background: linear-gradient(
|
|
169
|
+
180deg,
|
|
170
|
+
rgba(255, 255, 255, 0) 0%,
|
|
171
|
+
rgba(0, 0, 0, 0.75) 100%
|
|
172
|
+
),
|
|
173
|
+
linear-gradient(
|
|
174
|
+
90deg,
|
|
175
|
+
#999 0%,
|
|
176
|
+
#fbfbfb 2%,
|
|
177
|
+
#fbfbfb 4%,
|
|
178
|
+
#cdcdcd 6%,
|
|
179
|
+
#fbfbfb 10%,
|
|
180
|
+
#fbfbfb 100%
|
|
181
|
+
);
|
|
182
|
+
mix-blend-mode: multiply;
|
|
183
|
+
|
|
184
|
+
position: absolute;
|
|
185
|
+
top: 0;
|
|
186
|
+
left: 0;
|
|
187
|
+
border-radius: 8px;
|
|
188
|
+
z-index: ${ZINDEX_COVER};
|
|
189
|
+
`;
|
|
190
|
+
const Buttons = styled(motion.div) `
|
|
191
|
+
display: flex;
|
|
192
|
+
gap: 8px;
|
|
193
|
+
width: 100%;
|
|
194
|
+
padding: 0 12px 0 24px;
|
|
195
|
+
|
|
196
|
+
position: absolute;
|
|
197
|
+
bottom: 12px;
|
|
198
|
+
z-index: ${ZINDEX_BUTTONS};
|
|
199
|
+
`;
|
|
200
|
+
const Shelf = styled.div(({ theme }) => css `
|
|
201
|
+
height: ${SHELF_HEIGHT}px;
|
|
202
|
+
width: calc(100% + 32px);
|
|
203
|
+
border-radius: 4px;
|
|
204
|
+
background: ${theme.color.background.neutralAltActive};
|
|
205
|
+
|
|
206
|
+
position: absolute;
|
|
207
|
+
bottom: 0px;
|
|
208
|
+
left: -16px;
|
|
209
|
+
`);
|
|
210
|
+
const Hide = styled.div(({ theme }) => css `
|
|
211
|
+
position: absolute;
|
|
212
|
+
left: 0;
|
|
213
|
+
top: 0;
|
|
214
|
+
|
|
215
|
+
width: 100%;
|
|
216
|
+
height: 100%;
|
|
217
|
+
|
|
218
|
+
background: ${theme.color.blanket.neutral};
|
|
219
|
+
border-radius: 8px;
|
|
220
|
+
|
|
221
|
+
z-index: ${ZINDEX_HIDE};
|
|
222
|
+
`);
|
|
223
|
+
const Subtitle = styled.div(({ theme }) => css `
|
|
224
|
+
color: ${CARD_COLOR_PALETTE(theme).text};
|
|
225
|
+
|
|
226
|
+
/* Default/Label/12px-Md */
|
|
227
|
+
font-family: ${theme.fontFamily.ui};
|
|
228
|
+
font-size: 12px;
|
|
229
|
+
font-style: normal;
|
|
230
|
+
font-weight: 500;
|
|
231
|
+
line-height: 16px; /* 133.333% */
|
|
232
|
+
`);
|
|
233
|
+
const CoverIcon = styled.div(({ theme }) => css `
|
|
234
|
+
position: absolute;
|
|
235
|
+
top: 50%;
|
|
236
|
+
left: 100px;
|
|
237
|
+
transform: translateY(-50%);
|
|
238
|
+
|
|
239
|
+
svg {
|
|
240
|
+
width: 120px;
|
|
241
|
+
height: 120px;
|
|
242
|
+
color: ${theme.color.blanket.neutral};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
z-index: ${ZINDEX_ICON};
|
|
246
|
+
`);
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export * from "./components/AlertDialog";
|
|
|
3
3
|
export * from "./components/DecoratedNumber";
|
|
4
4
|
export { default as Banner } from "./components/Banner";
|
|
5
5
|
export * from "./components/Banner";
|
|
6
|
+
export { default as Book } from "./components/Book";
|
|
7
|
+
export * from "./components/Book";
|
|
6
8
|
export { default as Button } from "./components/Button";
|
|
7
9
|
export * from "./components/Button";
|
|
8
10
|
export { default as CheckboxInput } from "./components/CheckboxInput";
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,8 @@ export * from "./components/AlertDialog";
|
|
|
3
3
|
export * from "./components/DecoratedNumber";
|
|
4
4
|
export { default as Banner } from "./components/Banner";
|
|
5
5
|
export * from "./components/Banner";
|
|
6
|
+
export { default as Book } from "./components/Book";
|
|
7
|
+
export * from "./components/Book";
|
|
6
8
|
export { default as Button } from "./components/Button";
|
|
7
9
|
export * from "./components/Button";
|
|
8
10
|
export { default as CheckboxInput } from "./components/CheckboxInput";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-monolith/cds",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.64.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"@types/node": "^16.11.26",
|
|
14
14
|
"@types/react": "^18.2.28",
|
|
15
15
|
"@types/react-dom": "^18.2.13",
|
|
16
|
+
"framer-motion": "^11.3.19",
|
|
16
17
|
"hex-to-css-filter": "^5.4.0",
|
|
17
18
|
"lexical": "^0.12.4",
|
|
18
19
|
"lodash": "^4.17.21",
|