@bendyline/squisq-editor-react 1.1.1 → 1.2.1
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/PreviewControls.d.ts +1 -1
- package/dist/PreviewControls.d.ts.map +1 -1
- package/dist/PreviewControls.js +36 -17
- package/dist/PreviewControls.js.map +1 -1
- package/dist/RawEditor.d.ts.map +1 -1
- package/dist/RawEditor.js +10 -1
- package/dist/RawEditor.js.map +1 -1
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar.js +155 -10
- package/dist/Toolbar.js.map +1 -1
- package/dist/__tests__/tiptapBridge.test.d.ts +2 -0
- package/dist/__tests__/tiptapBridge.test.d.ts.map +1 -0
- package/dist/__tests__/tiptapBridge.test.js +241 -0
- package/dist/__tests__/tiptapBridge.test.js.map +1 -0
- package/dist/tiptapBridge.d.ts.map +1 -1
- package/dist/tiptapBridge.js +142 -0
- package/dist/tiptapBridge.js.map +1 -1
- package/package.json +23 -23
- package/src/PreviewControls.tsx +116 -87
- package/src/RawEditor.tsx +10 -1
- package/src/Toolbar.tsx +444 -14
- package/src/__tests__/tiptapBridge.test.ts +290 -0
- package/src/styles/editor.css +286 -11
- package/src/tiptapBridge.ts +159 -0
package/src/PreviewControls.tsx
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - PreviewPanel (the actual player, which reads the selected values)
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { createContext, useContext, useState, useMemo, useEffect } from 'react';
|
|
13
|
+
import { createContext, useContext, useState, useMemo, useEffect, useRef } from 'react';
|
|
14
14
|
import type { ReactNode } from 'react';
|
|
15
15
|
import type { DisplayMode, CaptionStyle } from '@bendyline/squisq-react';
|
|
16
16
|
import type { ViewportPreset, ViewportConfig } from '@bendyline/squisq/schemas';
|
|
@@ -233,90 +233,132 @@ const selectStyle: React.CSSProperties = {
|
|
|
233
233
|
|
|
234
234
|
// ── Toolbar Controls Component ───────────────────────────────────
|
|
235
235
|
|
|
236
|
+
/** Hook to track whether the viewport is narrow. */
|
|
237
|
+
function useIsNarrow(breakpoint = 600): boolean {
|
|
238
|
+
const [narrow, setNarrow] = useState(
|
|
239
|
+
() => typeof window !== 'undefined' && window.innerWidth <= breakpoint,
|
|
240
|
+
);
|
|
241
|
+
useEffect(() => {
|
|
242
|
+
const mq = window.matchMedia(`(max-width: ${breakpoint}px)`);
|
|
243
|
+
const handler = (e: MediaQueryListEvent) => setNarrow(e.matches);
|
|
244
|
+
mq.addEventListener('change', handler);
|
|
245
|
+
return () => mq.removeEventListener('change', handler);
|
|
246
|
+
}, [breakpoint]);
|
|
247
|
+
return narrow;
|
|
248
|
+
}
|
|
249
|
+
|
|
236
250
|
/**
|
|
237
251
|
* Inline preview controls rendered in the main toolbar row.
|
|
238
|
-
*
|
|
252
|
+
* On narrow viewports, collapses into a single settings button with a dropdown.
|
|
239
253
|
*/
|
|
240
254
|
export function PreviewToolbarControls() {
|
|
241
255
|
const s = usePreviewSettings();
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
256
|
+
const isNarrow = useIsNarrow(768);
|
|
257
|
+
const [popoverOpen, setPopoverOpen] = useState(false);
|
|
258
|
+
const popoverRef = useRef<HTMLDivElement>(null);
|
|
259
|
+
|
|
260
|
+
// Close popover on outside click
|
|
261
|
+
useEffect(() => {
|
|
262
|
+
if (!popoverOpen) return;
|
|
263
|
+
const handler = (e: MouseEvent) => {
|
|
264
|
+
if (popoverRef.current && !popoverRef.current.contains(e.target as Node)) {
|
|
265
|
+
setPopoverOpen(false);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
document.addEventListener('mousedown', handler);
|
|
269
|
+
return () => document.removeEventListener('mousedown', handler);
|
|
270
|
+
}, [popoverOpen]);
|
|
271
|
+
|
|
272
|
+
const controls = (
|
|
273
|
+
<>
|
|
274
|
+
<PreviewSelect
|
|
275
|
+
label="Format"
|
|
255
276
|
value={s.activePreset}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
</option>
|
|
263
|
-
))}
|
|
264
|
-
</select>
|
|
265
|
-
|
|
266
|
-
<Divider />
|
|
267
|
-
|
|
268
|
-
<label style={labelStyle}>Mode:</label>
|
|
269
|
-
<select
|
|
277
|
+
options={VIEWPORT_OPTIONS}
|
|
278
|
+
onChange={(v) => s.setSelectedPreset(v as ViewportPreset)}
|
|
279
|
+
compact={isNarrow}
|
|
280
|
+
/>
|
|
281
|
+
<PreviewSelect
|
|
282
|
+
label="Mode"
|
|
270
283
|
value={s.activeDisplayMode}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
</option>
|
|
278
|
-
))}
|
|
279
|
-
</select>
|
|
280
|
-
|
|
281
|
-
<Divider />
|
|
282
|
-
|
|
283
|
-
<label style={labelStyle}>Theme:</label>
|
|
284
|
-
<select
|
|
284
|
+
options={DISPLAY_MODE_OPTIONS}
|
|
285
|
+
onChange={(v) => s.setSelectedDisplayMode(v as DisplayMode)}
|
|
286
|
+
compact={isNarrow}
|
|
287
|
+
/>
|
|
288
|
+
<PreviewSelect
|
|
289
|
+
label="Theme"
|
|
285
290
|
value={s.activeThemeId}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
</option>
|
|
293
|
-
))}
|
|
294
|
-
</select>
|
|
295
|
-
|
|
296
|
-
<Divider />
|
|
297
|
-
|
|
298
|
-
<label style={labelStyle}>Transform:</label>
|
|
299
|
-
<select
|
|
291
|
+
options={THEME_OPTIONS}
|
|
292
|
+
onChange={(v) => s.setSelectedThemeId(v)}
|
|
293
|
+
compact={isNarrow}
|
|
294
|
+
/>
|
|
295
|
+
<PreviewSelect
|
|
296
|
+
label="Transform"
|
|
300
297
|
value={s.activeTransformStyle}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
298
|
+
options={TRANSFORM_STYLE_OPTIONS}
|
|
299
|
+
onChange={(v) => s.setSelectedTransformStyle(v)}
|
|
300
|
+
compact={isNarrow}
|
|
301
|
+
/>
|
|
302
|
+
<PreviewSelect
|
|
303
|
+
label="Captions"
|
|
304
|
+
value={s.activeCaptionStyle}
|
|
305
|
+
options={CAPTION_STYLE_OPTIONS}
|
|
306
|
+
onChange={(v) => s.setSelectedCaptionStyle(v as CaptionStyle)}
|
|
307
|
+
compact={isNarrow}
|
|
308
|
+
/>
|
|
309
|
+
</>
|
|
310
|
+
);
|
|
310
311
|
|
|
311
|
-
|
|
312
|
+
if (isNarrow) {
|
|
313
|
+
return (
|
|
314
|
+
<div className="squisq-preview-controls-compact" ref={popoverRef}>
|
|
315
|
+
<button
|
|
316
|
+
className="squisq-toolbar-button"
|
|
317
|
+
onClick={() => setPopoverOpen((v) => !v)}
|
|
318
|
+
aria-label="Preview settings"
|
|
319
|
+
title="Preview settings"
|
|
320
|
+
aria-expanded={popoverOpen}
|
|
321
|
+
>
|
|
322
|
+
<svg
|
|
323
|
+
width="16"
|
|
324
|
+
height="16"
|
|
325
|
+
viewBox="0 0 16 16"
|
|
326
|
+
fill="none"
|
|
327
|
+
stroke="currentColor"
|
|
328
|
+
strokeWidth="1.5"
|
|
329
|
+
strokeLinecap="round"
|
|
330
|
+
strokeLinejoin="round"
|
|
331
|
+
>
|
|
332
|
+
<circle cx="8" cy="8" r="2.5" />
|
|
333
|
+
<path d="M13.5 8a5.5 5.5 0 01-.4 1.8l1.2 1.2-1.6 1.6-1.2-1.2A5.5 5.5 0 018 13.5a5.5 5.5 0 01-3.5-1.3L3.3 13.4 1.7 11.8l1.2-1.2A5.5 5.5 0 012.5 8c0-.6.1-1.2.4-1.8L1.7 5 3.3 3.4l1.2 1.2A5.5 5.5 0 018 2.5c1.3 0 2.5.5 3.5 1.3l1.2-1.2 1.6 1.6-1.2 1.2c.3.6.4 1.2.4 1.6z" />
|
|
334
|
+
</svg>
|
|
335
|
+
</button>
|
|
336
|
+
{popoverOpen && <div className="squisq-preview-controls-popover">{controls}</div>}
|
|
337
|
+
</div>
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return <div className="squisq-preview-controls-inline">{controls}</div>;
|
|
342
|
+
}
|
|
312
343
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
344
|
+
function PreviewSelect({
|
|
345
|
+
label,
|
|
346
|
+
value,
|
|
347
|
+
options,
|
|
348
|
+
onChange,
|
|
349
|
+
compact,
|
|
350
|
+
}: {
|
|
351
|
+
label: string;
|
|
352
|
+
value: string;
|
|
353
|
+
options: { key: string; label: string }[];
|
|
354
|
+
onChange: (value: string) => void;
|
|
355
|
+
compact?: boolean;
|
|
356
|
+
}) {
|
|
357
|
+
return (
|
|
358
|
+
<div className={`squisq-preview-control${compact ? ' squisq-preview-control--compact' : ''}`}>
|
|
359
|
+
<label style={labelStyle}>{label}:</label>
|
|
360
|
+
<select value={value} onChange={(e) => onChange(e.target.value)} style={selectStyle}>
|
|
361
|
+
{options.map((o) => (
|
|
320
362
|
<option key={o.key} value={o.key}>
|
|
321
363
|
{o.label}
|
|
322
364
|
</option>
|
|
@@ -325,16 +367,3 @@ export function PreviewToolbarControls() {
|
|
|
325
367
|
</div>
|
|
326
368
|
);
|
|
327
369
|
}
|
|
328
|
-
|
|
329
|
-
function Divider() {
|
|
330
|
-
return (
|
|
331
|
-
<span
|
|
332
|
-
style={{
|
|
333
|
-
width: '1px',
|
|
334
|
-
height: '16px',
|
|
335
|
-
background: 'var(--squisq-border, #d1d5db)',
|
|
336
|
-
margin: '0 2px',
|
|
337
|
-
}}
|
|
338
|
-
/>
|
|
339
|
-
);
|
|
340
|
-
}
|
package/src/RawEditor.tsx
CHANGED
|
@@ -12,7 +12,16 @@ import * as monaco from 'monaco-editor';
|
|
|
12
12
|
import { useEditorContext } from './EditorContext';
|
|
13
13
|
import { getAvailableTemplates } from '@bendyline/squisq/doc';
|
|
14
14
|
|
|
15
|
-
// Use locally installed monaco-editor instead of CDN
|
|
15
|
+
// Use locally installed monaco-editor instead of CDN.
|
|
16
|
+
//
|
|
17
|
+
// NOTE: By default this imports the full monaco-editor with all 80+ languages
|
|
18
|
+
// and workers (~9MB). Consumers can dramatically reduce bundle size by aliasing
|
|
19
|
+
// 'monaco-editor' to a slim entry in their bundler config. For example with Vite:
|
|
20
|
+
//
|
|
21
|
+
// resolve: { alias: [{ find: /^monaco-editor$/, replacement: './monaco-slim.ts' }] }
|
|
22
|
+
//
|
|
23
|
+
// Where monaco-slim.ts re-exports 'monaco-editor/esm/vs/editor/editor.api' plus
|
|
24
|
+
// only the language contributions needed (e.g. markdown, javascript, etc.).
|
|
16
25
|
loader.config({ monaco });
|
|
17
26
|
|
|
18
27
|
export interface RawEditorProps {
|