catchup-library-web 1.12.2 → 1.12.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catchup-library-web",
3
- "version": "1.12.2",
3
+ "version": "1.12.5",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -11,7 +11,9 @@
11
11
  "author": "",
12
12
  "license": "ISC",
13
13
  "dependencies": {
14
+ "@types/lodash": "^4.17.20",
14
15
  "i18next": "^24.2.2",
16
+ "lodash": "^4.17.21",
15
17
  "mathlive": "^0.105.3",
16
18
  "react": "^18.3.0",
17
19
  "react-dnd": "^16.0.1",
@@ -0,0 +1,34 @@
1
+ import { IFullCardProps } from "../../properties/CardProperties";
2
+
3
+ const FullCard = ({
4
+ bgColor,
5
+ opacity,
6
+ isShadowed,
7
+ usePadding,
8
+ children,
9
+ }: IFullCardProps) => {
10
+ return (
11
+ <div className="flex flex-col justify-center items-center h-full">
12
+ <div
13
+ className={`flex-1 ${bgColor ? bgColor : ""} ${
14
+ opacity ? opacity : "opacity-100"
15
+ } rounded-catchup-xlarge w-full border border-catchup-gray-50 bg-catchup-white`}
16
+ // style={{
17
+ // boxShadow: isShadowed
18
+ // ? "rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px, rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px"
19
+ // : "none",
20
+ // }}
21
+ >
22
+ <div
23
+ className={`flex flex-col mx-auto ${
24
+ usePadding === false ? "p-0" : "p-6"
25
+ } h-full`}
26
+ >
27
+ {children}
28
+ </div>
29
+ </div>
30
+ </div>
31
+ );
32
+ };
33
+
34
+ export default FullCard;
@@ -1,10 +1,14 @@
1
1
  import Select from "react-select";
2
2
  import i18n from "../../language/i18n";
3
- import { useEffect, useRef, useState } from "react";
3
+ import { useEffect, useRef, useState, useCallback } from "react";
4
4
  import { IInputGroupProps } from "../../properties/GroupProperties";
5
5
  import BaseImage from "../images/BaseImage";
6
6
  import { IOptionProps } from "../../properties/CommonProperties";
7
7
  import { MathfieldElement } from "mathlive";
8
+ import BaseModal from "../modals/BaseModal";
9
+ import FullCard from "../cards/FullCard";
10
+ import SecondaryButton from "../buttons/SecondaryButton";
11
+ import PrimaryButton from "../buttons/PrimaryButton";
8
12
 
9
13
  declare global {
10
14
  namespace JSX {
@@ -63,9 +67,10 @@ const InputGroup = ({
63
67
  useMath,
64
68
  }: IInputGroupProps) => {
65
69
  const textAreaRef = useRef<HTMLTextAreaElement>(null);
70
+ const [showMathConstructor, setShowMathConstructor] =
71
+ useState<boolean>(false);
72
+ const [mathValue, setMathValue] = useState<string>("");
66
73
  const mathFieldRef = useRef<MathfieldElement>(null);
67
- const [isMathMode, setIsMathMode] = useState<boolean>(false);
68
- const [showMathOverlay, setShowMathOverlay] = useState<boolean>(false);
69
74
 
70
75
  useEffect(() => {
71
76
  if (!textAreaRef) return;
@@ -84,25 +89,7 @@ const InputGroup = ({
84
89
  customElements.define("math-field", MathfieldElement);
85
90
  }
86
91
  });
87
- }, [useMath, type, placeholder, title]);
88
-
89
- // Close overlay when clicking outside
90
- useEffect(() => {
91
- const handleClickOutside = (event: MouseEvent) => {
92
- if (
93
- showMathOverlay &&
94
- event.target instanceof Element &&
95
- !event.target.closest(".math-overlay")
96
- ) {
97
- setShowMathOverlay(false);
98
- }
99
- };
100
-
101
- document.addEventListener("mousedown", handleClickOutside);
102
- return () => {
103
- document.removeEventListener("mousedown", handleClickOutside);
104
- };
105
- }, [showMathOverlay]);
92
+ }, [useMath]);
106
93
 
107
94
  const retrieveNullableOptionList = () => {
108
95
  if (!optionList) return [];
@@ -134,110 +121,119 @@ const InputGroup = ({
134
121
  }
135
122
  };
136
123
 
137
- const handleMathFieldChange = (event: React.FormEvent<MathfieldElement>) => {
138
- const mathField = event.currentTarget;
139
- const syntheticEvent = {
140
- target: {
141
- value: mathField.value,
142
- type: "text",
143
- },
144
- };
145
- onChange && onChange(syntheticEvent);
146
- };
124
+ const handleMathFieldChange = useCallback(() => {
125
+ if (!mathFieldRef.current) return;
126
+ const latexValue = mathFieldRef.current.value;
127
+ const wasFocused = mathFieldRef.current === document.activeElement;
128
+ const cursorPosition = mathFieldRef.current.position;
147
129
 
148
- const handleMathFieldFocus = (event: React.FocusEvent<MathfieldElement>) => {
149
- const syntheticEvent = {
150
- target: event.currentTarget,
151
- };
152
- onFocus && onFocus(syntheticEvent);
153
- };
130
+ setMathValue(latexValue);
154
131
 
155
- const handleMathModeToggle = (mode: boolean) => {
156
- setIsMathMode(mode);
157
- setShowMathOverlay(false);
132
+ // Restore focus and cursor position after state update
133
+ if (wasFocused) {
134
+ setTimeout(() => {
135
+ if (mathFieldRef.current) {
136
+ mathFieldRef.current.focus();
137
+ mathFieldRef.current.position = cursorPosition;
138
+ }
139
+ }, 0);
140
+ }
141
+ }, []);
142
+
143
+ const handleCopyLatex = () => {
144
+ navigator.clipboard.writeText(`$$${mathValue}$$`);
158
145
  };
159
146
 
160
- const MathModeOverlay = () => {
161
- if (!showMathOverlay) return null;
147
+ const handleOpenMathConstructor = () => {
148
+ setMathValue("");
149
+ setShowMathConstructor(true);
150
+ };
162
151
 
152
+ const MathConstructorModal = () => {
163
153
  return (
164
- <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
165
- <div className="math-overlay bg-white rounded-lg p-6 shadow-xl max-w-sm w-full mx-4">
166
- <div className="flex justify-between items-center mb-4">
167
- <h3 className="text-lg font-semibold text-catchup-gray-400">
168
- {i18n.t("select_input_mode")}
169
- </h3>
170
- <button
171
- className="text-catchup-gray-300 hover:text-catchup-gray-400 transition-colors"
172
- onClick={() => setShowMathOverlay(false)}
173
- >
174
- <svg
175
- className="w-6 h-6"
176
- fill="none"
177
- stroke="currentColor"
178
- viewBox="0 0 24 24"
179
- >
180
- <path
181
- strokeLinecap="round"
182
- strokeLinejoin="round"
183
- strokeWidth={2}
184
- d="M6 18L18 6M6 6l12 12"
185
- />
186
- </svg>
187
- </button>
188
- </div>
189
- <div className="space-y-3">
190
- <button
191
- className={`w-full p-3 rounded-lg border-2 transition-all duration-200 ${
192
- !isMathMode
193
- ? "border-catchup-blue-400 bg-catchup-blue-50"
194
- : "border-catchup-gray-100 hover:border-catchup-gray-200"
195
- }`}
196
- onClick={() => handleMathModeToggle(false)}
197
- >
198
- <div className="flex items-center space-x-3">
199
- <div className="w-4 h-4 rounded-full border-2 border-catchup-blue-400 flex items-center justify-center">
200
- {!isMathMode && (
201
- <div className="w-2 h-2 rounded-full bg-catchup-blue-400"></div>
202
- )}
203
- </div>
204
- <div className="text-left">
205
- <p className="font-medium text-catchup-gray-400">
206
- {i18n.t("text_mode")}
207
- </p>
208
- <p className="text-sm text-catchup-gray-300">
209
- Regular text input
210
- </p>
154
+ <BaseModal
155
+ isOpen={showMathConstructor}
156
+ onAfterOpen={() => {}}
157
+ onRequestClose={() => {
158
+ setShowMathConstructor(false);
159
+ }}
160
+ size="large"
161
+ >
162
+ <FullCard>
163
+ <div className="bg-white rounded-lg overflow-hidden">
164
+ <div className="p-6 space-y-6">
165
+ <div>
166
+ <p className="text-md font-semibold pl-2 py-1 text-catchup-gray-400">
167
+ {i18n.t("math_editor")}
168
+ </p>
169
+ <div className="border border-catchup-gray-100 rounded-catchup-large focus-within:border-catchup-blue-400 focus-within:shadow-input">
170
+ <math-field
171
+ ref={mathFieldRef}
172
+ value={mathValue}
173
+ onInput={handleMathFieldChange}
174
+ placeholder={i18n.t("expression")}
175
+ virtual-keyboard-mode="off"
176
+ smart-fence={true}
177
+ smart-mode={true}
178
+ smart-superscript={true}
179
+ style={{
180
+ fontSize: "18px",
181
+ paddingRight: "16px",
182
+ paddingLeft: "16px",
183
+ paddingTop: "8px",
184
+ paddingBottom: "8px",
185
+ border: "none",
186
+ outline: "none",
187
+ width: "100%",
188
+ backgroundColor: "transparent",
189
+ borderRadius: "16px",
190
+ fontFamily: "inherit",
191
+ }}
192
+ />
211
193
  </div>
212
194
  </div>
213
- </button>
214
- <button
215
- className={`w-full p-3 rounded-lg border-2 transition-all duration-200 ${
216
- isMathMode
217
- ? "border-catchup-blue-400 bg-catchup-blue-50"
218
- : "border-catchup-gray-100 hover:border-catchup-gray-200"
219
- }`}
220
- onClick={() => handleMathModeToggle(true)}
221
- >
222
- <div className="flex items-center space-x-3">
223
- <div className="w-4 h-4 rounded-full border-2 border-catchup-blue-400 flex items-center justify-center">
224
- {isMathMode && (
225
- <div className="w-2 h-2 rounded-full bg-catchup-blue-400"></div>
226
- )}
227
- </div>
228
- <div className="text-left">
229
- <p className="font-medium text-catchup-gray-400">
230
- {i18n.t("math_mode")}
231
- </p>
232
- <p className="text-sm text-catchup-gray-300">
233
- Mathematical expressions
234
- </p>
195
+
196
+ {/* LaTeX Output */}
197
+ <div>
198
+ <p className="text-md font-semibold pl-2 py-1 text-catchup-gray-400">
199
+ {i18n.t("latex_output")}
200
+ </p>
201
+ <div className="relative">
202
+ <textarea
203
+ readOnly
204
+ value={mathValue}
205
+ className="w-full py-2 px-4 bg-catchup-gray-50 border border-catchup-gray-100 rounded-catchup-large font-mono text-sm resize-none focus:outline-none"
206
+ placeholder={i18n.t("latex_will_appear_here")}
207
+ />
208
+ <button
209
+ onClick={handleCopyLatex}
210
+ className="absolute top-2 right-2 bg-catchup-blue-400 text-white px-3 py-1 rounded-md text-sm hover:bg-catchup-blue-500 transition-colors"
211
+ disabled={!mathValue}
212
+ >
213
+ {i18n.t("copy")}
214
+ </button>
235
215
  </div>
236
216
  </div>
237
- </button>
217
+ </div>
218
+
219
+ {/* Footer */}
220
+ {/* <div className="flex justify-end gap-3 p-6 border-t border-catchup-gray-100">
221
+ <SecondaryButton
222
+ title={i18n.t("cancel")}
223
+ size="large"
224
+ onClick={() => {
225
+ setShowMathConstructor(false);
226
+ }}
227
+ />
228
+ <PrimaryButton
229
+ title={i18n.t("insert_latex")}
230
+ size="large"
231
+ onClick={handleInsertLatex}
232
+ />
233
+ </div> */}
238
234
  </div>
239
- </div>
240
- </div>
235
+ </FullCard>
236
+ </BaseModal>
241
237
  );
242
238
  };
243
239
 
@@ -413,7 +409,6 @@ const InputGroup = ({
413
409
  </div>
414
410
  </div>
415
411
 
416
- {/* Always show textarea - math mode overlay appears on top */}
417
412
  <textarea
418
413
  ref={textAreaRef}
419
414
  disabled={disabled}
@@ -425,60 +420,25 @@ const InputGroup = ({
425
420
  : "border-catchup-gray-100"
426
421
  } placeholder-catchup-gray-200 rounded-catchup-large focus:outline-none focus:border-catchup-blue-400 ${
427
422
  disabled ? "bg-catchup-lighter-gray" : ""
428
- } focus:shadow-input`}
423
+ } focus:shadow-input ${useMath ? "pr-20" : ""}`}
429
424
  placeholder={errorText ? errorText : placeholder}
430
425
  value={value}
431
- onLoad={(e) => {
432
- console.log(e);
433
- }}
434
426
  onChange={handleTextAreaOnChange}
435
427
  onFocus={onFocus}
436
428
  onKeyDown={onKeyDown}
437
429
  />
438
430
 
439
- {/* Math mode overlay appears on top of textarea when enabled */}
440
- {useMath && isMathMode && (
441
- <div className="absolute inset-0 bg-white border border-catchup-gray-100 rounded-catchup-large focus-within:border-catchup-blue-400 focus-within:shadow-input">
442
- <math-field
443
- ref={mathFieldRef}
444
- value={value || ""}
445
- onInput={handleMathFieldChange}
446
- onFocus={handleMathFieldFocus}
447
- placeholder={errorText ? errorText : placeholder}
448
- disabled={disabled}
449
- virtual-keyboard-mode="off"
450
- smart-fence={true}
451
- smart-mode={true}
452
- smart-superscript={true}
453
- style={{
454
- fontSize: "16px",
455
- padding: "8px 16px",
456
- border: "none",
457
- outline: "none",
458
- width: "100%",
459
- height: "100%",
460
- backgroundColor: "transparent",
461
- borderRadius: "16px",
462
- fontFamily: "inherit",
463
- color: disabled ? "#9ca3af" : "#000000",
464
- }}
465
- />
466
- </div>
467
- )}
468
-
469
- {/* Math mode toggle button */}
431
+ {/* Math constructor button */}
470
432
  {useMath && (
471
433
  <button
472
- className="absolute right-2 top-1/2 transform -translate-y-1/2 bg-catchup-white border border-catchup-gray-100 rounded-md px-3 py-1 shadow-sm hover:shadow-md transition-shadow duration-200 z-10"
473
- onClick={() => setShowMathOverlay(true)}
434
+ className="absolute right-2 top-1/2 transform -translate-y-1/2 bg-catchup-blue-400 text-white rounded-md px-3 py-1 shadow-sm hover:bg-catchup-blue-500 transition-colors duration-200 z-10"
435
+ onClick={handleOpenMathConstructor}
474
436
  type="button"
475
437
  >
476
438
  <div className="flex items-center gap-x-1">
477
- <span className="text-sm font-medium text-catchup-gray-400">
478
- {isMathMode ? "𝑓(x)" : "Aa"}
479
- </span>
439
+ <span className="text-sm font-medium">𝑓(x)</span>
480
440
  <svg
481
- className="w-3 h-3 text-catchup-gray-300"
441
+ className="w-3 h-3"
482
442
  fill="none"
483
443
  stroke="currentColor"
484
444
  viewBox="0 0 24 24"
@@ -487,7 +447,7 @@ const InputGroup = ({
487
447
  strokeLinecap="round"
488
448
  strokeLinejoin="round"
489
449
  strokeWidth={2}
490
- d="M19 9l-7 7-7-7"
450
+ d="M12 6v6m0 0v6m0-6h6m-6 0H6"
491
451
  />
492
452
  </svg>
493
453
  </div>
@@ -515,64 +475,36 @@ const InputGroup = ({
515
475
  disabled ? "bg-catchup-lighter-gray" : "bg-white"
516
476
  }`}
517
477
  >
518
- {useMath && isMathMode ? (
519
- <math-field
520
- ref={mathFieldRef}
521
- value={value || ""}
522
- onInput={handleMathFieldChange}
523
- onFocus={handleMathFieldFocus}
524
- placeholder={errorText ? errorText : placeholder}
525
- disabled={disabled}
526
- virtual-keyboard-mode="off"
527
- smart-fence={true}
528
- smart-mode={true}
529
- smart-superscript={true}
530
- style={{
531
- fontSize: "16px",
532
- padding: "8px 16px",
533
- border: "none",
534
- outline: "none",
535
- width: "100%",
536
- minHeight: "44px",
537
- backgroundColor: "transparent",
538
- borderRadius: "16px",
539
- fontFamily: "inherit",
540
- color: disabled ? "#9ca3af" : "#000000",
541
- }}
542
- />
543
- ) : (
544
- <input
545
- disabled={disabled}
546
- className={`w-full py-2 px-4 border-none rounded-catchup-large focus:outline-none placeholder-catchup-gray-200 ${
547
- disabled ? "bg-catchup-lighter-gray" : ""
548
- } ${
549
- errorText
550
- ? "placeholder:text-catchup-red placeholder:opacity-80"
551
- : ""
552
- }`}
553
- type={type}
554
- defaultValue={defaultValue}
555
- placeholder={errorText ? errorText : placeholder}
556
- value={value}
557
- onChange={onChange}
558
- onFocus={onFocus}
559
- onKeyDown={onKeyDown}
560
- />
561
- )}
478
+ <input
479
+ disabled={disabled}
480
+ className={`w-full py-2 px-4 border-none rounded-catchup-large focus:outline-none placeholder-catchup-gray-200 ${
481
+ disabled ? "bg-catchup-lighter-gray" : ""
482
+ } ${
483
+ errorText
484
+ ? "placeholder:text-catchup-red placeholder:opacity-80"
485
+ : ""
486
+ } ${useMath ? "pr-20" : ""}`}
487
+ type={type}
488
+ defaultValue={defaultValue}
489
+ placeholder={errorText ? errorText : placeholder}
490
+ value={value}
491
+ onChange={onChange}
492
+ onFocus={onFocus}
493
+ onKeyDown={onKeyDown}
494
+ />
562
495
  </div>
563
496
 
497
+ {/* Math constructor button */}
564
498
  {useMath && (
565
499
  <button
566
- className="absolute right-2 top-1/2 transform -translate-y-1/2 bg-catchup-white border border-catchup-gray-100 rounded-md px-3 py-1 shadow-sm hover:shadow-md transition-shadow duration-200"
567
- onClick={() => setShowMathOverlay(true)}
500
+ className="absolute right-2 top-1/2 transform -translate-y-1/2 bg-catchup-blue-400 text-white rounded-md px-3 py-1 shadow-sm hover:bg-catchup-blue-500 transition-colors duration-200"
501
+ onClick={handleOpenMathConstructor}
568
502
  type="button"
569
503
  >
570
504
  <div className="flex items-center gap-x-1">
571
- <span className="text-sm font-medium text-catchup-gray-400">
572
- {isMathMode ? "𝑓(x)" : "Aa"}
573
- </span>
505
+ <span className="text-sm font-medium">𝑓(x)</span>
574
506
  <svg
575
- className="w-3 h-3 text-catchup-gray-300"
507
+ className="w-3 h-3"
576
508
  fill="none"
577
509
  stroke="currentColor"
578
510
  viewBox="0 0 24 24"
@@ -581,7 +513,7 @@ const InputGroup = ({
581
513
  strokeLinecap="round"
582
514
  strokeLinejoin="round"
583
515
  strokeWidth={2}
584
- d="M19 9l-7 7-7-7"
516
+ d="M12 6v6m0 0v6m0-6h6m-6 0H6"
585
517
  />
586
518
  </svg>
587
519
  </div>
@@ -612,7 +544,7 @@ const InputGroup = ({
612
544
  return (
613
545
  <>
614
546
  {RenderMainContent()}
615
- <MathModeOverlay />
547
+ <MathConstructorModal />
616
548
  </>
617
549
  );
618
550
  };
package/src/index.ts CHANGED
@@ -5,6 +5,8 @@ export { default as DeleteButton } from "./components/buttons/DeleteButton";
5
5
  export { default as CancelButton } from "./components/buttons/CancelButton";
6
6
  export { default as ApproveButton } from "./components/buttons/ApproveButton";
7
7
 
8
+ export { default as FullCard } from "./components/cards/FullCard";
9
+
8
10
  export { default as BaseImage } from "./components/images/BaseImage";
9
11
 
10
12
  export { default as BaseLoading } from "./components/loading/BaseLoading";
@@ -2,8 +2,8 @@ export interface IButtonProps {
2
2
  title: string;
3
3
  size: string;
4
4
  onClick: () => void;
5
- disabled: boolean;
6
- iconPosition: string;
7
- textOnly: boolean;
8
- iconOnly: boolean;
5
+ disabled?: boolean;
6
+ iconPosition?: string;
7
+ textOnly?: boolean;
8
+ iconOnly?: boolean;
9
9
  }
@@ -0,0 +1,7 @@
1
+ export interface IFullCardProps {
2
+ bgColor?: string;
3
+ opacity?: string;
4
+ isShadowed?: boolean;
5
+ usePadding?: boolean;
6
+ children?: React.ReactNode;
7
+ }
@@ -1,8 +1,8 @@
1
1
  export interface IModalProps {
2
2
  isOpen: boolean;
3
3
  size: string;
4
- onAfterOpen: () => {};
5
- onRequestClose: () => {};
6
- customSize: string;
4
+ onAfterOpen: () => void;
5
+ onRequestClose: () => void;
6
+ customSize?: string;
7
7
  children: any;
8
8
  }