@jhits/plugin-blog 0.0.5 → 0.0.7
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 +16 -16
- package/src/api/config-handler.ts +76 -0
- package/src/api/handler.ts +4 -4
- package/src/api/router.ts +17 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useCategories.ts +76 -0
- package/src/index.tsx +8 -27
- package/src/init.tsx +0 -9
- package/src/lib/config-storage.ts +65 -0
- package/src/lib/layouts/blocks/ColumnsBlock.tsx +177 -13
- package/src/lib/layouts/blocks/ColumnsBlock.tsx.tmp +81 -0
- package/src/lib/layouts/registerLayoutBlocks.ts +6 -1
- package/src/lib/mappers/apiMapper.ts +53 -22
- package/src/registry/BlockRegistry.ts +1 -4
- package/src/state/EditorContext.tsx +39 -33
- package/src/state/types.ts +1 -1
- package/src/types/index.ts +2 -0
- package/src/types/post.ts +4 -0
- package/src/views/CanvasEditor/BlockWrapper.tsx +87 -24
- package/src/views/CanvasEditor/CanvasEditorView.tsx +214 -794
- package/src/views/CanvasEditor/EditorBody.tsx +317 -127
- package/src/views/CanvasEditor/EditorHeader.tsx +106 -17
- package/src/views/CanvasEditor/LayoutContainer.tsx +208 -380
- package/src/views/CanvasEditor/components/EditorCanvas.tsx +160 -0
- package/src/views/CanvasEditor/components/EditorLibrary.tsx +122 -0
- package/src/views/CanvasEditor/components/EditorSidebar.tsx +181 -0
- package/src/views/CanvasEditor/components/ErrorBanner.tsx +31 -0
- package/src/views/CanvasEditor/components/FeaturedMediaSection.tsx +260 -49
- package/src/views/CanvasEditor/components/index.ts +11 -0
- package/src/views/CanvasEditor/hooks/index.ts +10 -0
- package/src/views/CanvasEditor/hooks/useHeroBlock.ts +103 -0
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.ts +142 -0
- package/src/views/CanvasEditor/hooks/usePostLoader.ts +39 -0
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.ts +55 -0
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.ts +339 -0
- package/src/views/PostManager/PostCards.tsx +18 -13
- package/src/views/PostManager/PostFilters.tsx +15 -0
- package/src/views/PostManager/PostManagerView.tsx +21 -15
- package/src/views/PostManager/PostTable.tsx +7 -4
|
@@ -274,7 +274,7 @@ export function BlockWrapper({
|
|
|
274
274
|
>
|
|
275
275
|
{/* Left Margin Controls - Visible on Hover */}
|
|
276
276
|
<div
|
|
277
|
-
className={`absolute -left-16 top-1/2 -translate-y-1/2 flex flex-col gap-2 transition-all duration-200
|
|
277
|
+
className={`absolute -left-16 top-1/2 -translate-y-1/2 flex flex-col gap-2 transition-all duration-200 ${showControls ? 'opacity-100' : 'opacity-0 pointer-events-none'
|
|
278
278
|
}`}
|
|
279
279
|
onMouseEnter={() => setIsControlsHovered(true)}
|
|
280
280
|
onMouseLeave={() => setIsControlsHovered(false)}
|
|
@@ -321,7 +321,7 @@ export function BlockWrapper({
|
|
|
321
321
|
|
|
322
322
|
{/* Block Header - Positioned above the component */}
|
|
323
323
|
<div
|
|
324
|
-
className={`mb-2 transition-all relative
|
|
324
|
+
className={`mb-2 transition-all relative ${isHovered || showControls || showSettingsMenu
|
|
325
325
|
? 'opacity-100 translate-y-0'
|
|
326
326
|
: 'opacity-0 -translate-y-2 pointer-events-none'
|
|
327
327
|
}`}
|
|
@@ -339,7 +339,7 @@ export function BlockWrapper({
|
|
|
339
339
|
</span>
|
|
340
340
|
|
|
341
341
|
{/* Layout Block Settings - Inline in header */}
|
|
342
|
-
{block.type === 'section' && (
|
|
342
|
+
{/* {block.type === 'section' && (
|
|
343
343
|
<div className="flex items-center gap-2 ml-4 flex-1 min-w-0">
|
|
344
344
|
<select
|
|
345
345
|
value={(block.data.padding as string) || 'md'}
|
|
@@ -363,31 +363,95 @@ export function BlockWrapper({
|
|
|
363
363
|
<option value="CREAM">Cream</option>
|
|
364
364
|
</select>
|
|
365
365
|
</div>
|
|
366
|
-
)}
|
|
367
|
-
|
|
368
|
-
{block.type === 'columns' && (
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
>
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
<
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
366
|
+
)} */}
|
|
367
|
+
|
|
368
|
+
{block.type === 'columns' && (() => {
|
|
369
|
+
const columnCount = block.data.columnCount as number | undefined;
|
|
370
|
+
const layout = block.data.layout as string | undefined;
|
|
371
|
+
|
|
372
|
+
// Determine number of columns
|
|
373
|
+
let numColumns: number;
|
|
374
|
+
if (columnCount !== undefined && columnCount > 0) {
|
|
375
|
+
numColumns = columnCount;
|
|
376
|
+
} else if (layout) {
|
|
377
|
+
// Legacy layout system
|
|
378
|
+
const layoutMap: Record<string, number> = {
|
|
379
|
+
'50-50': 2,
|
|
380
|
+
'33-66': 2,
|
|
381
|
+
'66-33': 2,
|
|
382
|
+
'25-25-25-25': 4,
|
|
383
|
+
'25-75': 2,
|
|
384
|
+
'75-25': 2,
|
|
385
|
+
};
|
|
386
|
+
numColumns = layoutMap[layout] || 2;
|
|
387
|
+
} else {
|
|
388
|
+
numColumns = 2;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Get column widths
|
|
392
|
+
const storedWidths = block.data.columnWidths as number[] | undefined;
|
|
393
|
+
const columnWidths = storedWidths && storedWidths.length === numColumns
|
|
394
|
+
? storedWidths
|
|
395
|
+
: Array(numColumns).fill(Math.floor(100 / numColumns));
|
|
396
|
+
|
|
397
|
+
return (
|
|
398
|
+
<div className="flex items-center gap-1.5 ml-4 flex-1 min-w-0">
|
|
399
|
+
{Array.from({ length: numColumns }).map((_, colIndex) => (
|
|
400
|
+
<div key={colIndex} className="flex items-center gap-1">
|
|
401
|
+
<input
|
|
402
|
+
type="number"
|
|
403
|
+
min="10"
|
|
404
|
+
max="90"
|
|
405
|
+
step="1"
|
|
406
|
+
value={columnWidths[colIndex] || 50}
|
|
407
|
+
onChange={(e) => {
|
|
408
|
+
const newWidth = parseInt(e.target.value) || 50;
|
|
409
|
+
const newWidths = [...columnWidths];
|
|
410
|
+
newWidths[colIndex] = Math.max(10, Math.min(90, newWidth));
|
|
411
|
+
|
|
412
|
+
// Normalize remaining columns to sum to 100
|
|
413
|
+
const remainingTotal = newWidths.reduce((sum, w, i) => i === colIndex ? sum : sum + w, 0);
|
|
414
|
+
const remainingTarget = 100 - newWidths[colIndex];
|
|
415
|
+
if (remainingTotal > 0 && remainingTarget > 0) {
|
|
416
|
+
const scale = remainingTarget / remainingTotal;
|
|
417
|
+
newWidths.forEach((w, i) => {
|
|
418
|
+
if (i !== colIndex) {
|
|
419
|
+
newWidths[i] = Math.round(w * scale);
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Ensure sum is exactly 100
|
|
425
|
+
const finalTotal = newWidths.reduce((sum, w) => sum + w, 0);
|
|
426
|
+
if (finalTotal !== 100) {
|
|
427
|
+
const diff = 100 - finalTotal;
|
|
428
|
+
const lastIndex = newWidths.length - 1;
|
|
429
|
+
newWidths[lastIndex] = Math.max(10, newWidths[lastIndex] + diff);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
onUpdate({
|
|
433
|
+
...block.data,
|
|
434
|
+
columnWidths: newWidths,
|
|
435
|
+
layout: undefined, // Clear layout when using custom widths
|
|
436
|
+
});
|
|
437
|
+
}}
|
|
438
|
+
onClick={(e) => e.stopPropagation()}
|
|
439
|
+
className="w-12 text-[10px] font-bold bg-white dark:bg-neutral-900/50 border border-neutral-300 dark:border-neutral-700 px-1.5 py-0.5 rounded outline-none focus:border-primary transition-all dark:text-neutral-100 text-center"
|
|
440
|
+
/>
|
|
441
|
+
<span className="text-[9px] text-neutral-400 dark:text-neutral-500">%</span>
|
|
442
|
+
</div>
|
|
443
|
+
))}
|
|
444
|
+
</div>
|
|
445
|
+
);
|
|
446
|
+
})()}
|
|
383
447
|
</div>
|
|
384
|
-
<div className="relative shrink-0 z-
|
|
448
|
+
<div className="relative shrink-0 z-20" ref={settingsMenuRef}>
|
|
385
449
|
<button
|
|
386
450
|
onClick={(e) => {
|
|
387
451
|
e.stopPropagation();
|
|
388
452
|
setShowSettingsMenu(!showSettingsMenu);
|
|
389
453
|
}}
|
|
390
|
-
className="p-1 rounded transition-colors text-neutral-400 dark:text-neutral-500 hover:text-neutral-950 dark:hover:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 relative z-
|
|
454
|
+
className="p-1 rounded transition-colors text-neutral-400 dark:text-neutral-500 hover:text-neutral-950 dark:hover:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 relative z-20"
|
|
391
455
|
title="Block settings"
|
|
392
456
|
>
|
|
393
457
|
<Settings2 size={12} />
|
|
@@ -395,7 +459,7 @@ export function BlockWrapper({
|
|
|
395
459
|
|
|
396
460
|
{/* Settings Dropdown Menu */}
|
|
397
461
|
{showSettingsMenu && (
|
|
398
|
-
<div className="absolute right-0 top-full mt-1 w-40 bg-white dark:bg-neutral-900 border border-neutral-300 dark:border-neutral-700 rounded-lg shadow-xl z-
|
|
462
|
+
<div className="absolute right-0 top-full mt-1 w-40 bg-white dark:bg-neutral-900 border border-neutral-300 dark:border-neutral-700 rounded-lg shadow-xl z-20 overflow-hidden">
|
|
399
463
|
<button
|
|
400
464
|
onClick={(e) => {
|
|
401
465
|
e.stopPropagation();
|
|
@@ -418,7 +482,6 @@ export function BlockWrapper({
|
|
|
418
482
|
? 'border-primary/60 dark:border-primary/40 bg-primary/5 dark:bg-primary/10'
|
|
419
483
|
: 'border-neutral-200 dark:border-neutral-700 bg-transparent'
|
|
420
484
|
}`}
|
|
421
|
-
style={{ zIndex: 1 }}
|
|
422
485
|
>
|
|
423
486
|
{/* Edit Component - No padding, let the component control its own spacing */}
|
|
424
487
|
<div className="relative" style={{ userSelect: 'text' }}>
|