Skip to content

Commit 518c97c

Browse files
committed
style: edit statement adapts to mobile
1 parent 237a2dc commit 518c97c

File tree

2 files changed

+241
-123
lines changed

2 files changed

+241
-123
lines changed

src/features/statements/components/StatementItem.tsx

Lines changed: 233 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -182,146 +182,256 @@ const StatementItem: React.FC<StatementItemProps> = ({
182182

183183
if (isEditing) {
184184
return (
185-
<div className='flex items-center space-x-2 bg-gray-100 p-2 rounded'>
186-
{/* Privacy toggle button */}
187-
<Button
188-
variant={draft.isPublic ? 'success' : 'destructive'}
189-
size='compact'
190-
onClick={() => {
191-
// Create a new draft object to ensure React detects the change
192-
setDraft((prevDraft) => {
193-
const newDraft = JSON.parse(JSON.stringify(prevDraft));
194-
newDraft.isPublic = !prevDraft.isPublic;
195-
return newDraft;
196-
});
197-
}}
198-
className='p-2 transition-colors shadow-sm'
199-
>
200-
{draft.isPublic ? <MailPlus size={16} /> : <MailX size={16} />}
201-
</Button>
185+
<div className='bg-gray-100 p-3 rounded-lg shadow'>
186+
{/* Desktop layout - horizontal row */}
187+
<div className='hidden md:flex md:items-center md:space-x-2'>
188+
{/* Privacy toggle button */}
189+
<Button
190+
variant={draft.isPublic ? 'success' : 'destructive'}
191+
size='compact'
192+
onClick={() => {
193+
// Create a new draft object to ensure React detects the change
194+
setDraft((prevDraft) => {
195+
const newDraft = JSON.parse(JSON.stringify(prevDraft));
196+
newDraft.isPublic = !prevDraft.isPublic;
197+
return newDraft;
198+
});
199+
}}
200+
className='p-2 transition-colors shadow-sm'
201+
>
202+
{draft.isPublic ? <MailPlus size={16} /> : <MailX size={16} />}
203+
</Button>
202204

203-
<div className='flex flex-1 items-center flex-wrap gap-2'>
204-
<div className='flex space-x-2 flex-wrap'>
205-
{/* Subject */}
205+
<div className='flex flex-1 items-center flex-wrap gap-2'>
206+
<div className='flex space-x-2 flex-wrap'>
207+
{/* Subject */}
208+
<div
209+
onClick={() => onPartClick('subject', draft.id)}
210+
className='cursor-pointer px-2 py-1 rounded bg-subjectSelector hover:bg-subjectSelectorHover'
211+
>
212+
{draft.atoms.subject}
213+
</div>
214+
{/* Verb */}
215+
<div
216+
onClick={() => onPartClick('verb', draft.id)}
217+
className='cursor-pointer px-2 py-1 rounded bg-verbSelector hover:bg-verbSelectorHover'
218+
>
219+
<span>{getVerbName(draft.atoms.verb)}</span>
220+
</div>
221+
{/* Object */}
222+
<div
223+
onClick={() => onPartClick('object', draft.id)}
224+
className='cursor-pointer px-2 py-1 rounded bg-objectInput hover:bg-objectInputHover'
225+
>
226+
{draft.atoms.object}
227+
</div>
228+
</div>
229+
{/* Category */}
206230
<div
231+
onClick={() => onPartClick('category', draft.id)}
232+
className='cursor-pointer px-2 py-1 rounded bg-categorySelector text-black flex items-center gap-1 hover:bg-categorySelectorHover'
233+
>
234+
<span className='mr-1'>📁</span>
235+
{/* Use formatted category name */}
236+
{draft.category &&
237+
draft.category.toLowerCase() !== 'uncategorized' &&
238+
draft.category.toLowerCase() !== 'uncategorised'
239+
? getCategoryDisplayName(draft.category)
240+
: 'Uncategorized'}
241+
</div>
242+
</div>
243+
244+
<div className='flex items-center space-x-2 ml-auto'>
245+
{/* Save button with tooltip */}
246+
<Tooltip>
247+
<TooltipTrigger asChild>
248+
<span className='inline-block'>
249+
<Button
250+
variant='success'
251+
size='compact'
252+
onClick={async () => {
253+
setIsSaving(true);
254+
const updatedDraft = { ...draft };
255+
updatedDraft.input = `${draft.atoms.subject} ${getVerbName(
256+
draft.atoms.verb
257+
)} ${draft.atoms.object}`;
258+
await onLocalSave(updatedDraft);
259+
setIsSaving(false);
260+
}}
261+
disabled={!hasChanged || isSaving}
262+
className='shadow-sm p-2'
263+
>
264+
<Save size={16} />
265+
</Button>
266+
</span>
267+
</TooltipTrigger>
268+
<TooltipContent className='p-2 bg-black text-white rounded'>
269+
Save changes
270+
</TooltipContent>
271+
</Tooltip>
272+
273+
{/* Cancel button with PenOff icon and tooltip */}
274+
<Tooltip>
275+
<TooltipTrigger asChild>
276+
<span className='inline-block'>
277+
<Button
278+
variant='outline'
279+
size='compact'
280+
onClick={() => {
281+
setDraft(JSON.parse(JSON.stringify(initialDraft)));
282+
if (onCancel) onCancel(statement.id);
283+
}}
284+
className='p-2'
285+
>
286+
<PenOff size={16} />
287+
</Button>
288+
</span>
289+
</TooltipTrigger>
290+
<TooltipContent className='p-2 bg-black text-white rounded'>
291+
Cancel editing
292+
</TooltipContent>
293+
</Tooltip>
294+
295+
{/* Delete button with tooltip */}
296+
<Tooltip>
297+
<TooltipTrigger asChild>
298+
<span className='inline-block'>
299+
<Button
300+
variant='destructive'
301+
size='compact'
302+
onClick={() => onDelete(draft.id)}
303+
className='shadow-sm p-2'
304+
>
305+
<Trash2 size={16} />
306+
</Button>
307+
</span>
308+
</TooltipTrigger>
309+
<TooltipContent className='p-2 bg-black text-white rounded'>
310+
Delete statement
311+
</TooltipContent>
312+
</Tooltip>
313+
</div>
314+
</div>
315+
316+
{/* Mobile layout - vertical stack */}
317+
<div className='md:hidden flex flex-col space-y-4'>
318+
{/* Statement header with privacy toggle */}
319+
<div className='flex items-center justify-between pb-2 border-b border-gray-300'>
320+
<h3 className='text-sm font-semibold text-gray-700'>Edit Statement</h3>
321+
<Button
322+
variant={draft.isPublic ? 'success' : 'destructive'}
323+
size='compact'
207324
onClick={() => {
208-
// Just call onPartClick to open the modal, and don't try to edit inline
209-
onPartClick('subject', draft.id);
325+
setDraft((prevDraft) => {
326+
const newDraft = JSON.parse(JSON.stringify(prevDraft));
327+
newDraft.isPublic = !prevDraft.isPublic;
328+
return newDraft;
329+
});
210330
}}
211-
className='cursor-pointer px-2 py-1 rounded bg-subjectSelector hover:bg-subjectSelectorHover'
331+
className='p-2 transition-colors shadow-sm'
332+
>
333+
{draft.isPublic ?
334+
<><MailPlus size={16} /><span className="hidden sm:inline ml-1 text-xs">Public</span></> :
335+
<><MailX size={16} /><span className="hidden sm:inline ml-1 text-xs">Private</span></>
336+
}
337+
</Button>
338+
</div>
339+
340+
{/* Statement parts grid */}
341+
<div className='grid grid-cols-2 gap-2'>
342+
{/* Subject */}
343+
<div
344+
onClick={() => onPartClick('subject', draft.id)}
345+
className='cursor-pointer p-3 rounded bg-subjectSelector hover:bg-subjectSelectorHover flex flex-col'
212346
>
213-
{draft.atoms.subject}
347+
<span className='text-xs mb-1 opacity-70'>Subject</span>
348+
<span className='font-medium truncate'>{draft.atoms.subject}</span>
214349
</div>
350+
215351
{/* Verb */}
216352
<div
217-
onClick={() => {
218-
// Just call onPartClick to open the modal, and don't try to edit inline
219-
onPartClick('verb', draft.id);
220-
}}
221-
className='cursor-pointer px-2 py-1 rounded bg-verbSelector hover:bg-verbSelectorHover'
353+
onClick={() => onPartClick('verb', draft.id)}
354+
className='cursor-pointer p-3 rounded bg-verbSelector hover:bg-verbSelectorHover flex flex-col'
222355
>
223-
<span>{getVerbName(draft.atoms.verb)}</span>
356+
<span className='text-xs mb-1 opacity-70'>Verb</span>
357+
<span className='font-medium truncate'>{getVerbName(draft.atoms.verb)}</span>
224358
</div>
359+
225360
{/* Object */}
226361
<div
227-
onClick={() => {
228-
// Just call onPartClick to open the modal, and don't try to edit inline
229-
onPartClick('object', draft.id);
230-
}}
231-
className='cursor-pointer px-2 py-1 rounded bg-objectInput hover:bg-objectInputHover'
362+
onClick={() => onPartClick('object', draft.id)}
363+
className='cursor-pointer p-3 rounded bg-objectInput hover:bg-objectInputHover flex flex-col'
364+
>
365+
<span className='text-xs mb-1 opacity-70'>Object</span>
366+
<span className='font-medium truncate'>{draft.atoms.object}</span>
367+
</div>
368+
369+
{/* Category */}
370+
<div
371+
onClick={() => onPartClick('category', draft.id)}
372+
className='cursor-pointer p-3 rounded bg-categorySelector hover:bg-categorySelectorHover flex flex-col'
232373
>
233-
{draft.atoms.object}
374+
<span className='text-xs mb-1 opacity-70'>Category</span>
375+
<span className='font-medium truncate'>
376+
{draft.category &&
377+
draft.category.toLowerCase() !== 'uncategorized' &&
378+
draft.category.toLowerCase() !== 'uncategorised'
379+
? getCategoryDisplayName(draft.category)
380+
: 'Uncategorized'}
381+
</span>
234382
</div>
235383
</div>
236-
{/* Category */}
237-
<div
238-
onClick={() => {
239-
// Open the category modal
240-
onPartClick('category', draft.id);
241-
}}
242-
className='cursor-pointer px-2 py-1 rounded bg-categorySelector text-black flex items-center gap-1 hover:bg-categorySelectorHover'
243-
>
244-
<span className='mr-1'>📁</span>
245-
{/* Use formatted category name */}
246-
{draft.category &&
247-
draft.category.toLowerCase() !== 'uncategorized' &&
248-
draft.category.toLowerCase() !== 'uncategorised'
249-
? getCategoryDisplayName(draft.category)
250-
: 'Uncategorized'}
384+
385+
{/* Action buttons - bottom fixed bar */}
386+
<div className='flex justify-between items-center pt-3 mt-2 border-t border-gray-300'>
387+
{/* Delete button */}
388+
<Button
389+
variant='destructive'
390+
size='compact'
391+
onClick={() => onDelete(draft.id)}
392+
className='min-w-[40px] xs:px-3 py-2 flex justify-center'
393+
>
394+
<Trash2 size={16} className="xs:mr-1" />
395+
<span className="hidden xs:inline text-xs">Delete</span>
396+
</Button>
397+
398+
<div className='flex space-x-2'>
399+
{/* Cancel button */}
400+
<Button
401+
variant='outline'
402+
size='compact'
403+
onClick={() => {
404+
setDraft(JSON.parse(JSON.stringify(initialDraft)));
405+
if (onCancel) onCancel(statement.id);
406+
}}
407+
className='min-w-[40px] xs:px-3 py-2 flex justify-center'
408+
>
409+
<PenOff size={16} className="xs:mr-1" />
410+
<span className="hidden xs:inline text-xs">Cancel</span>
411+
</Button>
412+
413+
{/* Save button */}
414+
<Button
415+
variant='success'
416+
size='compact'
417+
onClick={async () => {
418+
setIsSaving(true);
419+
const updatedDraft = { ...draft };
420+
updatedDraft.input = `${draft.atoms.subject} ${getVerbName(
421+
draft.atoms.verb
422+
)} ${draft.atoms.object}`;
423+
await onLocalSave(updatedDraft);
424+
setIsSaving(false);
425+
}}
426+
disabled={!hasChanged || isSaving}
427+
className='min-w-[40px] xs:px-3 py-2 flex justify-center'
428+
>
429+
<Save size={16} className="xs:mr-1" />
430+
<span className="hidden xs:inline text-xs">Save</span>
431+
</Button>
432+
</div>
251433
</div>
252434
</div>
253-
<div className='flex items-center space-x-2 ml-auto'>
254-
{/* Save button with tooltip */}
255-
<Tooltip>
256-
<TooltipTrigger asChild>
257-
<span className='inline-block'>
258-
<Button
259-
variant='success'
260-
size='compact'
261-
onClick={async () => {
262-
setIsSaving(true);
263-
// Update the input field to reflect the edited statement
264-
const updatedDraft = { ...draft };
265-
updatedDraft.input = `${draft.atoms.subject} ${getVerbName(
266-
draft.atoms.verb
267-
)} ${draft.atoms.object}`;
268-
await onLocalSave(updatedDraft);
269-
setIsSaving(false);
270-
}}
271-
disabled={!hasChanged || isSaving}
272-
className='shadow-sm p-2'
273-
>
274-
<Save size={16} />
275-
</Button>
276-
</span>
277-
</TooltipTrigger>
278-
<TooltipContent className='p-2 bg-black text-white rounded'>
279-
Save changes
280-
</TooltipContent>
281-
</Tooltip>
282-
283-
{/* Cancel button with PenOff icon and tooltip */}
284-
<Tooltip>
285-
<TooltipTrigger asChild>
286-
<span className='inline-block'>
287-
<Button
288-
variant='outline'
289-
size='compact'
290-
onClick={() => {
291-
// Deep clone to avoid reference issues
292-
setDraft(JSON.parse(JSON.stringify(initialDraft)));
293-
if (onCancel) onCancel(statement.id);
294-
}}
295-
className='p-2'
296-
>
297-
<PenOff size={16} />
298-
</Button>
299-
</span>
300-
</TooltipTrigger>
301-
<TooltipContent className='p-2 bg-black text-white rounded'>
302-
Cancel editing
303-
</TooltipContent>
304-
</Tooltip>
305-
306-
{/* Delete button with tooltip */}
307-
<Tooltip>
308-
<TooltipTrigger asChild>
309-
<span className='inline-block'>
310-
<Button
311-
variant='destructive'
312-
size='compact'
313-
onClick={() => onDelete(draft.id)}
314-
className='shadow-sm p-2'
315-
>
316-
<Trash2 size={16} />
317-
</Button>
318-
</span>
319-
</TooltipTrigger>
320-
<TooltipContent className='p-2 bg-black text-white rounded'>
321-
Delete statement
322-
</TooltipContent>
323-
</Tooltip>
324-
</div>
325435
</div>
326436
);
327437
}

tailwind.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ export default {
1414
'2xl': '1400px',
1515
},
1616
},
17+
screens: {
18+
'xs': '480px',
19+
'sm': '640px',
20+
'md': '768px',
21+
'lg': '1024px',
22+
'xl': '1280px',
23+
'2xl': '1536px',
24+
},
1725
extend: {
1826
colors: {
1927
subjectSelector: 'var(--subject-selector)',

0 commit comments

Comments
 (0)