@c2-markdown-ui/react 0.4.4 → 0.4.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.
Files changed (73) hide show
  1. package/README.md +24 -6
  2. package/dist/index.js +4365 -3733
  3. package/package.json +2 -2
  4. package/src/lib/MarkdownUI.tsx +47 -9
  5. package/src/lib/widgets.css +2188 -1372
  6. package/src/widgets/Button.tsx +39 -39
  7. package/src/widgets/ButtonGroup.tsx +47 -47
  8. package/src/widgets/Cascader.tsx +3 -2
  9. package/src/widgets/Chart.tsx +237 -237
  10. package/src/widgets/DatePicker.tsx +451 -434
  11. package/src/widgets/Form.tsx +512 -372
  12. package/src/widgets/Incomplete.tsx +3 -3
  13. package/src/widgets/MultipleChoiceQuestion.tsx +82 -82
  14. package/src/widgets/Quiz.tsx +320 -320
  15. package/src/widgets/Radio.tsx +57 -26
  16. package/src/widgets/Select.tsx +178 -142
  17. package/src/widgets/SelectMulti.tsx +105 -72
  18. package/src/widgets/ShortAnswerQuestion.tsx +101 -101
  19. package/src/widgets/Slider.tsx +70 -56
  20. package/src/widgets/Switch.tsx +14 -2
  21. package/src/widgets/TextInput.tsx +76 -38
  22. package/src/widgets/TreeSelect.tsx +649 -0
  23. package/src/widgets/Widget.tsx +130 -82
  24. package/src/widgets/choiceUtils.ts +90 -45
  25. package/src/widgets/index.ts +17 -16
  26. package/dist/index.d.ts +0 -2
  27. package/dist/index.d.ts.map +0 -1
  28. package/dist/lib/MarkdownUI.d.ts +0 -10
  29. package/dist/lib/MarkdownUI.d.ts.map +0 -1
  30. package/dist/lib/index.d.ts +0 -4
  31. package/dist/lib/index.d.ts.map +0 -1
  32. package/dist/widgets/Button.d.ts +0 -15
  33. package/dist/widgets/Button.d.ts.map +0 -1
  34. package/dist/widgets/ButtonGroup.d.ts +0 -11
  35. package/dist/widgets/ButtonGroup.d.ts.map +0 -1
  36. package/dist/widgets/Cascader.d.ts +0 -34
  37. package/dist/widgets/Cascader.d.ts.map +0 -1
  38. package/dist/widgets/Chart.d.ts +0 -20
  39. package/dist/widgets/Chart.d.ts.map +0 -1
  40. package/dist/widgets/DatePicker.d.ts +0 -28
  41. package/dist/widgets/DatePicker.d.ts.map +0 -1
  42. package/dist/widgets/Form.d.ts +0 -47
  43. package/dist/widgets/Form.d.ts.map +0 -1
  44. package/dist/widgets/Incomplete.d.ts +0 -3
  45. package/dist/widgets/Incomplete.d.ts.map +0 -1
  46. package/dist/widgets/MultipleChoiceQuestion.d.ts +0 -15
  47. package/dist/widgets/MultipleChoiceQuestion.d.ts.map +0 -1
  48. package/dist/widgets/NumberInput.d.ts +0 -26
  49. package/dist/widgets/NumberInput.d.ts.map +0 -1
  50. package/dist/widgets/Quiz.d.ts +0 -37
  51. package/dist/widgets/Quiz.d.ts.map +0 -1
  52. package/dist/widgets/Radio.d.ts +0 -24
  53. package/dist/widgets/Radio.d.ts.map +0 -1
  54. package/dist/widgets/Select.d.ts +0 -15
  55. package/dist/widgets/Select.d.ts.map +0 -1
  56. package/dist/widgets/SelectMulti.d.ts +0 -20
  57. package/dist/widgets/SelectMulti.d.ts.map +0 -1
  58. package/dist/widgets/ShortAnswerQuestion.d.ts +0 -16
  59. package/dist/widgets/ShortAnswerQuestion.d.ts.map +0 -1
  60. package/dist/widgets/Slider.d.ts +0 -13
  61. package/dist/widgets/Slider.d.ts.map +0 -1
  62. package/dist/widgets/Switch.d.ts +0 -17
  63. package/dist/widgets/Switch.d.ts.map +0 -1
  64. package/dist/widgets/TextInput.d.ts +0 -11
  65. package/dist/widgets/TextInput.d.ts.map +0 -1
  66. package/dist/widgets/Widget.d.ts +0 -7
  67. package/dist/widgets/Widget.d.ts.map +0 -1
  68. package/dist/widgets/chartRemoteData.d.ts +0 -18
  69. package/dist/widgets/chartRemoteData.d.ts.map +0 -1
  70. package/dist/widgets/choiceUtils.d.ts +0 -24
  71. package/dist/widgets/choiceUtils.d.ts.map +0 -1
  72. package/dist/widgets/index.d.ts +0 -18
  73. package/dist/widgets/index.d.ts.map +0 -1
