WiredVariableHolderPanelView.tsx 11.4 Ko
Télécharger le fichier
import { KeyboardEvent, ReactNode } from 'react';
import { Button, DraggableWindowPosition, OctaneCardContentView, OctaneCardHeaderView, OctaneCardView, Text } from '../../common';
import { Select } from '../../common/Select';
import { NumberInput } from '../../common/NumberInput';
import { BaseButton } from '../../common/BaseButton';
import { NativeTextTree } from '../../common/native-text/NativeTextTree';
export interface WiredHolderPanelEntry {
id: string;
name: string;
availability: string;
hasValue: boolean;
value: number | null;
isReadOnly?: boolean;
}
export interface WiredHolderPanelGiveOption {
id: string;
name: string;
hasValue: boolean;
}
export interface WiredVariableHolderPanelViewProps {
title: string;
warningText: string;
onRefresh: () => void;
onClose: () => void;
preview: ReactNode;
infoLines: string[];
variablesTitle: string;
entries: WiredHolderPanelEntry[];
selectedId: string | null;
onSelect: (id: string) => void;
canEdit: boolean;
editingId: string | null;
editingValue: string;
onBeginEdit: (entry: WiredHolderPanelEntry) => void;
onEditingValueChange: (value: string) => void;
onEditBlur: () => void;
onEditKeyDown: (event: KeyboardEvent<HTMLInputElement>) => void;
isGiveOpen: boolean;
onToggleGive: () => void;
giveOptions: WiredHolderPanelGiveOption[];
giveSelectedId: string;
onGiveSelect: (id: string) => void;
giveValue: string;
onGiveValueChange: (value: string) => void;
canGive: boolean;
onGive: () => void;
canRemove: boolean;
onRemove: () => void;
status?: ReactNode;
extraActions?: ReactNode;
uniqueKey?: string;
offsetLeft?: number;
offsetTop?: number;
}
/**
* The holder detail window of the creator tools: who the holder is, the variables they hold with
* inline value editing, and remove / give. Pure view; the caller owns the data and the writes.
*/
export const WiredVariableHolderPanelView = (props: WiredVariableHolderPanelViewProps) => {
const {
title,
warningText,
onRefresh,
onClose,
preview,
infoLines,
variablesTitle,
entries,
selectedId,
onSelect,
canEdit,
editingId,
editingValue,
onBeginEdit,
onEditingValueChange,
onEditBlur,
onEditKeyDown,
isGiveOpen,
onToggleGive,
giveOptions,
giveSelectedId,
onGiveSelect,
giveValue,
onGiveValueChange,
canGive,
onGive,
canRemove,
onRemove,
status = null,
extraActions = null,
uniqueKey = 'wired-variable-management-entry',
offsetLeft = 890,
offsetTop = 110
} = props;
const selectedGiveOption = giveOptions.find((option) => option.id === giveSelectedId) ?? null;
return (
<NativeTextTree>
<OctaneCardView
className="octane-wired-menu min-w-[430px] max-w-[430px] max-h-[620px]"
theme="primary-slim"
uniqueKey={uniqueKey}
windowPosition={DraggableWindowPosition.TOP_LEFT}
offsetLeft={offsetLeft}
offsetTop={offsetTop}
>
<OctaneCardHeaderView headerText={title} onCloseClick={onClose} />
<OctaneCardContentView className="text-black bg-[#f4efe3] p-3 flex flex-col gap-3 relative" overflow="hidden">
<div className="rounded border border-[#c8c2b2] bg-white p-3 flex items-center justify-between gap-3">
<div className="grow text-center">
<Text>{warningText}</Text>
</div>
<Button variant="secondary" onClick={onRefresh}>
Refresh
</Button>
</div>
<div className="flex flex-col gap-2">
<Text bold>Holder info:</Text>
<div className="flex gap-4">
<div className="w-[140px] h-[110px] rounded border border-[#d8d2c3] bg-[#dedede] flex items-center justify-center overflow-hidden">
{preview}
</div>
<div className="grow rounded border border-[#d8d2c3] bg-white p-3 flex flex-col gap-1 text-[12px]">
{infoLines.map((line, index) => (
<Text key={`${line}-${index}`}>{line}</Text>
))}
</div>
</div>
</div>
{status}
<div className="flex flex-col gap-2 min-h-0 grow">
<Text bold>{variablesTitle}</Text>
<div className="grow rounded border border-[#d1ccbf] bg-white overflow-y-auto">
<table className="w-full text-[12px]">
<thead className="bg-[#efede5] sticky top-0">
<tr>
<th className="text-left px-2 py-1">Variable</th>
<th className="text-left px-2 py-1">Value</th>
</tr>
</thead>
<tbody>
{!entries.length && (
<tr>
<td className="px-2 py-3 text-center text-[#8b8678]" colSpan={2}>
No variables currently assigned
</td>
</tr>
)}
{entries.map((entry, index) => {
const isSelected = selectedId === entry.id;
const isEditing = editingId === entry.id;
const isEditable = canEdit && !entry.isReadOnly;
return (
<tr
key={entry.id}
className={`${isSelected ? 'bg-[#d7dfea]' : index % 2 === 0 ? 'bg-white' : 'bg-[#f8f6f0]'} cursor-pointer hover:bg-[#e8eefc]`}
onClick={() => onSelect(entry.id)}
>
<td className="px-2 py-1">
<div className="flex flex-col">
<span>{entry.name}</span>
<span className="text-[10px] text-[#8a8476]">{entry.availability}</span>
</div>
</td>
<td className="px-2 py-1">
{!entry.hasValue && <span className="text-[#8a8476]">/</span>}
{!!entry.hasValue && !isEditing && (
<BaseButton
className={`rounded px-1 py-[1px] ${isEditable ? 'text-[#1b57b2] underline underline-offset-2' : 'text-[#222]'}`}
disabled={!isEditable}
type="button"
onClick={(event) => {
event.stopPropagation();
if (!isEditable) return;
onBeginEdit(entry);
}}
>
{entry.value ?? 0}
</BaseButton>
)}
{!!entry.hasValue && isEditing && (
<NumberInput autoFocus className="w-[72px] rounded border border-[#b8b2a4] bg-white px-2 py-[2px] text-[12px]" value={editingValue} onBlur={onEditBlur} onChange={(event) => onEditingValueChange(event.target.value)} onClick={(event) => event.stopPropagation()} onKeyDownCapture={onEditKeyDown} />
)}
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
{extraActions}
<div className="relative flex items-center justify-between gap-3 pt-1">
{isGiveOpen && (
<div className="absolute right-0 bottom-full mb-2 w-[210px] rounded border border-[#8d887a] bg-[#efede5] p-3 shadow-[0_2px_8px_rgba(0,0,0,.25)] z-10 flex flex-col gap-2">
<Text bold>Variable:</Text>
<Select
className="rounded border border-[#b8b2a4] bg-white px-2 py-[3px] text-[12px]"
value={selectedGiveOption?.id ?? ''}
onChange={(event) => onGiveSelect(event.target.value)}
>
{!giveOptions.length && <option value="">No variables available</option>}
{giveOptions.map((option) => (
<option key={option.id} value={option.id}>
{option.name}
</option>
))}
</Select>
<Text bold>Value:</Text>
<NumberInput className="w-[96px] rounded border border-[#b8b2a4] bg-white px-2 py-[3px] text-[12px] disabled:opacity-60" disabled={!selectedGiveOption?.hasValue} value={giveValue} onChange={(event) => onGiveValueChange(event.target.value)} />
<Button disabled={!canGive} variant="secondary" onClick={onGive}>
Create
</Button>
</div>
)}
<Button disabled={!canRemove} variant="secondary" onClick={onRemove}>
Remove variable
</Button>
<Button disabled={!canGive} variant="secondary" onClick={onToggleGive}>
Give variable
</Button>
</div>
</OctaneCardContentView>
</OctaneCardView>
</NativeTextTree>
);
};
