Skip to content

Commit 5cfc301

Browse files
authored
Merge pull request #20 from yoanbernabeu/marko78700/main
Marko78700/main
2 parents 0197c05 + 834da90 commit 5cfc301

File tree

5 files changed

+452
-207
lines changed

5 files changed

+452
-207
lines changed

components/Block.tsx

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,8 @@ const Block: React.FC<BlockProps> = ({
391391
aria-label="Resize block"
392392
data-resize-handle="true"
393393
className={`absolute bottom-2 right-2 z-30 transition-opacity pointer-events-auto ${
394-
isResizing ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'
395-
}`}
394+
isResizing ? 'opacity-100' : 'opacity-0 group-hover:opacity-100 focus:opacity-100'
395+
} focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 rounded-lg`}
396396
onPointerDown={(e) => {
397397
e.preventDefault();
398398
e.stopPropagation();
@@ -432,6 +432,16 @@ const Block: React.FC<BlockProps> = ({
432432
if (block.type === BlockType.SPACER) {
433433
return (
434434
<motion.div
435+
role="button"
436+
tabIndex={0}
437+
aria-label={`Edit block ${block.title ?? block.type}`}
438+
aria-grabbed={isDragging ? 'true' : 'false'}
439+
onKeyDown={(e) => {
440+
if (e.key === 'Enter' || e.key === ' ') {
441+
e.preventDefault();
442+
onEdit(block);
443+
}
444+
}}
435445
layoutId={block.id}
436446
layout
437447
draggable={!isResizing}
@@ -462,6 +472,7 @@ const Block: React.FC<BlockProps> = ({
462472
${isDragging ? 'opacity-40 scale-95' : ''}
463473
transition-all duration-200 group
464474
flex items-center justify-center
475+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
465476
`}
466477
style={{ borderRadius, ...gridPositionStyle }}
467478
>
@@ -499,6 +510,15 @@ const Block: React.FC<BlockProps> = ({
499510

500511
return (
501512
<motion.a
513+
tabIndex={0}
514+
aria-label={`Edit block ${block.title ?? block.type}`}
515+
aria-grabbed={isDragging ? 'true' : 'false'}
516+
onKeyDown={(e) => {
517+
if (e.key === 'Enter' || e.key === ' ') {
518+
e.preventDefault();
519+
onEdit(block);
520+
}
521+
}}
502522
layoutId={block.id}
503523
layout
504524
href={url || undefined}
@@ -538,6 +558,7 @@ const Block: React.FC<BlockProps> = ({
538558
transition-all duration-200 group
539559
flex items-center justify-center
540560
shadow-sm border border-gray-100
561+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
541562
`}
542563
style={{
543564
...gridPositionStyle,
@@ -563,27 +584,31 @@ const Block: React.FC<BlockProps> = ({
563584

564585
{/* Action buttons */}
565586
{showActionButtons && (
566-
<div className="absolute top-1 right-1 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity z-20">
587+
<div className="absolute top-1 right-1 flex gap-1 opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity z-20">
567588
{onDuplicate && (
568589
<button
590+
type="button"
591+
aria-label={`Duplicate ${block.title ?? 'block'}`}
569592
onClick={(e) => {
570593
e.preventDefault();
571594
e.stopPropagation();
572595
onDuplicate(block.id);
573596
}}
574-
className="p-1 bg-white/90 text-gray-800 rounded-md shadow-sm hover:bg-white"
597+
className="p-1 bg-white/90 text-gray-800 rounded-md shadow-sm hover:bg-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-400"
575598
title="Duplicate block"
576599
>
577600
<CopyPlus size={12} />
578601
</button>
579602
)}
580603
<button
604+
type="button"
605+
aria-label={`Delete ${block.title ?? 'block'}`}
581606
onClick={(e) => {
582607
e.preventDefault();
583608
e.stopPropagation();
584609
onDelete(block.id);
585610
}}
586-
className="p-1 bg-red-500/80 hover:bg-red-600 text-white rounded-md shadow-sm"
611+
className="p-1 bg-red-500/80 hover:bg-red-600 text-white rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-400"
587612
title="Delete block"
588613
>
589614
<Trash2 size={12} />
@@ -643,6 +668,15 @@ const Block: React.FC<BlockProps> = ({
643668

644669
return (
645670
<motion.div
671+
tabIndex={0}
672+
aria-label={`Edit block ${block.title ?? block.type}`}
673+
aria-grabbed={isDragging ? 'true' : 'false'}
674+
onKeyDown={(e) => {
675+
if (e.key === 'Enter' || e.key === ' ') {
676+
e.preventDefault();
677+
onEdit(block);
678+
}
679+
}}
646680
layoutId={block.id}
647681
layout
648682
draggable={!isResizing}
@@ -686,6 +720,7 @@ const Block: React.FC<BlockProps> = ({
686720
${isDragTarget ? 'ring-2 ring-violet-500 z-20 scale-[1.02]' : ''}
687721
${isDragging ? 'opacity-40 scale-95' : ''}
688722
transition-all duration-300 select-none
723+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
689724
`}
690725
>
691726
{/* Drop indicator */}
@@ -725,8 +760,9 @@ const Block: React.FC<BlockProps> = ({
725760
href={`https://youtube.com/watch?v=${vid.id}`}
726761
target="_blank"
727762
rel="noopener noreferrer"
763+
aria-label={`Watch video: ${vid.title}`}
728764
onClick={(e) => e.stopPropagation()}
729-
className="relative overflow-hidden group/vid rounded bg-gray-100 block"
765+
className="relative overflow-hidden group/vid rounded bg-gray-100 block focus:outline-none focus:ring-2 focus:ring-blue-500"
730766
>
731767
<img
732768
src={vid.thumbnail}
@@ -767,6 +803,15 @@ const Block: React.FC<BlockProps> = ({
767803

768804
return (
769805
<motion.div
806+
tabIndex={0}
807+
aria-label={`Edit block ${block.title ?? block.type}`}
808+
aria-grabbed={isDragging ? 'true' : 'false'}
809+
onKeyDown={(e) => {
810+
if (e.key === 'Enter' || e.key === ' ') {
811+
e.preventDefault();
812+
onEdit(block);
813+
}
814+
}}
770815
layoutId={block.id}
771816
layout
772817
draggable={!isResizing}
@@ -824,6 +869,7 @@ const Block: React.FC<BlockProps> = ({
824869
${!isSelected && !enableTiltEffect ? 'shadow-sm hover:shadow-xl' : 'shadow-sm'}
825870
${isDragTarget ? 'ring-2 ring-violet-500' : ''}
826871
transition-all duration-300
872+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
827873
`}
828874
>
829875
{/* Drop indicator */}
@@ -841,27 +887,31 @@ const Block: React.FC<BlockProps> = ({
841887
)}
842888
{/* Action buttons */}
843889
{showActionButtons && (
844-
<div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity z-30 pointer-events-auto">
890+
<div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity z-30 pointer-events-auto">
845891
{onDuplicate && (
846892
<button
893+
type="button"
894+
aria-label={`Duplicate ${block.title ?? 'block'}`}
847895
onClick={(e) => {
848896
e.preventDefault();
849897
e.stopPropagation();
850898
onDuplicate(block.id);
851899
}}
852-
className="p-2 bg-white/80 hover:bg-white text-gray-800 rounded-lg shadow-sm backdrop-blur-sm"
900+
className="p-2 bg-white/80 hover:bg-white text-gray-800 rounded-lg shadow-sm backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-400"
853901
title="Duplicate block"
854902
>
855903
<CopyPlus size={14} />
856904
</button>
857905
)}
858906
<button
907+
type="button"
908+
aria-label={`Delete ${block.title ?? 'block'}`}
859909
onClick={(e) => {
860910
e.preventDefault();
861911
e.stopPropagation();
862912
onDelete(block.id);
863913
}}
864-
className="p-2 bg-red-500/80 hover:bg-red-600 text-white rounded-lg backdrop-blur-sm shadow-sm"
914+
className="p-2 bg-red-500/80 hover:bg-red-600 text-white rounded-lg backdrop-blur-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-400"
865915
title="Delete block"
866916
>
867917
<Trash2 size={14} />
@@ -884,12 +934,14 @@ const Block: React.FC<BlockProps> = ({
884934
{/* Reposition button - appears on hover */}
885935
{!isRepositioning && onInlineUpdate && (
886936
<button
937+
type="button"
938+
aria-label="Reposition background image"
887939
onClick={(e) => {
888940
e.preventDefault();
889941
e.stopPropagation();
890942
setIsRepositioning(true);
891943
}}
892-
className={`absolute ${repositionButtonOffsetClass} right-2 p-2 bg-black/60 hover:bg-black/80 text-white rounded-lg opacity-0 group-hover:opacity-100 transition-opacity z-20 backdrop-blur-sm`}
944+
className={`absolute ${repositionButtonOffsetClass} right-2 p-2 bg-black/60 hover:bg-black/80 text-white rounded-lg opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity z-20 backdrop-blur-sm focus:ring-2 focus:ring-offset-2 focus:ring-white focus:outline-none`}
893945
title="Reposition image"
894946
>
895947
<Move size={16} />
@@ -910,23 +962,27 @@ const Block: React.FC<BlockProps> = ({
910962
</div>
911963
<div className="absolute bottom-2 left-1/2 -translate-x-1/2 flex gap-2">
912964
<button
965+
type="button"
966+
aria-label="Cancel image repositioning"
913967
onClick={(e) => {
914968
e.preventDefault();
915969
e.stopPropagation();
916970
handleCancelMediaPosition();
917971
}}
918-
className="p-2 bg-gray-800 hover:bg-gray-700 text-white rounded-lg transition-colors"
972+
className="p-2 bg-gray-800 hover:bg-gray-700 text-white rounded-lg transition-colors focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 focus:outline-none"
919973
title="Cancel"
920974
>
921975
<X size={16} />
922976
</button>
923977
<button
978+
type="button"
979+
aria-label="Save image position"
924980
onClick={(e) => {
925981
e.preventDefault();
926982
e.stopPropagation();
927983
handleSaveMediaPosition();
928984
}}
929-
className="p-2 bg-green-600 hover:bg-green-500 text-white rounded-lg transition-colors"
985+
className="p-2 bg-green-600 hover:bg-green-500 text-white rounded-lg transition-colors focus:ring-2 focus:ring-offset-2 focus:ring-green-400 focus:outline-none"
930986
title="Save position"
931987
>
932988
<Check size={16} />
@@ -970,12 +1026,14 @@ const Block: React.FC<BlockProps> = ({
9701026
{/* Reposition button - appears on hover */}
9711027
{!isRepositioning && onInlineUpdate && (
9721028
<button
1029+
type="button"
1030+
aria-label="Reposition media content"
9731031
onClick={(e) => {
9741032
e.preventDefault();
9751033
e.stopPropagation();
9761034
setIsRepositioning(true);
9771035
}}
978-
className={`absolute ${repositionButtonOffsetClass} right-2 p-2 bg-black/60 hover:bg-black/80 text-white rounded-lg opacity-0 group-hover:opacity-100 transition-opacity pointer-events-auto z-20 backdrop-blur-sm`}
1036+
className={`absolute ${repositionButtonOffsetClass} right-2 p-2 bg-black/60 hover:bg-black/80 text-white rounded-lg opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity pointer-events-auto z-20 backdrop-blur-sm focus:ring-2 focus:ring-offset-2 focus:ring-white focus:outline-none`}
9791037
title="Reposition media"
9801038
>
9811039
<Move size={16} />
@@ -994,23 +1052,27 @@ const Block: React.FC<BlockProps> = ({
9941052
{/* Save/Cancel buttons */}
9951053
<div className="absolute bottom-2 left-1/2 -translate-x-1/2 flex gap-2 pointer-events-auto z-20">
9961054
<button
1055+
type="button"
1056+
aria-label="Cancel media repositioning"
9971057
onClick={(e) => {
9981058
e.preventDefault();
9991059
e.stopPropagation();
10001060
handleCancelMediaPosition();
10011061
}}
1002-
className="p-2 bg-gray-800 hover:bg-gray-700 text-white rounded-lg transition-colors"
1062+
className="p-2 bg-gray-800 hover:bg-gray-700 text-white rounded-lg transition-colors focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 focus:outline-none"
10031063
title="Cancel"
10041064
>
10051065
<X size={16} />
10061066
</button>
10071067
<button
1068+
type="button"
1069+
aria-label="Save media position"
10081070
onClick={(e) => {
10091071
e.preventDefault();
10101072
e.stopPropagation();
10111073
handleSaveMediaPosition();
10121074
}}
1013-
className="p-2 bg-green-600 hover:bg-green-500 text-white rounded-lg transition-colors"
1075+
className="p-2 bg-green-600 hover:bg-green-500 text-white rounded-lg transition-colors focus:ring-2 focus:ring-offset-2 focus:ring-green-400 focus:outline-none"
10141076
title="Save position"
10151077
>
10161078
<Check size={16} />
@@ -1131,6 +1193,7 @@ const Block: React.FC<BlockProps> = ({
11311193
<input
11321194
ref={titleInputRef}
11331195
type="text"
1196+
aria-label={`Edit block title${block.title ? `: ${block.title}` : ''}`}
11341197
value={editTitleValue}
11351198
onChange={(e) => setEditTitleValue(e.target.value)}
11361199
onBlur={handleTitleSave}
@@ -1168,6 +1231,7 @@ const Block: React.FC<BlockProps> = ({
11681231
<input
11691232
ref={subtextInputRef}
11701233
type="text"
1234+
aria-label={`Edit block subtitle${block.subtext ? `: ${block.subtext}` : ''}`}
11711235
value={editSubtextValue}
11721236
onChange={(e) => setEditSubtextValue(e.target.value)}
11731237
onBlur={handleSubtextSave}

0 commit comments

Comments
 (0)