@team-monolith/cds 1.61.0 → 1.62.0

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/index.d.ts CHANGED
@@ -3,7 +3,6 @@ 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
6
  export { default as Button } from "./components/Button";
8
7
  export * from "./components/Button";
9
8
  export { default as CheckboxInput } from "./components/CheckboxInput";
package/dist/index.js CHANGED
@@ -3,7 +3,6 @@ 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
6
  export { default as Button } from "./components/Button";
8
7
  export * from "./components/Button";
9
8
  export { default as CheckboxInput } from "./components/CheckboxInput";
@@ -2,18 +2,18 @@ import React from "react";
2
2
  import { ImageProps } from "../../../components/InsertImageDialog";
3
3
  export type SelectBoxType = "primary" | "success" | "danger";
4
4
  /** 비지니스 로직과 무관한 SelectBox를 그리는 공통 컴포넌트입니다. */
5
- export declare function SelectBoxComponent(props: {
6
- className?: string;
7
- indexClassName?: string;
8
- contentClassName?: string;
5
+ export declare const SelectBoxComponent: React.ForwardRefExoticComponent<{
6
+ className?: string | undefined;
7
+ indexClassName?: string | undefined;
8
+ contentClassName?: string | undefined;
9
9
  /** SelectBox의 스타일 타입. 전달하지 않으면 기본 스타일이 적용됩니다. */
10
- type?: SelectBoxType;
10
+ type?: SelectBoxType | undefined;
11
11
  /** 문제 번호 박스 내부를 그립니다. */
12
12
  index: React.ReactNode;
13
- image?: ImageProps | null;
13
+ image?: ImageProps | null | undefined;
14
14
  text: React.ReactNode;
15
15
  /** SelectBox content 우측에 표시될 부분 */
16
16
  endIcon?: React.ReactNode;
17
- /** 박스를 클릭할 때 실행될 콜백. 존재하지 않으면 cursor: pointer가 적용되지 않습니다. */
18
- onClick?: () => void;
19
- }): import("@emotion/react/jsx-runtime").JSX.Element;
17
+ /** 박스를 클릭할 때 실행될 콜백. 전달하지 않으면 cursor: pointer가 적용되지 않습니다. */
18
+ onClick?: (() => void) | undefined;
19
+ } & Partial<React.HTMLAttributes<HTMLDivElement>> & React.RefAttributes<HTMLDivElement>>;
@@ -1,7 +1,19 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
1
12
  import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
13
  /** @jsxImportSource @emotion/react */
3
14
  import { css, useTheme } from "@emotion/react";
4
15
  import styled from "@emotion/styled";
16
+ import { forwardRef } from "react";
5
17
  const TYPE_TO_CONTAINER_STYLE = (theme, type) => ({
6
18
  primary: css `
7
19
  background: ${theme.color.container.blueContainer};
@@ -37,17 +49,17 @@ const TYPE_TO_INDEX_STYLE = (theme, type) => ({
37
49
  `,
38
50
  })[type];
39
51
  /** 비지니스 로직과 무관한 SelectBox를 그리는 공통 컴포넌트입니다. */
