@elice/material-quiz 1.240709.0 → 1.240710.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.
Files changed (35) hide show
  1. package/cjs/components/material-quiz/MaterialQuiz.js +32 -12
  2. package/cjs/components/material-quiz/MaterialQuizInfo.js +57 -12
  3. package/cjs/components/material-quiz/MaterialQuizSelectMultiple.js +12 -5
  4. package/cjs/components/material-quiz/MaterialQuizSelectMultipleOrder.js +11 -5
  5. package/cjs/components/material-quiz/MaterialQuizSelectOne.js +12 -5
  6. package/cjs/components/material-quiz/MaterialQuizText.js +9 -4
  7. package/cjs/components/material-quiz/context/MaterialQuizContext.d.ts +2 -0
  8. package/cjs/components/material-quiz/context/MaterialQuizContext.js +14 -2
  9. package/cjs/components/material-quiz/locales/en.json.js +1 -1
  10. package/cjs/components/material-quiz/locales/ja.json.js +1 -1
  11. package/cjs/components/material-quiz/locales/ko.json.js +1 -1
  12. package/cjs/components/material-quiz/locales/th.json.js +1 -1
  13. package/cjs/components/material-quiz/material-quiz-group/MaterialQuizGroup.js +23 -30
  14. package/cjs/components/shared/QuestionBox.js +57 -37
  15. package/cjs/components/shared/utils/mergeRefs.d.ts +2 -0
  16. package/cjs/components/shared/utils/mergeRefs.js +11 -0
  17. package/cjs/hooks/useCaculatePassage.js +11 -3
  18. package/es/components/material-quiz/MaterialQuiz.js +32 -12
  19. package/es/components/material-quiz/MaterialQuizInfo.js +58 -13
  20. package/es/components/material-quiz/MaterialQuizSelectMultiple.js +14 -7
  21. package/es/components/material-quiz/MaterialQuizSelectMultipleOrder.js +12 -6
  22. package/es/components/material-quiz/MaterialQuizSelectOne.js +13 -6
  23. package/es/components/material-quiz/MaterialQuizText.js +10 -5
  24. package/es/components/material-quiz/context/MaterialQuizContext.d.ts +2 -0
  25. package/es/components/material-quiz/context/MaterialQuizContext.js +15 -3
  26. package/es/components/material-quiz/locales/en.json.js +1 -1
  27. package/es/components/material-quiz/locales/ja.json.js +1 -1
  28. package/es/components/material-quiz/locales/ko.json.js +1 -1
  29. package/es/components/material-quiz/locales/th.json.js +1 -1
  30. package/es/components/material-quiz/material-quiz-group/MaterialQuizGroup.js +24 -31
  31. package/es/components/shared/QuestionBox.js +57 -37
  32. package/es/components/shared/utils/mergeRefs.d.ts +2 -0
  33. package/es/components/shared/utils/mergeRefs.js +9 -0
  34. package/es/hooks/useCaculatePassage.js +12 -4
  35. package/package.json +5 -4
@@ -4,10 +4,16 @@ import { useIntersection } from 'react-use';
4
4
  import { Flex, Text, Box, Button } from '@elice/blocks';
5
5
  import { base } from '@elice/design-tokens';
6
6
  import { useRawEliceIntl } from '@elice/intl';
7
+ import { useTheme } from '@mui/material';
8
+ import animateScrollTo from 'animated-scroll-to';
7
9
  import styled from 'styled-components';
8
10
  import { MATERIAL_QUIZ_ANSWER_ID, MATERIAL_QUIZ_PASSIVE_ID } from '../../constant/element.js';
9
11
  import { useMaterialQuizState } from '../material-quiz/context/MaterialQuizContext.js';
12
+ import { mergeRefs } from './utils/mergeRefs.js';
10
13
 
