@tinyweb_dev/oe-exam-sdk 0.2.2 → 0.2.3

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.
@@ -1,7 +1 @@
1
- import React from 'react';
2
- export interface CueCardEditorProps {
3
- value?: string;
4
- onChange: (html: string) => void;
5
- label?: React.ReactNode;
6
- }
7
- export declare function CueCardEditor({ value, onChange, label, }: CueCardEditorProps): React.JSX.Element;
1
+ export * from './shared/CueCardEditor';
@@ -1,58 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useEffect, useState } from 'react';
4
- import { useEditor, EditorContent } from '@tiptap/react';
5
- import StarterKit from '@tiptap/starter-kit';
6
- const DEFAULT_CONTENT = `
7
- <p><strong>Describe a topic you know well.</strong></p>
8
- <p>You should say:</p>
9
- <ul>
10
- <li>What it is</li>
11
- <li>When you first learned about it</li>
12
- <li>Why you find it interesting</li>
13
- </ul>
14
- <p><em>and explain how it has impacted your life.</em></p>
15
- `;
16
- export function CueCardEditor({ value, onChange, label, }) {
17
- const [mounted, setMounted] = useState(false);
18
- const editor = useEditor({
19
- immediatelyRender: false,
20
- extensions: [StarterKit],
21
- content: value || DEFAULT_CONTENT,
22
- onUpdate: ({ editor }) => {
23
- onChange(editor.getHTML());
24
- },
25
- editorProps: {
26
- attributes: {
27
- class: 'tiptap-editor focus:outline-none min-h-[250px] p-4 bg-white border border-gray-200 rounded-b-md',
28
- },
29
- },
30
- });
31
- useEffect(() => {
32
- setMounted(true);
33
- }, []);
34
- // Sync value if changed from outside (e.g., initial load)
35
- useEffect(() => {
36
- if (editor && value !== undefined && value !== editor.getHTML()) {
37
- // Small timeout to prevent update loops
38
- setTimeout(() => editor.commands.setContent(value), 0);
39
- }
40
- }, [value, editor]);
41
- if (!mounted)
42
- return null;
43
- return (_jsxs("div", { className: "w-full flex flex-col gap-4", children: [label && _jsx("div", { children: label }), _jsxs("div", { className: "flex flex-col border border-gray-300 rounded-md shadow-sm", children: [_jsxs("div", { className: "flex flex-wrap gap-2 p-2 relative bg-gray-50 border-b border-gray-300 rounded-t-md", children: [_jsx("button", { type: "button", onClick: () => editor?.chain().focus().toggleBold().run(), disabled: !editor?.can().chain().focus().toggleBold().run(), className: `px-3 py-1 rounded text-sm font-bold ${editor?.isActive('bold')
44
- ? 'bg-blue-500 text-white'
45
- : 'bg-white border text-gray-700 hover:bg-gray-100'}`, children: "B" }), _jsx("button", { type: "button", onClick: () => editor?.chain().focus().toggleItalic().run(), disabled: !editor?.can().chain().focus().toggleItalic().run(), className: `px-3 py-1 rounded text-sm italic ${editor?.isActive('italic')
46
- ? 'bg-blue-500 text-white'
47
- : 'bg-white border text-gray-700 hover:bg-gray-100'}`, children: "I" }), _jsx("div", { className: "w-px h-6 bg-gray-300 mx-1 self-center" }), _jsx("button", { type: "button", onClick: () => editor?.chain().focus().toggleHeading({ level: 1 }).run(), className: `px-3 py-1 rounded text-sm font-bold ${editor?.isActive('heading', { level: 1 })
48
- ? 'bg-blue-500 text-white'
49
- : 'bg-white border text-gray-700 hover:bg-gray-100'}`, children: "H1" }), _jsx("button", { type: "button", onClick: () => editor?.chain().focus().toggleHeading({ level: 2 }).run(), className: `px-3 py-1 rounded text-sm font-bold ${editor?.isActive('heading', { level: 2 })
50
- ? 'bg-blue-500 text-white'
51
- : 'bg-white border text-gray-700 hover:bg-gray-100'}`, children: "H2" }), _jsx("button", { type: "button", onClick: () => editor?.chain().focus().toggleHeading({ level: 3 }).run(), className: `px-3 py-1 rounded text-sm font-bold ${editor?.isActive('heading', { level: 3 })
52
- ? 'bg-blue-500 text-white'
53
- : 'bg-white border text-gray-700 hover:bg-gray-100'}`, children: "H3" }), _jsx("div", { className: "w-px h-6 bg-gray-300 mx-1 self-center" }), _jsx("button", { type: "button", onClick: () => editor?.chain().focus().toggleBulletList().run(), className: `px-3 py-1 rounded text-sm ${editor?.isActive('bulletList')
54
- ? 'bg-blue-500 text-white'
55
- : 'bg-white border text-gray-700 hover:bg-gray-100'}`, children: "\u2022 List" }), _jsx("button", { type: "button", onClick: () => editor?.chain().focus().toggleOrderedList().run(), className: `px-3 py-1 rounded text-sm ${editor?.isActive('orderedList')
56
- ? 'bg-blue-500 text-white'
57
- : 'bg-white border text-gray-700 hover:bg-gray-100'}`, children: "1. List" })] }), _jsx(EditorContent, { editor: editor })] })] }));
58
- }
1
+ export * from './shared/CueCardEditor';
@@ -4,7 +4,7 @@ import { useState, useEffect, useRef, useCallback } from 'react';
4
4
  import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