@@ -1,101 +1,101 @@
1
- import React, { useState } from 'react';
2
-
3
- export interface ShortAnswerQuestionProps {
4
- question: string;
5
- placeholder?: string;
6
- correctAnswer?: string;
7
- correctAnswers?: string[];
8
- showFeedback?: boolean;
9
- /** 是否禁用:禁用后输入框与提交按钮不可交互 */
10
- disabled?: boolean;
11
- onchange: (value: { answer: string; isCorrect?: boolean }) => void;
12
- }
13
-
14
- export const ShortAnswerQuestion: React.FC<ShortAnswerQuestionProps> = ({
15
- question,
16
- placeholder = "Type your answer here...",
17
- correctAnswer,
18
- correctAnswers,
19
- showFeedback = false,
20
- disabled = false,
21
- onchange
22
- }) => {
23
- const [answer, setAnswer] = useState('');
24
- const [hasAnswered, setHasAnswered] = useState(false);
25
- const [isCorrect, setIsCorrect] = useState<boolean | undefined>(undefined);
26
-
27
- const handleSubmit = () => {
28
- if (disabled) return;
29
- if (!answer.trim()) return;
30
-
31
- setHasAnswered(true);
32
-
33
- const normalized = (correctAnswers && correctAnswers.length > 0)
34
- ? correctAnswers
35
- : (typeof correctAnswer === 'string' ? [correctAnswer] : undefined);
36
-
37
- const correct = normalized
38
- ? normalized.some(a => a.toLowerCase() === answer.trim().toLowerCase())
39
- : undefined;
40
-
41
- setIsCorrect(correct);
42
- onchange({ answer: answer.trim(), isCorrect: correct });
43
- };
44
-
45
- const handleKeyDown = (e: React.KeyboardEvent) => {
46
- if (e.key === 'Enter' && !hasAnswered && !disabled) {
47
- handleSubmit();
48
- }
49
- };
50
-
51
- const getFeedbackMessage = () => {
52
- if (!hasAnswered || !showFeedback) return null;
53
-
54
- const hasAnswers = (correctAnswers && correctAnswers.length > 0) || typeof correctAnswer !== 'undefined';
55
- if (!hasAnswers) {
56
- return `Your answer: ${answer.trim()}`;
57
- }
58
-
59
- if (correctAnswers && correctAnswers.length > 0) {
60
- return isCorrect
61
- ? '✓ Correct!'
62
- : `✗ Incorrect. Accepted answers: ${correctAnswers.join(', ')}`;
63
- }
64
- return isCorrect ? '✓ Correct!' : `✗ Incorrect. The correct answer is: ${correctAnswer}`;
65
- };
66
-
67
- return (
68
- <div className="widget-saq">
69
- <div className="saq-question">{question}</div>
70
- <div className="saq-input-container">
71
- <input
72
- type="text"
73
- value={answer}
74
- onChange={(e) => setAnswer(e.target.value)}
75
- onKeyDown={handleKeyDown}
76
- placeholder={placeholder}
77
- disabled={disabled || (hasAnswered && showFeedback)}
78
- className={`saq-input ${
79
- hasAnswered && showFeedback
80
- ? isCorrect ? 'saq-input-correct' : 'saq-input-incorrect'
81
- : ''
82
- }`}
83
- />
84
- {!hasAnswered && (
85
- <button
86
- onClick={handleSubmit}
87
- disabled={disabled || !answer.trim()}
88
- className="saq-submit"
89
- >
90
- Submit
91
- </button>
92
- )}
93
- </div>
94
- {hasAnswered && showFeedback && getFeedbackMessage() && (
95
- <div className={`saq-feedback ${isCorrect ? 'correct' : 'incorrect'}`}>
96
- {getFeedbackMessage()}
97
- </div>
98
- )}
99
- </div>
100
- );
101
- };
1
+ import React, { useState } from 'react';
2
+
3
+ export interface ShortAnswerQuestionProps {
4
+ question: string;
5
+ placeholder?: string;
6
+ correctAnswer?: string;
7
+ correctAnswers?: string[];
8
+ showFeedback?: boolean;
9
+ /** 是否禁用:禁用后输入框与提交按钮不可交互 */
10
+ disabled?: boolean;
11
+ onchange: (value: { answer: string; isCorrect?: boolean }) => void;
12
+ }
13
+
14
+ export const ShortAnswerQuestion: React.FC<ShortAnswerQuestionProps> = ({
15
+ question,
16
+ placeholder = "Type your answer here...",
17
+ correctAnswer,
18
+ correctAnswers,
19
+ showFeedback = false,
20
+ disabled = false,
21
+ onchange
22
+ }) => {
23
+ const [answer, setAnswer] = useState('');
24
+ const [hasAnswered, setHasAnswered] = useState(false);
25
+ const [isCorrect, setIsCorrect] = useState<boolean | undefined>(undefined);
26
+
27
+ const handleSubmit = () => {
28
+ if (disabled) return;
29
+ if (!answer.trim()) return;
30
+
31
+ setHasAnswered(true);
32
+
33
+ const normalized = (correctAnswers && correctAnswers.length > 0)
34
+ ? correctAnswers
35
+ : (typeof correctAnswer === 'string' ? [correctAnswer] : undefined);
36
+
37
+ const correct = normalized
38
+ ? normalized.some(a => a.toLowerCase() === answer.trim().toLowerCase())
39
+ : undefined;
40
+
41
+ setIsCorrect(correct);
42
+ onchange({ answer: answer.trim(), isCorrect: correct });
43
+ };
44
+
45
+ const handleKeyDown = (e: React.KeyboardEvent) => {
46
+ if (e.key === 'Enter' && !hasAnswered && !disabled) {
47
+ handleSubmit();
48
+ }
49
+ };
50
+
51
+ const getFeedbackMessage = () => {
52
+ if (!hasAnswered || !showFeedback) return null;
53
+
54
+ const hasAnswers = (correctAnswers && correctAnswers.length > 0) || typeof correctAnswer !== 'undefined';
55
+ if (!hasAnswers) {
56
+ return `Your answer: ${answer.trim()}`;
57
+ }
58
+
59
+ if (correctAnswers && correctAnswers.length > 0) {
60
+ return isCorrect
61
+ ? '✓ Correct!'
62
+ : `✗ Incorrect. Accepted answers: ${correctAnswers.join(', ')}`;
63
+ }
64
+ return isCorrect ? '✓ Correct!' : `✗ Incorrect. The correct answer is: ${correctAnswer}`;
65
+ };
66
+
67
+ return (
68
+ <div className="widget-saq">
69
+ <div className="saq-question">{question}</div>
70
+ <div className="saq-input-container">
71
+ <input
72
+ type="text"
73
+ value={answer}
74
+ onChange={(e) => setAnswer(e.target.value)}
75
+ onKeyDown={handleKeyDown}
76
+ placeholder={placeholder}
77
+ disabled={disabled || (hasAnswered && showFeedback)}
78
+ className={`saq-input ${
79
+ hasAnswered && showFeedback
80
+ ? isCorrect ? 'saq-input-correct' : 'saq-input-incorrect'
81
+ : ''
82
+ }`}
83
+ />
84
+ {!hasAnswered && (
85
+ <button
86
+ onClick={handleSubmit}
87
+ disabled={disabled || !answer.trim()}
88
+ className="saq-submit"
89
+ >
90
+ Submit
91
+ </button>
92
+ )}
93
+ </div>
94
+ {hasAnswered && showFeedback && getFeedbackMessage() && (
95
+ <div className={`saq-feedback ${isCorrect ? 'correct' : 'incorrect'}`}>
96
+ {getFeedbackMessage()}
97
+ </div>
98
+ )}
99
+ </div>
100
+ );
101
+ };
@@ -1,57 +1,71 @@
1
- import React, { useState } from 'react';
2
-
3
- export interface SliderProps {
4
- min: number;
5
- max: number;
6
- step?: number;
7
- label?: string;
8
- default?: number;
9
- /** 是否禁用 */
10
- disabled?: boolean;
11
- onchange: (value: number) => void;
12
- }
13
-
14
- export const Slider: React.FC<SliderProps> = ({
15
- min,
16
- max,
17
- step = 1,
18
- label = '',
19
- default: initial = min,
20
- disabled = false,
21
- onchange
22
- }) => {
23
- const [value, setValue] = useState(initial);
24
-
25
- const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
26
- const newValue = Number(e.target.value);
27
- setValue(newValue);
28
- };
29
-
30
- const handleComplete = () => {
31
- onchange(value);
32
- };
33
-
34
- return (
35
- <div className="widget-slider">
36
- {label && <label>{label}</label>}
37
- <div className="slider-container">
38
- <div className="slider-values">
39
- <span className="min-value">{min}</span>
40
- <span className="current-value">{value}</span>
41
- <span className="max-value">{max}</span>
42
- </div>
43
- <input
44
- type="range"
45
- value={value}
46
- min={min}
47
- max={max}
48
- step={step}
49
- onChange={handleChange}
50
- onMouseUp={handleComplete}
51
- onTouchEnd={handleComplete}
52
- disabled={disabled}
53
- />
54
- </div>
55
- </div>
56
- );
1
+ import React, { useState } from 'react';
2
+
3
+ export interface SliderProps {
4
+ min: number;
5
+ max: number;
6
+ step?: number;
7
+ label?: string;
8
+ default?: number;
9
+ /** 是否禁用 */
10
+ disabled?: boolean;
11
+ /** 表单项说明:label 后 ⓘ,悬浮显示 tooltip */
12
+ hint?: string;
13
+ /** 是否必填(slider 始终有值,仅语义标记) */
14
+ required?: boolean;
15
+ onchange: (value: number) => void;
16
+ }
17
+
18
+ export const Slider: React.FC<SliderProps> = ({
19
+ min,
20
+ max,
21
+ step = 1,
22
+ label = '',
23
+ default: initial = min,
24
+ disabled = false,
25
+ hint = '',
26
+ required = false,
27
+ onchange
28
+ }) => {
29
+ const [value, setValue] = useState(initial);
30
+
31
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
32
+ const newValue = Number(e.target.value);
33
+ setValue(newValue);
34
+ };
35
+
36
+ const handleComplete = () => {
37
+ onchange(value);
38
+ };
39
+
40
+ return (
41
+ <div className="widget-slider">
42
+ {label || hint || required ? (
43
+ <div className="widget-label-row">
44
+ {required && <span className="widget-required-mark" aria-hidden="true">*</span>}
45
+ {label && <label>{label}</label>}
46
+ {hint && <span className="widget-hint" data-hint={hint}>ⓘ</span>}
47
+ </div>
48
+ ) : null}
49
+ <div className="widget-field-content">
50
+ <div className="slider-container">
51
+ <div className="slider-values">
52
+ <span className="min-value">{min}</span>
53
+ <span className="current-value">{value}</span>
54
+ <span className="max-value">{max}</span>
55
+ </div>
56
+ <input
57
+ type="range"
58
+ value={value}
59
+ min={min}
60
+ max={max}
61
+ step={step}
62
+ onChange={handleChange}
63
+ onMouseUp={handleComplete}
64
+ onTouchEnd={handleComplete}
65
+ disabled={disabled}
66
+ />
67
+ </div>
68
+ </div>
69
+ </div>
70
+ );
57
71
  };