14
+ const easeInOutCubic = t => {
15
+ return t < 0.5 ? 4 * Math.pow(t, 3) : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
16
+ };
11
17
  const StyledQuestionBox = styled.div.withConfig({
12
18
  componentId: "sc-6qfyxj-0"
13
19
  })(["position:relative;display:flex;flex-direction:column;overflow:", ";border-radius:", ";width:100%;background-color:", ";", ";"], ({
@@ -22,7 +28,9 @@ const StyledQuestionBox = styled.div.withConfig({
22
28
  `);
23
29
  const StyledQuestionBoxHeader = styled.div.withConfig({
24
30
  componentId: "sc-6qfyxj-1"
25
- })(["display:flex;justify-content:space-between;background-color:", ";padding:1.5rem;flex:none;", ""], base.color.navy8, ({
31
+ })(["display:flex;justify-content:space-between;background-color:", ";padding:", ";flex:none;", ""], base.color.navy8, ({
32
+ vertical
33
+ }) => !vertical ? '1.5rem' : '1.5rem 1rem 1rem', ({
26
34
  vertical
27
35
  }) => vertical ? `
28
36
  position: sticky;
@@ -32,18 +40,16 @@ const StyledQuestionBoxHeader = styled.div.withConfig({
32
40
  ` : '');
33
41
  const StyledQuestionBoxBody = styled.div.withConfig({
34
42
  componentId: "sc-6qfyxj-2"
35
- })(["background-color:", ";flex:", ";", ";"], base.color.navy8, ({
36
- vertical
37
- }) => vertical ? 'none' : 'auto', ({
43
+ })(["background-color:", ";flex:auto;", ";"], base.color.navy8, ({
38
44
  hasFooter,
39
45
  vertical
40
- }) => hasFooter && !vertical ? 'height: 100%; padding: 0 1.5rem 1.5rem; overflow-y: auto;' : 'padding: 1.5rem');
46
+ }) => hasFooter && !vertical ? 'height: 100%; padding: 0 1.5rem 1.5rem; overflow-y: auto;' : 'padding: 0.25rem 1.5rem 1.5rem');
41
47
  const StyledAnchorBox = styled.div.withConfig({
42
48
  componentId: "sc-6qfyxj-3"
43
49
  })(["position:sticky;bottom:0;left:0;display:flex;justify-content:center;background-color:", ";border-top:1px solid ", ";"], base.color.navy8, base.color.gray7);
44
50
  const StyledQuestionBoxFooter = styled.div.withConfig({
45
51
  componentId: "sc-6qfyxj-4"
46
- })(["flex:none;display:flex;align-items:center;padding:1rem 1.5rem 1.5rem;background-color:", ";", ""], base.color.navy8, ({
52
+ })(["flex:none;display:flex;align-items:center;padding:1rem;background-color:", ";", ""], base.color.navy8, ({
47
53
  vertical
48
54
  }) => {
49
55
  return vertical ? `
@@ -62,16 +68,6 @@ const StyledQuestionBoxFooterActions = styled.div.withConfig({
62
68
  }) => {
63
69
  return vertical ? 'width: 100%' : '';
64
70
  });
65
- const StyledQuestionBoxFooterStatus = styled.div.withConfig({
66
- componentId: "sc-6qfyxj-6"
67
- })(["margin-left:0.75rem;", ""], ({
68
- vertical
69
- }) => {
70
- return vertical ? `
71
- margin-top: 0.75rem;
72
- order: 1;
73
- ` : '';
74
- });
75
71
  const QuestionBox = _a => {
76
72
  var {
77
73
  children,
@@ -85,18 +81,22 @@ const QuestionBox = _a => {
85
81
  bodyContainerRef
86
82
  } = _a,
87
83
  props = __rest(_a, ["children", "footerActions", "title", "titlePrefix", "submitResult", "submitStatus", "onNext", "isNextActive", "bodyContainerRef"]);
84
+ const theme = useTheme();
88
85
  const intl = useRawEliceIntl();
89
86
  const {
90
87
  vertical,
91
88
  isLongPassage
92
89
  } = useMaterialQuizState();
93
90
  const intersectionRef = React.useRef(null);
91
+ const headerRef = React.useRef(null);
92
+ const bodyRef = React.useRef(null);
93
+ const currentBodyRef = mergeRefs(bodyContainerRef, bodyRef);
94
94
  const hasFooter = footerActions.length > 0;
95
95
  const visibleAnchorSection = vertical && isLongPassage;
96
96
  const intersection = useIntersection(intersectionRef, {
97
97
  root: null,
98
98
  rootMargin: '0px',
99
- threshold: 1
99
+ threshold: 0.1
100
100
  });
101
101
  const isViewingAnswerContainer = Boolean(intersection === null || intersection === void 0 ? void 0 : intersection.isIntersecting);
102
102
  useEffect(() => {
@@ -104,15 +104,22 @@ const QuestionBox = _a => {
104
104
  // only enable for vertical mode
105
105
  if (answerContainer && vertical) intersectionRef.current = answerContainer;
106
106
  }, [intersectionRef, vertical]);
107
- const scrollToElement = elementId => {
107
+ const scrollToElement = async elementId => {
108
+ var _a, _b;
108
109
  const target = document.getElementById(elementId);
109
- target && target.scrollIntoView({
110
- behavior: 'smooth',
111
- block: 'start'
112
- });
110
+ if (target && bodyRef.current) {
111
+ await animateScrollTo(target.offsetTop, {
112
+ elementToScroll: bodyRef.current,
113
+ verticalOffset: -((_b = (_a = headerRef.current) === null || _a === void 0 ? void 0 : _a.offsetHeight) !== null && _b !== void 0 ? _b : 0),
114
+ easing: easeInOutCubic,
115
+ minDuration: 50,
116
+ speed: 200
117
+ });
118
+ }
113
119
  };
114
120
  const header = title ? React.createElement(StyledQuestionBoxHeader, {
115
- vertical: vertical
121
+ vertical: vertical,
122
+ ref: headerRef
116
123
  }, React.createElement(Flex, null, titlePrefix ? React.createElement(Text, {
117
124
  bold: true,
118
125
  size: "large",
@@ -127,36 +134,38 @@ const QuestionBox = _a => {
127
134
  wordBreak: "break-word"
128
135
  }, title)), submitResult ? React.createElement(Box, null, submitResult) : null) : null;
129
136
  const body = React.createElement(StyledQuestionBoxBody, {
137
+ ref: !vertical ? currentBodyRef : undefined,
130
138
  hasFooter: hasFooter,
131
- ref: bodyContainerRef,
132
139
  vertical: vertical
133
140
  }, children);
134
141
  const footer = React.createElement(StyledQuestionBoxFooter, {
135
142
  vertical: vertical
136
- }, React.createElement(StyledQuestionBoxFooterStatus, {
137
- vertical: vertical
138
- }, submitStatus), React.createElement(StyledQuestionBoxFooterActions, {
143
+ }, React.createElement(StyledQuestionBoxFooterActions, {
139
144
  vertical: vertical
140
145
  }, footerActions.map((action, index) => React.createElement(Button, Object.assign({
141
- isFluid: true,
146
+ isFluid: vertical,
142
147
  key: index,
143
148
  size: "small"
144
149
  }, action), action.children)), isNextActive ? React.createElement(Button, {
145
- isFluid: true,
150
+ isFluid: vertical,
146
151
  size: "small",
147
- border: true,
152
+ border: false,
148
153
  tabIndex: 0,
149
154
  transparent: false,
150
- role: "lightpurple",
151
- onClick: onNext
155
+ onClick: onNext,
156
+ customStyles: {
157
+ backgroundColor: theme.palette.primary.main,
158
+ color: theme.palette.primary.contrastText
159
+ }
152
160
  }, intl.formatMessage({
153
161
  id: 'materialQuiz.next'
154
162
  })) : null));
155
163
  const _renderAnchorSection = () => {
156
164
  const getCustomStyles = active => ({
157
- borderBottom: active ? '3px solid currentColor' : 'none',
165
+ borderBottom: '3px solid currentColor',
158
166
  background: 'none',
159
- paddingBlock: '1.5rem'
167
+ borderColor: active ? 'currentColor' : 'transparent',
168
+ color: active ? theme.palette.primary.main : theme.palette.secondary.light
160
169
  });
161
170
  return visibleAnchorSection && React.createElement(StyledAnchorBox, null, React.createElement(Button, {
162
171
  size: "small",
@@ -164,7 +173,6 @@ const QuestionBox = _a => {
164
173
  hasBorderRadius: false,
165
174
  tabIndex: 0,
166
175
  transparent: true,
167
- role: isViewingAnswerContainer ? 'white' : 'primary',
168
176
  onClick: scrollToElement.bind(null, MATERIAL_QUIZ_PASSIVE_ID),
169
177
  customStyles: getCustomStyles(!isViewingAnswerContainer)
170
178
  }, intl.formatMessage({
@@ -175,16 +183,28 @@ const QuestionBox = _a => {
175
183
  hasBorderRadius: false,
176
184
  tabIndex: 0,
177
185
  transparent: true,
178
- role: isViewingAnswerContainer ? 'primary' : 'white',
179
186
  onClick: scrollToElement.bind(null, MATERIAL_QUIZ_ANSWER_ID),
180
187
  customStyles: getCustomStyles(isViewingAnswerContainer)
181
188
  }, intl.formatMessage({
182
189
  id: 'materialQuiz.anchorLabel.answer'
183
190
  })));
184
191
  };
192
+ const renderContent = () => {
193
+ const content = React.createElement(React.Fragment, null, body, footerActions.length > 0 ? footer : null);
194
+ if (vertical) {
195
+ return React.createElement("div", {
196
+ ref: currentBodyRef,
197
+ style: {
198
+ height: '100%',
199
+ overflow: 'auto'
200
+ }
201
+ }, content);
202
+ }
203
+ return content;
204
+ };
185
205
  return React.createElement(StyledQuestionBox, Object.assign({}, props, {
186
206
  vertical: vertical
187
- }), header, body, footerActions.length > 0 ? footer : null, _renderAnchorSection());
207
+ }), header, renderContent(), _renderAnchorSection());
188
208
  };
189
209
 
190
210
  export { StyledQuestionBox, QuestionBox as default };
@@ -0,0 +1,2 @@
1
+ import type { Ref, RefCallback } from 'react';
2
+ export declare const mergeRefs: <T>(...refs: (Ref<T> | undefined)[]) => RefCallback<T>;
@@ -0,0 +1,9 @@
1
+ const mergeRefs = (...refs) => element => refs.forEach(ref => {
2
+ if (typeof ref === 'function') {
3
+ ref(element);
4
+ } else if (ref && typeof ref === 'object') {
5
+ ref.current = element;
6
+ }
7
+ });
8
+
9
+ export { mergeRefs };
@@ -1,4 +1,4 @@
1
- import { useLayoutEffect } from 'react';
1
+ import { useEffect } from 'react';
2
2
  import { useMeasure } from 'react-use';
3
3
  import { useMaterialQuizState, useMaterialQuizDispatch } from '../components/material-quiz/context/MaterialQuizContext.js';
4
4
  import { MATERIAL_QUIZ_CONTAINER_ID } from '../constant/element.js';
@@ -9,7 +9,8 @@ const useCaculatePassage = () => {
9
9
  vertical
10
10
  } = useMaterialQuizState();
11
11
  const {
12
- setIsLongPassage
12
+ setIsLongPassage,
13
+ setIsInitialLoading
13
14
  } = useMaterialQuizDispatch();
14
15
  const [questionRef, {
15
16
  height: questionDetailHeight
@@ -17,13 +18,20 @@ const useCaculatePassage = () => {
17
18
  const [containerRef, {
18
19
  height: containerHeight
19
20
  }] = useMeasure();
20
- useLayoutEffect(() => {
21
+ useEffect(() => {
21
22
  var _a, _b;
22
23
  const currentContainerHeight = vertical ? (_b = (_a = document.getElementById(MATERIAL_QUIZ_CONTAINER_ID)) === null || _a === void 0 ? void 0 : _a.offsetHeight) !== null && _b !== void 0 ? _b : 0 : containerHeight;
23
24
  if (currentContainerHeight && questionDetailHeight && materialQuiz) {
24
25
  setIsLongPassage(questionDetailHeight > currentContainerHeight);
26
+ // add timeout for forcing caculate layout and render in the parent
27
+ // finish before turning off loading
28
+ setTimeout(() => {
29
+ setIsInitialLoading(false);
30
+ }, 300);
31
+ } else if (materialQuiz && !materialQuiz.questionDescription) {
32
+ setIsInitialLoading(false);
25
33
  }
26
- }, [containerHeight, questionDetailHeight, materialQuiz, setIsLongPassage, vertical]);
34
+ }, [containerHeight, questionDetailHeight, materialQuiz, setIsLongPassage, vertical, setIsInitialLoading]);
27
35
  return {
28
36
  questionRef,
29
37
  containerRef
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elice/material-quiz",
3
- "version": "1.240709.0",
3
+ "version": "1.240710.0",
4
4
  "description": "User view and editing components of Elice material quiz",
5
5
  "repository": "https://git.elicer.io/elice/frontend/library/elice-material",
6
6
  "license": "UNLICENSED",
@@ -48,6 +48,7 @@
48
48
  "styled-components": "^5.2.0"
49
49
  },
50
50
  "dependencies": {
51
+ "animated-scroll-to": "^2.0.5",
51
52
  "classnames": "^2.2.0",
52
53
  "jquery": "^3.6.0",
53
54
  "jquery-ui": "^1.13.1",
@@ -64,8 +65,8 @@
64
65
  "@elice/icons-legacy": "npm:@elice/icons@0.230814.0",
65
66
  "@elice/intl": "0.240425.0-rc.2",
66
67
  "@elice/markdown": "^1.240124.0",
67
- "@elice/material-shared-types": "1.240709.0",
68
- "@elice/material-shared-utils": "1.240709.0",
68
+ "@elice/material-shared-types": "1.240710.0",
69
+ "@elice/material-shared-utils": "1.240710.0",
69
70
  "@elice/mui-system": "^5.240108.1",
70
71
  "@elice/types": "^1.240619.0",
71
72
  "@emotion/react": "^11.10.0",
@@ -87,5 +88,5 @@
87
88
  "react-use": "^17.2.4",
88
89
  "styled-components": "^5.3.0"
89
90
  },
90
- "gitHead": "1890e435180fb5e3662fdaedbba1009225bc316a"
91
+ "gitHead": "26aba6603e491668e8258b108b3b45f31ba33b24"
91
92
  }