5
5
  import { Input } from '../../../../components/ui/input';
6
6
  import { Label } from '../../../../components/ui/label';
7
- import { CueCardEditor } from '../../../../components/CueCardEditor';
7
+ import { CueCardEditor } from '../../../../components/shared/CueCardEditor';
8
8
  import { CreatorGuide } from '../../components/CreatorGuide';
9
9
  import { CreditCard } from 'lucide-react';
10
10
  export function SpeakingCueCardCreator({ initialData, onChange, onUnsavedChangesChange, externalErrors, validationRef, }) {
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ export interface CueCardEditorProps {
3
+ value?: string;
4
+ onChange: (html: string) => void;
5
+ label?: React.ReactNode;
6
+ }
7
+ export declare function CueCardEditor({ value, onChange, label, }: CueCardEditorProps): React.JSX.Element;
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from 'react';
4
+ import { useEditor, EditorContent } from '@tiptap/react';
5
+ import StarterKit from '@tiptap/starter-kit';
6
+ const DEFAULT_CONTENT = `
7
+ <p><strong>Describe a topic you know well.</strong></p>
8
+ <p>You should say:</p>
9
+ <ul>
10
+ <li>What it is</li>
11
+ <li>When you first learned about it</li>
12
+ <li>Why you find it interesting</li>
13
+ </ul>
14
+ <p><em>and explain how it has impacted your life.</em></p>
15
+ `;
16
+ export function CueCardEditor({ value, onChange, label, }) {
17
+ const [mounted, setMounted] = useState(false);
18
+ const editor = useEditor({
19
+ immediatelyRender: false,
20
+ extensions: [StarterKit],
21
+ content: value || DEFAULT_CONTENT,
22
+ onUpdate: ({ editor }) => {
23
+ onChange(editor.getHTML());
24
+ },
25
+ editorProps: {
26
+ attributes: {
27
+ class: 'tiptap-editor focus:outline-none min-h-[250px] p-4 bg-white border border-gray-200 rounded-b-md',
28
+ },
29
+ },
30
+ });
31
+ useEffect(() => {
32
+ setMounted(true);
33
+ }, []);
34
+ // Sync value if changed from outside (e.g., initial load)
35
+ useEffect(() => {
36
+ if (editor && value !== undefined && value !== editor.getHTML()) {
37
+ // Small timeout to prevent update loops
38
+ setTimeout(() => editor.commands.setContent(value), 0);
39
+ }
40
+ }, [value, editor]);
41
+ if (!mounted)
42
+ return null;
43
+ return (_jsxs("div", { className: "flex flex-col gap-1.5 w-full", children: [label && (_jsx("label", { className: "text-sm font-medium leading-none text-gray-700", children: label })), _jsxs("div", { className: "rounded-md border border-gray-200 shadow-sm overflow-hidden focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-blue-500", children: [editor && (_jsxs("div", { className: "flex items-center gap-1 p-2 bg-gray-50 border-b border-gray-200 flex-wrap", children: [_jsx("button", { type: "button", onClick: () => editor.chain().focus().toggleBold().run(), className: `p-1.5 rounded hover:bg-gray-200 text-xs font-semibold text-gray-700 ${editor.isActive('bold') ? 'bg-gray-200' : ''}`, title: "Bold", children: "B" }), _jsx("button", { type: "button", onClick: () => editor.chain().focus().toggleItalic().run(), className: `p-1.5 rounded hover:bg-gray-200 text-xs italic text-gray-700 ${editor.isActive('italic') ? 'bg-gray-200' : ''}`, title: "Italic", children: "I" }), _jsx("div", { className: "w-[1px] h-4 bg-gray-300 mx-1" }), _jsx("button", { type: "button", onClick: () => editor.chain().focus().toggleHeading({ level: 3 }).run(), className: `p-1.5 rounded hover:bg-gray-200 text-xs font-semibold text-gray-700 ${editor.isActive('heading', { level: 3 }) ? 'bg-gray-200' : ''}`, title: "Heading", children: "H3" }), _jsx("div", { className: "w-[1px] h-4 bg-gray-300 mx-1" }), _jsx("button", { type: "button", onClick: () => editor.chain().focus().toggleBulletList().run(), className: `p-1.5 rounded hover:bg-gray-200 text-xs text-gray-700 ${editor.isActive('bulletList') ? 'bg-gray-200' : ''}`, title: "Bullet List", children: "\u2022 List" }), _jsx("button", { type: "button", onClick: () => editor.chain().focus().toggleOrderedList().run(), className: `p-1.5 rounded hover:bg-gray-200 text-xs text-gray-700 ${editor.isActive('orderedList') ? 'bg-gray-200' : ''}`, title: "Numbered List", children: "1. List" }), _jsx("div", { className: "w-[1px] h-4 bg-gray-300 mx-1" }), _jsx("button", { type: "button", onClick: () => editor.chain().focus().clearNodes().unsetAllMarks().run(), className: "p-1.5 rounded hover:bg-gray-200 text-xs text-gray-500", title: "Clear Formatting", children: "Clear" })] })), _jsx(EditorContent, { editor: editor })] })] }));
44
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinyweb_dev/oe-exam-sdk",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Reusable OceanEdu question components.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",