40
- export function SelectBoxComponent(props) {
41
- const { className, indexClassName, contentClassName, type, index, image, text, endIcon, onClick, } = props;
52
+ export const SelectBoxComponent = forwardRef(function SelectBoxComponent(props, ref) {
53
+ const { className, indexClassName, contentClassName, type, index, image, text, endIcon, onClick } = props, restProps = __rest(props, ["className", "indexClassName", "contentClassName", "type", "index", "image", "text", "endIcon", "onClick"]);
42
54
  const theme = useTheme();
43
- return (_jsxs(Container, Object.assign({ className: className, css: type ? TYPE_TO_CONTAINER_STYLE(theme, type) : undefined, onClick: onClick }, { children: [_jsx(Index, Object.assign({ className: indexClassName, css: type ? TYPE_TO_INDEX_STYLE(theme, type) : undefined }, { children: index })), _jsxs(Content, Object.assign({ className: contentClassName }, { children: [image && (_jsx("img", { src: image.src, alt: image.altText, css: css `
55
+ return (_jsxs(Container, Object.assign({ className: className, ref: ref }, restProps, { css: type ? TYPE_TO_CONTAINER_STYLE(theme, type) : undefined, onClick: onClick }, { children: [_jsx(Index, Object.assign({ className: indexClassName, "aria-hidden": "true", css: type ? TYPE_TO_INDEX_STYLE(theme, type) : undefined }, { children: index })), _jsxs(Content, Object.assign({ className: contentClassName, "aria-hidden": "true" }, { children: [image && (_jsx("img", { "aria-hidden": "true", src: image.src, alt: image.altText, css: css `
44
56
  height: auto;
45
57
  // 이미지로 인해 좌우로 스크롤이 생기는 것을 방지
46
58
  max-width: 100%;
47
59
  width: fit-content;
48
60
  border-radius: 6px;
49
61
  ` })), text] })), endIcon] })));
50
- }
62
+ });
51
63
  const Container = styled.div(({ theme, onClick }) => css `
52
64
  ${onClick && "cursor: pointer;"}
53
65
  display: flex;
@@ -4,6 +4,8 @@ import { css } from "@emotion/react";
4
4
  import { CheckboxCircleFillIcon, CheckFillIcon, CloseCircleFillIcon, } from "../../../../../icons";
5
5
  import { SelectBoxComponent } from "./SelectBoxComponent";
6
6
  import Tag from "../../../../../components/Tag";
7
+ import { useRef } from "react";
8
+ import { useEventListener } from "usehooks-ts";
7
9
  const TYPE_TO_INDEX_ICON = (type) => ({
8
10
  primary: (_jsx(CheckFillIcon, { css: css `
9
11
  width: 12px;
@@ -27,9 +29,28 @@ export function SelectBoxView(props) {
27
29
  ? "success"
28
30
  : "danger"
29
31
  : undefined;
32
+ const boxRef = useRef(null);
33
+ const handleKeyDown = (e) => {
34
+ // onClick이 없으면 아무것도 하지 않음.
35
+ if (!onClick) {
36
+ return;
37
+ }
38
+ // tab을 통해 focus된 상태가 아니면 아무것도 하지 않음.
39
+ if (document.activeElement !== boxRef.current) {
40
+ return;
41
+ }
42
+ // spacebar 또는 엔터를 누르면 onClick 실행
43
+ if (e.key === " " || e.key === "Enter") {
44
+ e.preventDefault();
45
+ onClick();
46
+ }
47
+ };
48
+ useEventListener("keydown", handleKeyDown);
49
+ // text가 있으면 text, 없으면 image의 altText, 없으면 index를 aria-label로 사용
50
+ const ariaText = text || (image && image.altText) || index.toString() + "번 선택지";
30
51
  return (_jsx(SelectBoxComponent, { css: css `
31
52
  width: 100%;
32
- `, type: selectBoxType, index: selectBoxType ? TYPE_TO_INDEX_ICON(selectBoxType) : index, image: image, text: text, onClick: onClick,
53
+ `, ref: boxRef, role: onClick ? "checkbox" : undefined, "aria-checked": isSelected, "aria-label": onClick ? ariaText + (isSelected ? " 선택됨" : " 선택하기") : ariaText, tabIndex: onClick ? 0 : -1, type: selectBoxType, index: selectBoxType ? TYPE_TO_INDEX_ICON(selectBoxType) : index, image: image, text: text, onClick: onClick,
33
54
  // 선택되지 않았으나 정답일 때 정답 태그를 표시
34
55
  endIcon: isAnswer &&
35
56
  !isSelected && (_jsx(Tag, { label: "\uC815\uB2F5", icon: _jsx(CheckboxCircleFillIcon, {}), color: "green", css: css `
@@ -7,12 +7,12 @@ export declare const SelectBoxClasses: {
7
7
  };
8
8
  export type SelectBoxType = "primary" | "normal";
9
9
  /** 비지니스 로직과 무관한 SelectBox를 그리는 공통 컴포넌트입니다. */
10
- export declare function SelectBoxComponent(props: {
11
- className?: string;
10
+ export declare const SelectBoxComponent: import("react").ForwardRefExoticComponent<{
11
+ className?: string | undefined;
12
12
  type: SelectBoxType;
13
13
  index: React.ReactNode;
14
- image?: ImageProps | null;
14
+ image?: ImageProps | null | undefined;
15
15
  text: React.ReactNode;
16
16
  /** 박스를 클릭할 때 실행될 콜백. 존재하지 않으면 cursor: pointer가 적용되지 않습니다. */
17
- onClick?: () => void;
18
- }): import("@emotion/react/jsx-runtime").JSX.Element;
17
+ onClick?: (() => void) | undefined;
18
+ } & Partial<import("react").HTMLAttributes<HTMLDivElement>> & import("react").RefAttributes<HTMLDivElement>>;
@@ -1,7 +1,19 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
1
12
  import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
13
  /** @jsxImportSource @emotion/react */
3
14
  import { css, useTheme } from "@emotion/react";
4
15
  import styled from "@emotion/styled";
16
+ import { forwardRef } from "react";
5
17
  export const SelectBoxClasses = {
6
18
  container: "SheetSelectNode-SelectBox-container",
7
19
  index: "SheetSelectNode-SelectBox-index",
@@ -32,17 +44,17 @@ const TYPE_TO_INDEX_STYLE = (theme, type) => ({
32
44
  `,
33
45
  })[type];
34
46
  /** 비지니스 로직과 무관한 SelectBox를 그리는 공통 컴포넌트입니다. */
35
- export function SelectBoxComponent(props) {
36
- const { className, type, index, image, text, onClick } = props;
47
+ export const SelectBoxComponent = forwardRef(function SelectBoxComponent(props, ref) {
48
+ const { className, type, index, image, text, onClick } = props, restProps = __rest(props, ["className", "type", "index", "image", "text", "onClick"]);
37
49
  const theme = useTheme();
38
- return (_jsxs(Container, Object.assign({ className: `${className} ${SelectBoxClasses.container}`, css: TYPE_TO_CONTAINER_STYLE(theme, type), onClick: onClick }, { children: [_jsx(Index, Object.assign({ className: SelectBoxClasses.index, css: TYPE_TO_INDEX_STYLE(theme, type) }, { children: index })), _jsxs(Content, Object.assign({ className: SelectBoxClasses.content }, { children: [image && (_jsx("img", { src: image.src, alt: image.altText, css: css `
50
+ return (_jsxs(Container, Object.assign({ className: `${className} ${SelectBoxClasses.container}`, ref: ref }, restProps, { css: TYPE_TO_CONTAINER_STYLE(theme, type), onClick: onClick }, { children: [_jsx(Index, Object.assign({ className: SelectBoxClasses.index, "aria-hidden": "true", css: TYPE_TO_INDEX_STYLE(theme, type) }, { children: index })), _jsxs(Content, Object.assign({ className: SelectBoxClasses.content, "aria-hidden": "true" }, { children: [image && (_jsx("img", { "aria-hidden": "true", src: image.src, alt: image.altText, css: css `
39
51
  height: auto;
40
52
  // 이미지로 인해 좌우로 스크롤이 생기는 것을 방지
41
53
  max-width: 100%;
42
54
  width: fit-content;
43
55
  border-radius: 6px;
44
56
  ` })), text] }))] })));
45
- }
57
+ });
46
58
  const Container = styled.div(({ onClick }) => css `
47
59
  ${onClick && "cursor: pointer;"}
48
60
  display: flex;
@@ -3,6 +3,8 @@ import { jsx as _jsx } from "@emotion/react/jsx-runtime";
3
3
  import { css } from "@emotion/react";
4
4
  import { SelectBoxComponent } from "./SelectBoxComponent";
5
5
  import { CheckFillIcon } from "../../../../../../icons";
6
+ import { useRef } from "react";
7
+ import { useEventListener } from "usehooks-ts";
6
8
  function getIndexIcon(type, index) {
7
9
  return {
8
10
  primary: (_jsx(CheckFillIcon, { css: css `
@@ -16,7 +18,26 @@ function getIndexIcon(type, index) {
16
18
  export function SelectBoxView(props) {
17
19
  const { index, isSelected, image, text, onClick } = props;
18
20
  const selectBoxType = isSelected ? "primary" : "normal";
21
+ const boxRef = useRef(null);
22
+ const handleKeyDown = (e) => {
23
+ // onClick이 없으면 아무것도 하지 않음.
24
+ if (!onClick) {
25
+ return;
26
+ }
27
+ // tab을 통해 focus된 상태가 아니면 아무것도 하지 않음.
28
+ if (document.activeElement !== boxRef.current) {
29
+ return;
30
+ }
31
+ // spacebar 또는 엔터를 누르면 onClick 실행
32
+ if (e.key === " " || e.key === "Enter") {
33
+ e.preventDefault();
34
+ onClick();
35
+ }
36
+ };
37
+ useEventListener("keydown", handleKeyDown);
38
+ // text가 있으면 text, 없으면 image의 altText, 없으면 index를 aria-label로 사용
39
+ const ariaText = text || (image && image.altText) || index.toString() + "번 선택지";
19
40
  return (_jsx(SelectBoxComponent, { css: css `
20
41
  width: 100%;
21
- `, type: selectBoxType, index: getIndexIcon(selectBoxType, index), image: image, text: text, onClick: onClick }));
42
+ `, ref: boxRef, role: onClick ? "checkbox" : undefined, "aria-checked": isSelected, "aria-label": onClick ? ariaText + (isSelected ? " 선택됨" : " 선택하기") : ariaText, tabIndex: onClick ? 0 : -1, type: selectBoxType, index: getIndexIcon(selectBoxType, index), image: image, text: text, onClick: onClick }));
22
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-monolith/cds",
3
- "version": "1.61.0",
3
+ "version": "1.62.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "sideEffects": false,
@@ -13,7 +13,6 @@
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",
17
16
  "hex-to-css-filter": "^5.4.0",
18
17
  "lexical": "^0.12.4",
19
18
  "lodash": "^4.17.21",
@@ -22,7 +21,8 @@
22
21
  "react-hook-form": "^7.48.2",
23
22
  "remixicon": "^3.4.0",
24
23
  "typescript": "^4.9.5",
25
- "uid": "^2.0.2"
24
+ "uid": "^2.0.2",
25
+ "usehooks-ts": "^2.9.1"
26
26
  },
27
27
  "files": [
28
28
  "dist/**/*",
@@ -1,17 +0,0 @@
1
- import React from "react";
2
- export interface BookProps {
3
- className?: string;
4
- title?: string;
5
- chapter?: string;
6
- courseType?: string;
7
- isHidden?: boolean;
8
- buttons?: React.ReactNode;
9
- /** 전달하면 controlled component로 애니메이션을 제어할 수 있다 */
10
- open?: boolean;
11
- onHoverStart?: () => void;
12
- onHoverEnd?: () => void;
13
- onCoverClick?: () => void;
14
- icon?: React.ReactNode;
15
- }
16
- declare const _default: React.ForwardRefExoticComponent<BookProps & React.RefAttributes<HTMLDivElement>>;
17
- export default _default;
@@ -1,265 +0,0 @@
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
- backgrond: theme.color.background.primary,
58
- text: theme.color.foreground.neutralAlt,
59
- });
60
- /**
61
- * Framer Motion의 whileHover를 사용하지 않고 직접 애니메이션 상태를 관리합니다.
62
- * 내부적으로는 간단한 애니메이션 상태 관리가 있으며, 필요한 경우
63
- * open 상태를 전달하여 외부에서 관리할 수 있습니다.
64
- *
65
- * 사유1)
66
- * 모바일에서는 hover가 없기 때문에 whileHover를 사용할 수 없습니다.
67
- * 클릭(탭)을 통해 열고 닫을 수 있어야 합니다.
68
- *
69
- * 사유2)
70
- * 관리/배부 드랍다운 Modal이 Card 위로 그려져 hoverEnd를 유발하기 때문에
71
- * 특성 상황에서는 hoverEnd가 발생했어도 Card를 열고 있어야 합니다.
72
- * @param props
73
- * @param ref
74
- * @returns
75
- */
76
- function Book(props, ref) {
77
- const { className, title, chapter, courseType, isHidden = false, buttons, open: controlledOpen, onHoverStart, onHoverEnd, onCoverClick, icon, } = props;
78
- const theme = useTheme();
79
- const [open, setOpen] = useState(false);
80
- return (_jsxs(Container, Object.assign({ initial: "closed", animate: (controlledOpen !== undefined ? controlledOpen : open)
81
- ? "open"
82
- : "closed", ref: ref, onHoverStart: () => {
83
- setOpen(true);
84
- onHoverStart === null || onHoverStart === void 0 ? void 0 : onHoverStart();
85
- }, onHoverEnd: () => {
86
- setOpen(false);
87
- onHoverEnd === null || onHoverEnd === void 0 ? void 0 : onHoverEnd();
88
- } }, { children: [_jsxs(Cover, Object.assign({ className: className, 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
-
99
- width: 24px;
100
- height: 24px;
101
- ` })] })), _jsxs(CoverTexts, Object.assign({ variants: buttons ? textMotion : undefined }, { children: [_jsx(Chapter, { children: chapter }), _jsx(CourseType, { children: courseType }), _jsx(CoverTitle, { children: title })] })), _jsx(BookGradient, {}), _jsx(Buttons, Object.assign({ variants: buttonsMotion, onClick: (e) => {
102
- // Button 클릭 시에도 CoverClick 이벤트가 발생하지 않도록 막습니다.
103
- e.stopPropagation();
104
- } }, { children: buttons }))] })), _jsx(Shelf, {})] })));
105
- }
106
- export default forwardRef(Book);
107
- const Container = styled(motion.div) `
108
- position: relative;
109
-
110
- width: 200px;
111
- height: ${BOOK_HEIGHT + HOVER_Y_OFFSET + SHELF_HEIGHT + BOOK_SHELF_GAP}px;
112
-
113
- padding-top: ${HOVER_Y_OFFSET}px;
114
- `;
115
- const Cover = styled(motion.div)(({ theme }) => css `
116
- height: ${BOOK_HEIGHT}px;
117
-
118
- border-radius: 8px;
119
- background: ${CARD_COLOR_PALETTE(theme).backgrond};
120
- box-shadow: ${shadows.shadow04};
121
-
122
- display: flex;
123
- flex-direction: row;
124
- gap: 12px;
125
- overflow: hidden;
126
-
127
- position: relative;
128
- bottom: 0px;
129
- z-index: ${ZINDEX_COVER};
130
- `);
131
- const CoverTexts = styled(motion.div) `
132
- width: 100%;
133
- height: 100%;
134
- padding: 12px 12px 12px 24px;
135
-
136
- position: absolute;
137
- top: 0;
138
- left: 0;
139
- z-index: ${ZINDEX_TEXT};
140
-
141
- display: flex;
142
- flex-direction: column;
143
- gap: 4px;
144
- `;
145
- const CoverTitle = styled.h1(({ theme }) => css `
146
- word-wrap: break-word;
147
- word-break: keep-all;
148
-
149
- margin: auto 0 8px 0;
150
- color: ${CARD_COLOR_PALETTE(theme).text};
151
-
152
- /* Alt/Paragraph/16px-Eb */
153
- font-family: ${theme.fontFamily.title};
154
- font-size: 16px;
155
- font-style: normal;
156
- font-weight: 800;
157
- line-height: 24px;
158
-
159
- /* multiline 말줄임말 적용 css */
160
- overflow: hidden;
161
- text-overflow: ellipsis;
162
- display: -webkit-box;
163
- -webkit-line-clamp: 7;
164
- -webkit-box-orient: vertical;
165
- `);
166
- const BookGradient = styled.div `
167
- width: 200px;
168
- height: ${BOOK_HEIGHT}px;
169
- background: linear-gradient(
170
- 180deg,
171
- rgba(255, 255, 255, 0) 0%,
172
- rgba(0, 0, 0, 0.75) 100%
173
- ),
174
- linear-gradient(
175
- 90deg,
176
- #999 0%,
177
- #fbfbfb 2%,
178
- #fbfbfb 4%,
179
- #cdcdcd 6%,
180
- #fbfbfb 10%,
181
- #fbfbfb 100%
182
- );
183
- mix-blend-mode: multiply;
184
-
185
- position: absolute;
186
- top: 0;
187
- left: 0;
188
- border-radius: 8px;
189
- z-index: ${ZINDEX_COVER};
190
- `;
191
- const Buttons = styled(motion.div)(({ theme }) => css `
192
- display: flex;
193
- gap: 8px;
194
- width: 100%;
195
- padding: 0 12px 0 24px;
196
-
197
- position: absolute;
198
- bottom: 12px;
199
- z-index: ${ZINDEX_BUTTONS};
200
-
201
- svg {
202
- color: ${CARD_COLOR_PALETTE(theme).text};
203
- }
204
- `);
205
- const Shelf = styled.div(({ theme }) => css `
206
- height: ${SHELF_HEIGHT}px;
207
- width: calc(100% + 32px);
208
- border-radius: 4px;
209
- background: ${theme.color.background.neutralAltActive};
210
-
211
- position: absolute;
212
- bottom: 0px;
213
- left: -16px;
214
- `);
215
- const Hide = styled.div(({ theme }) => css `
216
- position: absolute;
217
- left: 0;
218
- top: 0;
219
-
220
- width: 100%;
221
- height: 100%;
222
-
223
- background: ${theme.color.blanket.neutral};
224
- border-radius: 8px;
225
-
226
- z-index: ${ZINDEX_HIDE};
227
- `);
228
- const Chapter = styled.div(({ theme }) => css `
229
- color: ${CARD_COLOR_PALETTE(theme).text};
230
-
231
- /* Default/Label/12px-Md */
232
- font-family: ${theme.fontFamily.ui};
233
- font-size: 12px;
234
- font-style: normal;
235
- font-weight: 500;
236
- line-height: 16px; /* 133.333% */
237
-
238
- opacity: 0.8;
239
- `);
240
- const CourseType = styled.div(({ theme }) => css `
241
- color: ${CARD_COLOR_PALETTE(theme).text};
242
-
243
- /* Default/Label/12px-Eb */
244
- font-family: ${theme.fontFamily.ui};
245
- font-size: 12px;
246
- font-style: normal;
247
- font-weight: 800;
248
- line-height: 16px; /* 133.333% */
249
-
250
- opacity: 0.8;
251
- `);
252
- const CoverIcon = styled.div(({ theme }) => css `
253
- position: absolute;
254
- top: 50%;
255
- left: 100px;
256
- transform: translateY(-50%);
257
-
258
- svg {
259
- width: 120px;
260
- height: 120px;
261
- color: ${theme.color.blanket.neutral};
262
- }
263
-
264
- z-index: ${ZINDEX_ICON};
265
- `);
@@ -1 +0,0 @@
1
- export { default } from "./Book";
@@ -1 +0,0 @@
1
- export { default } from "./Book";