refactor: readonly inputs in cuesheet

This commit is contained in:
Carlos Valente
2025-08-02 06:44:55 +02:00
committed by Carlos Valente
parent ed4d3ba265
commit 765d5d97cf
7 changed files with 66 additions and 21 deletions
@@ -10,7 +10,12 @@
padding-inline: 0.5em; padding-inline: 0.5em;
outline: none; outline: none;
&:hover:not(:disabled) { &::placeholder {
color: $gray-500;
letter-spacing: 0;
}
&:hover:not(:disabled):not(:read-only) {
background-color: $gray-1100; background-color: $gray-1100;
} }
@@ -25,9 +30,8 @@
cursor: not-allowed; cursor: not-allowed;
} }
&::placeholder { &:read-only::placeholder {
color: $gray-500; opacity: 0;
letter-spacing: 0;
} }
} }
@@ -9,9 +9,13 @@
border-radius: $component-border-radius-md; border-radius: $component-border-radius-md;
border: 1px solid transparent; border: 1px solid transparent;
padding-inline: 0.5em;
outline: none; outline: none;
&::placeholder {
color: $gray-500;
letter-spacing: 0;
}
&:hover:not(:disabled) { &:hover:not(:disabled) {
background-color: $gray-1100; background-color: $gray-1100;
} }
@@ -26,21 +30,21 @@
cursor: not-allowed; cursor: not-allowed;
} }
&::placeholder { &:read-only::placeholder {
color: $gray-500; opacity: 0;
letter-spacing: 0;
} }
} }
.subtle { .subtle {
background-color: $gray-1200; background-color: $gray-1200;
padding-top: 0.5em; padding-inline: 0.5em;
padding-top: 0.25em;
} }
.ghosted { .ghosted {
background-color: transparent; background-color: transparent;
padding: 0; padding: 0;
padding-top: 0.5em; padding-top: 0.25em;
} }
.fluid { .fluid {
@@ -1,8 +1,8 @@
.imageInput { .imageInput {
&::placeholder { &:not(:read-only)::placeholder {
opacity: 0.2; opacity: 0.2;
} }
&:hover::placeholder { &:not(:read-only):hover::placeholder {
opacity: 1; opacity: 1;
} }
} }
@@ -7,12 +7,13 @@ import style from './EditableImage.module.scss';
interface EditableImageProps { interface EditableImageProps {
initialValue: string; initialValue: string;
readOnly?: boolean;
updateValue: (newValue: string) => void; updateValue: (newValue: string) => void;
} }
export default memo(EditableImage); export default memo(EditableImage);
function EditableImage({ initialValue, updateValue }: EditableImageProps) { function EditableImage({ initialValue, readOnly, updateValue }: EditableImageProps) {
const handleUpdate = (newValue: string) => { const handleUpdate = (newValue: string) => {
if (newValue === initialValue) { if (newValue === initialValue) {
return; return;
@@ -35,6 +36,9 @@ function EditableImage({ initialValue, updateValue }: EditableImageProps) {
variant='ghosted' variant='ghosted'
className={style.imageInput} className={style.imageInput}
fluid fluid
readOnly={readOnly}
// we disable the field to prevent receiving focus
disabled={readOnly}
placeholder='Paste image URL' placeholder='Paste image URL'
onBlur={(event) => handleUpdate(event.currentTarget.value)} onBlur={(event) => handleUpdate(event.currentTarget.value)}
onKeyDown={(event) => { onKeyDown={(event) => {
@@ -49,12 +53,14 @@ function EditableImage({ initialValue, updateValue }: EditableImageProps) {
return ( return (
<div className={style.imageCell}> <div className={style.imageCell}>
<div className={style.overlay}> {!readOnly && (
<Button onClick={openInNewTab}>Preview</Button> <div className={style.overlay}>
<Button variant='subtle-destructive' onClick={() => handleUpdate('')}> <Button onClick={openInNewTab}>Preview</Button>
Delete <Button variant='subtle-destructive' onClick={() => handleUpdate('')}>
</Button> Delete
</div> </Button>
</div>
)}
{Boolean(initialValue) && <img loading='lazy' src={initialValue} className={style.image} />} {Boolean(initialValue) && <img loading='lazy' src={initialValue} className={style.image} />}
</div> </div>
); );
@@ -0,0 +1,6 @@
.ghostedText {
min-height: 2rem;
padding: 0;
padding-top: 0.25em;
width: 100%;
}
@@ -0,0 +1,7 @@
import { PropsWithChildren } from 'react';
import style from './GhostedText.module.scss';
export default function GhostedText({ children }: PropsWithChildren) {
return <div className={style.ghostedText}>{children}</div>;
}
@@ -10,6 +10,7 @@ import { AppMode } from '../../../../ontimeConfig';
import DurationInput from './DurationInput'; import DurationInput from './DurationInput';
import EditableImage from './EditableImage'; import EditableImage from './EditableImage';
import FlagCell from './FlagCell'; import FlagCell from './FlagCell';
import GhostedText from './GhostedText';
import MultiLineCell from './MultiLineCell'; import MultiLineCell from './MultiLineCell';
import MutedText from './MutedText'; import MutedText from './MutedText';
import SingleLineCell from './SingleLineCell'; import SingleLineCell from './SingleLineCell';
@@ -139,6 +140,11 @@ function MakeMultiLineField({ row, column, table }: CellContext<OntimeEntry, unk
return null; return null;
} }
const canWrite = column.columnDef.meta?.canWrite;
if (!canWrite) {
return <GhostedText>{initialValue}</GhostedText>;
}
return <MultiLineCell initialValue={initialValue as string} handleUpdate={update} />; return <MultiLineCell initialValue={initialValue as string} handleUpdate={update} />;
} }
@@ -155,8 +161,9 @@ function LazyImage({ row, column, table }: CellContext<OntimeEntry, unknown>) {
return null; return null;
} }
const canWrite = column.columnDef.meta?.canWrite;
const initialValue = event.custom[column.id]; const initialValue = event.custom[column.id];
return <EditableImage initialValue={initialValue} updateValue={update} />; return <EditableImage initialValue={initialValue} updateValue={update} readOnly={!canWrite} />;
} }
function MakeSingleLineField({ row, column, table }: CellContext<OntimeEntry, unknown>) { function MakeSingleLineField({ row, column, table }: CellContext<OntimeEntry, unknown>) {
@@ -173,6 +180,11 @@ function MakeSingleLineField({ row, column, table }: CellContext<OntimeEntry, un
return null; return null;
} }
const canWrite = column.columnDef.meta?.canWrite;
if (!canWrite) {
return <GhostedText>{initialValue}</GhostedText>;
}
return <SingleLineCell initialValue={initialValue as string} handleUpdate={update} />; return <SingleLineCell initialValue={initialValue as string} handleUpdate={update} />;
} }
@@ -197,9 +209,15 @@ function MakeCustomField({ row, column, table }: CellContext<OntimeEntry, unknow
return null; return null;
} }
// fields will not contain the field if there is no value set by the user // entries will not contain the field if there is no value set by the user
// event if there is no initial value, we still render the cell // event if there is no initial value, we still render the cell
const initialValue = event.custom[column.id] ?? ''; const initialValue = event.custom[column.id] ?? '';
const canWrite = column.columnDef.meta?.canWrite;
if (!canWrite) {
return <GhostedText>{initialValue}</GhostedText>;
}
return <MultiLineCell initialValue={initialValue} handleUpdate={update} />; return <MultiLineCell initialValue={initialValue} handleUpdate={update} />;
} }