@@ -10,17 +10,23 @@ export interface SwitchProps {
10
10
  unCheckedText?: string;
11
11
  /** 是否禁用 */
12
12
  disabled?: boolean;
13
+ /** 表单项说明:label 后 ⓘ,悬浮显示 tooltip */
14
+ hint?: string;
15
+ /** 是否必填(switch 始终有值,仅语义标记) */
16
+ required?: boolean;
13
17
  onchange: (value: boolean) => void;
14
18
  }
15
19
 
16
20
  /** 开关组件(参考 antd Switch):布尔切换控件,可配置选中/未选中文案。
17
- label 在上(与表单其他字段同构),表单布局切换时位置自动跟随 */
21
+ 与表单其他字段同构(label-row 在前、内容在后),表单 layout:horizontal 时 label 在左、开关在右 */
18
22
  export const Switch: React.FC<SwitchProps> = ({
19
23
  label = '',
20
24
  default: initial = false,
21
25
  checkedText = '',
22
26
  unCheckedText = '',
23
27
  disabled = false,
28
+ hint = '',
29
+ required = false,
24
30
  onchange
25
31
  }) => {
26
32
  const [checked, setChecked] = useState(initial);
@@ -34,7 +40,13 @@ export const Switch: React.FC<SwitchProps> = ({
34
40
 
35
41
  return (
36
42
  <div className="widget-switch">
37
- {label && <label>{label}</label>}
43
+ {label || hint || required ? (
44
+ <div className="widget-label-row switch-label-row">
45
+ {required && <span className="widget-required-mark" aria-hidden="true">*</span>}
46
+ {label && <label>{label}</label>}
47
+ {hint && <span className="widget-hint" data-hint={hint}>ⓘ</span>}
48
+ </div>
49
+ ) : null}
38
50
  <div className="widget-field-content">
39
51
  <button
40
52
  type="button"
@@ -1,38 +1,76 @@
1
- import React, { useState } from 'react';
2
-
3
- export interface TextInputProps {
4
- placeholder?: string;
5
- label?: string;
6
- default?: string;
7
- /** 是否禁用 */
8
- disabled?: boolean;
9
- onchange: (value: string) => void;
10
- }
11
-
12
- export const TextInput: React.FC<TextInputProps> = ({
13
- placeholder = '',
14
- label = '',
15
- default: initial = '',
16
- disabled = false,
17
- onchange
18
- }) => {
19
- const [value, setValue] = useState(initial);
20
-
21
- const handleBlur = () => {
22
- onchange(value);
23
- };
24
-
25
- return (
26
- <div className="widget-text-input">
27
- {label && <label>{label}</label>}
28
- <input
29
- type="text"
30
- value={value}
31
- onChange={(e) => setValue(e.target.value)}
32
- onBlur={handleBlur}
33
- placeholder={placeholder}
34
- disabled={disabled}
35
- />
36
- </div>
37
- );
38
- };
1
+ import React, { useState } from 'react';
2
+
3
+ export interface TextInputProps {
4
+ placeholder?: string;
5
+ label?: string;
6
+ default?: string;
7
+ hint?: string;
8
+ /** 是否必填:label 前红色 * */
9
+ required?: boolean;
10
+ /** 正则校验规则(JS 正则源字符串) */
11
+ pattern?: string;
12
+ /** 校验失败提示文案 */
13
+ message?: string;
14
+ /** 外部(form 提交时)传入的错误信息,优先于组件内部即时校验 */
15
+ error?: string;
16
+ /** 是否禁用 */
17
+ disabled?: boolean;
18
+ onchange: (value: string) => void;
19
+ }
20
+
21
+ export const TextInput: React.FC<TextInputProps> = ({
22
+ placeholder = '',
23
+ label = '',
24
+ default: initial = '',
25
+ hint = '',
26
+ required = false,
27
+ pattern,
28
+ message = '',
29
+ error = '',
30
+ disabled = false,
31
+ onchange
32
+ }) => {
33
+ const [value, setValue] = useState(initial);
34
+ const [localError, setLocalError] = useState('');
35
+
36
+ /** 校验当前值:返回错误文案或空串 */
37
+ const validate = (v: string): string => {
38
+ if (required && !v.trim()) return message || (label ? `请填写${label}` : '该项为必填项');
39
+ if (pattern && v && !new RegExp(pattern).test(v)) return message || (label ? `${label}格式不正确` : '格式不正确');
40
+ return '';
41
+ };
42
+
43
+ const commit = () => {
44
+ setLocalError(validate(value));
45
+ onchange(value);
46
+ };
47
+
48
+ const shownError = error || localError;
49
+
50
+ return (
51
+ <div className={`widget-text-input${shownError ? ' widget-field-error' : ''}`}>
52
+ {label || hint || required ? (
53
+ <div className="widget-label-row">
54
+ {required && <span className="widget-required-mark" aria-hidden="true">*</span>}
55
+ {label && <label>{label}</label>}
56
+ {hint && <span className="widget-hint" data-hint={hint}>ⓘ</span>}
57
+ </div>
58
+ ) : null}
59
+ <div className="widget-field-content">
60
+ <input
61
+ type="text"
62
+ className={shownError ? 'widget-input-error' : undefined}
63
+ value={value}
64
+ onChange={(e) => {
65
+ setValue(e.target.value);
66
+ if (localError) setLocalError(validate(e.target.value));
67
+ }}
68
+ onBlur={commit}
69
+ placeholder={placeholder}
70
+ disabled={disabled}
71
+ />
72
+ {shownError ? <div className="widget-error-text" role="alert">{shownError}</div> : null}
73
+ </div>
74
+ </div>
75
+ );
76
+ };