mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
Extract Variables tab JSX into WiredVariablesTabView component
Proposal #5 from docs/ARCHITECTURE.md, first slice: split one of the three remaining inline tab bodies of WiredCreatorToolsView out into its own file. Same approach the Settings tab has had for a while (see WiredToolsSettingsTabView). What moved - 113 lines of inline JSX (the `{ activeTab === 'variables' && <div> ... </div> }` block) → src/components/wired-tools/WiredVariablesTabView.tsx - The new component is a pure presentation function: 12 typed props, no useState, no useEffect, no event subscriptions. It receives: * state to render: variablesType, variablePickerDefinitions, selectedVariableDefinition, canVariableHighlight, isVariableHighlightActive, variableManageCanOpen, selectedVariableProperties, selectedVariableTextValues * actions to call: onVariablesTypeChange, onPickVariable, onToggleVariableHighlight, onOpenManagePanel - The parent supplies all of them inline at the call site. The manage-panel open sequence (request fresh user vars + reset page + clear selection + show modal) is closed over into a single onOpenManagePanel callback, so the sub-component doesn't need to know about its three internal setters. Impact - WiredCreatorToolsView.tsx: 3901 → 3809 lines (−92 net). The file is still large, but one of the three big inline blocks is gone. Monitor (~176 lines) and Inspection (~138 lines) remain inline as follow-up PRs. - The React Compiler now has a smaller file boundary for the Variables panel; once the other two blocks come out the parent module should stop being skipped for memoization. Conscious non-goals - No state was moved. The shared state (selectedVariableKeys, isVariableHighlightActive, variableManagePage, etc.) still lives in the parent's useState. Hoisting them to a Zustand slice would be a separate PR — premature here. - No behavior change. Same renders, same handlers, same DOM. Verification - yarn eslint on the two touched files: 34 problems baseline, 34 problems after the split (identical: same FC<{}>, same pre-existing set-state-in-effect, same react-compiler skip warnings on the parent module). - yarn test: 49/49 passing. - yarn tsc on the two files: clean.
This commit is contained in:
@@ -10,6 +10,7 @@ import { DIRECTION_NAMES, EDITABLE_FURNI_VARIABLES, EDITABLE_USER_VARIABLES, INS
|
||||
import { createEmptyMonitorSnapshot, formatMonitorHistoryOccurrence, formatMonitorLatestOccurrence, formatMonitorSource, formatVariableTimestamp, getHotelDateTimeParts, getHotelTimeFormatter, normalizeMonitorReason } from './WiredCreatorTools.helpers';
|
||||
import { HotelDateTimeParts, InspectionElementButton, InspectionElementType, InspectionFurniLiveState, InspectionFurniSelection, InspectionUserLiveState, InspectionUserSelection, InspectionUserTeamData, InspectionVariable, ManagedHolderVariableEntry, MonitorLog, MonitorLogDetails, MonitorSnapshot, MonitorStat, ParsedWallLocation, TeamEffectData, VariableDefinition, VariableHighlightOverlay, VariableHighlightTarget, VariableManageEntry, VariableTextValue, VariablesElementButton, VariablesElementType, WiredToolsTab } from './WiredCreatorTools.types';
|
||||
import { WiredToolsSettingsTabView } from './WiredToolsSettingsTabView';
|
||||
import { WiredVariablesTabView } from './WiredVariablesTabView';
|
||||
|
||||
export const WiredCreatorToolsView: FC<{}> = () =>
|
||||
{
|
||||
@@ -3387,118 +3388,26 @@ export const WiredCreatorToolsView: FC<{}> = () =>
|
||||
</div>
|
||||
</div> }
|
||||
{ (activeTab === 'variables') &&
|
||||
<div className="p-3 min-h-[360px] flex gap-4">
|
||||
<div className="w-[205px] shrink-0 flex flex-col gap-3">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>Variable type:</Text>
|
||||
<div className="flex gap-1">
|
||||
{ VARIABLES_ELEMENTS.map(element => (
|
||||
<button
|
||||
key={ element.key }
|
||||
type="button"
|
||||
className={ `w-[42px] h-[38px] rounded border flex items-center justify-center shadow-[inset_0_1px_0_rgba(255,255,255,.7)] ${ element.disabled ? 'border-[#b7b7b7] bg-[#e7e3da] opacity-60 cursor-not-allowed' : ((variablesType === element.key) ? 'border-[#222] bg-[#d9d6cf]' : 'border-[#7f7f7f] bg-[#ece9e1]') }` }
|
||||
disabled={ element.disabled }
|
||||
onClick={ () => !element.disabled && setVariablesType(element.key) }
|
||||
title={ element.label }>
|
||||
<img alt={ element.label } className="w-auto h-auto max-w-[22px] max-h-[22px] object-contain" src={ element.icon } />
|
||||
</button>
|
||||
)) }
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 min-h-0 grow">
|
||||
<Text bold>Variable picker:</Text>
|
||||
<div className="grow rounded border border-[#bdb8ab] bg-white overflow-hidden">
|
||||
<div className="max-h-[408px] overflow-y-auto">
|
||||
<table className="w-full text-[12px]">
|
||||
<tbody>
|
||||
{ variablePickerDefinitions.map((variable, index) => (
|
||||
<tr
|
||||
key={ variable.key }
|
||||
className={ `cursor-pointer ${ (selectedVariableDefinition?.key === variable.key) ? 'bg-[#d7dfea]' : ((index % 2 === 0) ? 'bg-white' : 'bg-[#f3f3f3]') } hover:bg-[#e8eefc]` }
|
||||
onClick={ () => setSelectedVariableKeys(prev => ({ ...prev, [variablesType]: variable.key })) }>
|
||||
<td className="px-3 py-1 text-[#444]">{ variable.key }</td>
|
||||
</tr>
|
||||
)) }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
disabled={ !canVariableHighlight }
|
||||
variant="secondary"
|
||||
onClick={ () => setIsVariableHighlightActive(value => !value) }>
|
||||
{ isVariableHighlightActive ? 'Undo' : 'Highlight' }
|
||||
</Button>
|
||||
<Button
|
||||
disabled={ !variableManageCanOpen }
|
||||
variant="secondary"
|
||||
onClick={ () =>
|
||||
{
|
||||
requestUserVariables();
|
||||
setVariableManagePage(1);
|
||||
setSelectedManagedVariableEntry(null);
|
||||
setIsVariableManageOpen(true);
|
||||
} }>
|
||||
Manage
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-w-0 grow flex flex-col gap-3">
|
||||
{ (variablesType === 'context') &&
|
||||
<div className="rounded border border-[#c8b98f] bg-[#fff7df] px-3 py-2 text-[12px] text-[#6a5d33]">
|
||||
Context variables live only during the current wired execution. This tab shows their definitions, text mappings and execution-scoped capabilities, but not live values from a running stack.
|
||||
</div> }
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>Properties:</Text>
|
||||
<div className="rounded border border-[#bdb8ab] bg-white overflow-hidden">
|
||||
<div className="grid grid-cols-[1fr_120px] border-b border-[#d8d4c8] bg-[#f5f2ea] px-3 py-2 text-[12px] font-bold text-[#333]">
|
||||
<span>Property</span>
|
||||
<span>Value</span>
|
||||
</div>
|
||||
<div className="max-h-[210px] overflow-y-auto">
|
||||
<table className="w-full text-[12px]">
|
||||
<tbody>
|
||||
{ selectedVariableProperties.map((property, index) => (
|
||||
<tr key={ property.key } className={ (index % 2 === 0) ? 'bg-white' : 'bg-[#f3f3f3]' }>
|
||||
<td className="px-3 py-1 text-[#444]">{ property.key }</td>
|
||||
<td className="px-3 py-1 text-[#222]">{ property.value }</td>
|
||||
</tr>
|
||||
)) }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 min-h-0 grow">
|
||||
<Text bold>Text values:</Text>
|
||||
<div className="grow rounded border border-[#bdb8ab] bg-white overflow-hidden">
|
||||
<div className="grid grid-cols-[120px_1fr] border-b border-[#d8d4c8] bg-[#f5f2ea] px-3 py-2 text-[12px] font-bold text-[#333]">
|
||||
<span>Value</span>
|
||||
<span>Text</span>
|
||||
</div>
|
||||
{ !selectedVariableTextValues.length &&
|
||||
<div className="h-[calc(100%-37px)] flex items-center justify-center text-[#b1aca2] text-[20px]">
|
||||
<Text>Nothing to display</Text>
|
||||
</div> }
|
||||
{ !!selectedVariableTextValues.length &&
|
||||
<div className="max-h-[178px] overflow-y-auto">
|
||||
<table className="w-full text-[12px]">
|
||||
<tbody>
|
||||
{ selectedVariableTextValues.map((entry, index) => (
|
||||
<tr key={ `${ entry.value }-${ index }` } className={ (index % 2 === 0) ? 'bg-white' : 'bg-[#f3f3f3]' }>
|
||||
<td className="px-3 py-1 text-[#444]">{ entry.value }</td>
|
||||
<td className="px-3 py-1 text-[#222]">{ entry.text }</td>
|
||||
</tr>
|
||||
)) }
|
||||
</tbody>
|
||||
</table>
|
||||
</div> }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> }
|
||||
<WiredVariablesTabView
|
||||
variablesType={ variablesType }
|
||||
onVariablesTypeChange={ setVariablesType }
|
||||
variablePickerDefinitions={ variablePickerDefinitions }
|
||||
selectedVariableDefinition={ selectedVariableDefinition }
|
||||
onPickVariable={ key => setSelectedVariableKeys(prev => ({ ...prev, [variablesType]: key })) }
|
||||
canVariableHighlight={ canVariableHighlight }
|
||||
isVariableHighlightActive={ isVariableHighlightActive }
|
||||
onToggleVariableHighlight={ () => setIsVariableHighlightActive(value => !value) }
|
||||
variableManageCanOpen={ variableManageCanOpen }
|
||||
onOpenManagePanel={ () =>
|
||||
{
|
||||
requestUserVariables();
|
||||
setVariableManagePage(1);
|
||||
setSelectedManagedVariableEntry(null);
|
||||
setIsVariableManageOpen(true);
|
||||
} }
|
||||
selectedVariableProperties={ selectedVariableProperties }
|
||||
selectedVariableTextValues={ selectedVariableTextValues }
|
||||
/> }
|
||||
{ (activeTab === 'settings') && <WiredToolsSettingsTabView /> }
|
||||
{ (activeTab !== 'monitor') &&
|
||||
(activeTab !== 'inspection') &&
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
import { FC } from 'react';
|
||||
import { Button, Text } from '../../common';
|
||||
import { VARIABLES_ELEMENTS } from './WiredCreatorTools.constants';
|
||||
import { VariableDefinition, VariablesElementType, VariableTextValue } from './WiredCreatorTools.types';
|
||||
|
||||
export interface WiredVariablesTabViewProps
|
||||
{
|
||||
variablesType: VariablesElementType;
|
||||
onVariablesTypeChange: (next: VariablesElementType) => void;
|
||||
variablePickerDefinitions: VariableDefinition[];
|
||||
selectedVariableDefinition: VariableDefinition | null;
|
||||
onPickVariable: (key: string) => void;
|
||||
canVariableHighlight: boolean;
|
||||
isVariableHighlightActive: boolean;
|
||||
onToggleVariableHighlight: () => void;
|
||||
variableManageCanOpen: boolean;
|
||||
onOpenManagePanel: () => void;
|
||||
selectedVariableProperties: { key: string; value: string; }[];
|
||||
selectedVariableTextValues: VariableTextValue[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The "Variables" tab body of WiredCreatorToolsView. Extracted so the
|
||||
* parent module no longer carries 110 lines of inline JSX. Pure
|
||||
* presentation: every piece of state and every callback is supplied as
|
||||
* a prop, so this component is trivially memoizable and (eventually)
|
||||
* testable in isolation.
|
||||
*/
|
||||
export const WiredVariablesTabView: FC<WiredVariablesTabViewProps> = ({
|
||||
variablesType,
|
||||
onVariablesTypeChange,
|
||||
variablePickerDefinitions,
|
||||
selectedVariableDefinition,
|
||||
onPickVariable,
|
||||
canVariableHighlight,
|
||||
isVariableHighlightActive,
|
||||
onToggleVariableHighlight,
|
||||
variableManageCanOpen,
|
||||
onOpenManagePanel,
|
||||
selectedVariableProperties,
|
||||
selectedVariableTextValues
|
||||
}) =>
|
||||
(
|
||||
<div className="p-3 min-h-[360px] flex gap-4">
|
||||
<div className="w-[205px] shrink-0 flex flex-col gap-3">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>Variable type:</Text>
|
||||
<div className="flex gap-1">
|
||||
{ VARIABLES_ELEMENTS.map(element => (
|
||||
<button
|
||||
key={ element.key }
|
||||
type="button"
|
||||
className={ `w-[42px] h-[38px] rounded border flex items-center justify-center shadow-[inset_0_1px_0_rgba(255,255,255,.7)] ${ element.disabled ? 'border-[#b7b7b7] bg-[#e7e3da] opacity-60 cursor-not-allowed' : ((variablesType === element.key) ? 'border-[#222] bg-[#d9d6cf]' : 'border-[#7f7f7f] bg-[#ece9e1]') }` }
|
||||
disabled={ element.disabled }
|
||||
onClick={ () => !element.disabled && onVariablesTypeChange(element.key) }
|
||||
title={ element.label }>
|
||||
<img alt={ element.label } className="w-auto h-auto max-w-[22px] max-h-[22px] object-contain" src={ element.icon } />
|
||||
</button>
|
||||
)) }
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 min-h-0 grow">
|
||||
<Text bold>Variable picker:</Text>
|
||||
<div className="grow rounded border border-[#bdb8ab] bg-white overflow-hidden">
|
||||
<div className="max-h-[408px] overflow-y-auto">
|
||||
<table className="w-full text-[12px]">
|
||||
<tbody>
|
||||
{ variablePickerDefinitions.map((variable, index) => (
|
||||
<tr
|
||||
key={ variable.key }
|
||||
className={ `cursor-pointer ${ (selectedVariableDefinition?.key === variable.key) ? 'bg-[#d7dfea]' : ((index % 2 === 0) ? 'bg-white' : 'bg-[#f3f3f3]') } hover:bg-[#e8eefc]` }
|
||||
onClick={ () => onPickVariable(variable.key) }>
|
||||
<td className="px-3 py-1 text-[#444]">{ variable.key }</td>
|
||||
</tr>
|
||||
)) }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
disabled={ !canVariableHighlight }
|
||||
variant="secondary"
|
||||
onClick={ onToggleVariableHighlight }>
|
||||
{ isVariableHighlightActive ? 'Undo' : 'Highlight' }
|
||||
</Button>
|
||||
<Button
|
||||
disabled={ !variableManageCanOpen }
|
||||
variant="secondary"
|
||||
onClick={ onOpenManagePanel }>
|
||||
Manage
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-w-0 grow flex flex-col gap-3">
|
||||
{ (variablesType === 'context') &&
|
||||
<div className="rounded border border-[#c8b98f] bg-[#fff7df] px-3 py-2 text-[12px] text-[#6a5d33]">
|
||||
Context variables live only during the current wired execution. This tab shows their definitions, text mappings and execution-scoped capabilities, but not live values from a running stack.
|
||||
</div> }
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>Properties:</Text>
|
||||
<div className="rounded border border-[#bdb8ab] bg-white overflow-hidden">
|
||||
<div className="grid grid-cols-[1fr_120px] border-b border-[#d8d4c8] bg-[#f5f2ea] px-3 py-2 text-[12px] font-bold text-[#333]">
|
||||
<span>Property</span>
|
||||
<span>Value</span>
|
||||
</div>
|
||||
<div className="max-h-[210px] overflow-y-auto">
|
||||
<table className="w-full text-[12px]">
|
||||
<tbody>
|
||||
{ selectedVariableProperties.map((property, index) => (
|
||||
<tr key={ property.key } className={ (index % 2 === 0) ? 'bg-white' : 'bg-[#f3f3f3]' }>
|
||||
<td className="px-3 py-1 text-[#444]">{ property.key }</td>
|
||||
<td className="px-3 py-1 text-[#222]">{ property.value }</td>
|
||||
</tr>
|
||||
)) }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 min-h-0 grow">
|
||||
<Text bold>Text values:</Text>
|
||||
<div className="grow rounded border border-[#bdb8ab] bg-white overflow-hidden">
|
||||
<div className="grid grid-cols-[120px_1fr] border-b border-[#d8d4c8] bg-[#f5f2ea] px-3 py-2 text-[12px] font-bold text-[#333]">
|
||||
<span>Value</span>
|
||||
<span>Text</span>
|
||||
</div>
|
||||
{ !selectedVariableTextValues.length &&
|
||||
<div className="h-[calc(100%-37px)] flex items-center justify-center text-[#b1aca2] text-[20px]">
|
||||
<Text>Nothing to display</Text>
|
||||
</div> }
|
||||
{ !!selectedVariableTextValues.length &&
|
||||
<div className="max-h-[178px] overflow-y-auto">
|
||||
<table className="w-full text-[12px]">
|
||||
<tbody>
|
||||
{ selectedVariableTextValues.map((entry, index) => (
|
||||
<tr key={ `${ entry.value }-${ index }` } className={ (index % 2 === 0) ? 'bg-white' : 'bg-[#f3f3f3]' }>
|
||||
<td className="px-3 py-1 text-[#444]">{ entry.value }</td>
|
||||
<td className="px-3 py-1 text-[#222]">{ entry.text }</td>
|
||||
</tr>
|
||||
)) }
|
||||
</tbody>
|
||||
</table>
|
||||
</div> }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
Reference in New Issue
Block a user