Skip to content

Commit f4a2753

Browse files
fix number inputs (NumericInput), widen dialogs, reset wizard on open
1 parent 2927ac4 commit f4a2753

10 files changed

Lines changed: 58 additions & 63 deletions

File tree

deno.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@molstar/molstar-components",
3-
"version": "0.6.0-experimental.17",
3+
"version": "0.6.0-experimental.18",
44
"license": "MIT",
55
"exports": {
66
".": "./src/mod.ts",

src/state-builder-ui/SetupWizard.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useState } from 'react';
3+
import { useState, useEffect } from 'react';
44
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from './base/dialog.tsx';
55
import { Button } from './base/button.tsx';
66
import { Input } from './base/input.tsx';
@@ -18,6 +18,7 @@ import {
1818
assignMissingRefs,
1919
} from '../state-builder/index.ts';
2020
import { SelectorHelperContent, type SelectorTab } from './helpers/SelectorHelperContent.tsx';
21+
import { NumericInput } from './components/NumericInput.tsx';
2122

2223
interface SetupWizardProps {
2324
open: boolean;
@@ -96,6 +97,16 @@ export function SetupWizard({ open, onOpenChange, onComplete }: SetupWizardProps
9697
const [components, setComponents] = useState<ComponentConfig[]>([newComponent()]);
9798
const [activeCompTab, setActiveCompTab] = useState('0');
9899

100+
useEffect(() => {
101+
if (!open) return;
102+
setStep(1);
103+
setUrl(''); setFormat('bcif');
104+
setStructureType('model'); setAssemblyId('');
105+
setRadius(5); setIjkMin([0, 0, 0]); setIjkMax([1, 1, 1]);
106+
setModelIndex(undefined); setBlockIndex(undefined); setBlockHeader(undefined); setCoordinatesRef(undefined);
107+
setComponents([newComponent()]); setActiveCompTab('0');
108+
}, [open]);
109+
99110
const formats = getActiveValues(PARSE_FORMATS);
100111
const reprTypes = getActiveValues(REPRESENTATION_TYPES);
101112
const presetSelectors = getActiveValues(COMPONENT_SELECTORS);
@@ -164,7 +175,7 @@ export function SetupWizard({ open, onOpenChange, onComplete }: SetupWizardProps
164175

165176
return (
166177
<Dialog open={open} onOpenChange={onOpenChange}>
167-
<DialogContent className='sm:max-w-[520px] gap-0 p-0 overflow-hidden'>
178+
<DialogContent className='sm:max-w-[860px] gap-0 p-0 overflow-hidden'>
168179
<DialogHeader className='pb-0'>
169180
<StepIndicator currentStep={step} />
170181
<DialogTitle className='px-5 pt-3 pb-0 text-base'>
@@ -216,7 +227,7 @@ export function SetupWizard({ open, onOpenChange, onComplete }: SetupWizardProps
216227
{structureType === 'symmetry_mates' && (
217228
<div className='flex flex-col gap-1 w-28'>
218229
<Label className='text-xs'>Radius (Å)</Label>
219-
<Input className='h-7 text-xs font-mono' type='number' min='0' step='1' placeholder='5' value={radius} onChange={(e) => setRadius(parseFloat(e.target.value) || 5)} />
230+
<NumericInput className='h-7 text-xs font-mono' placeholder='5' value={radius} onChange={(v) => setRadius(v ?? radius)} />
220231
</div>
221232
)}
222233
{structureType === 'symmetry' && (
@@ -229,16 +240,14 @@ export function SetupWizard({ open, onOpenChange, onComplete }: SetupWizardProps
229240
<Label className='text-xs'>IJK {bound}</Label>
230241
<div className='flex gap-1'>
231242
{(['i', 'j', 'k'] as const).map((axis, idx) => (
232-
<Input
243+
<NumericInput
233244
key={axis}
234245
className='h-7 text-xs font-mono w-14'
235-
type='number'
236-
step='1'
237246
placeholder={axis}
238247
value={val[idx]}
239-
onChange={(e) => {
248+
onChange={(v) => {
240249
const next = [...val] as [number, number, number];
241-
next[idx] = parseInt(e.target.value) || 0;
250+
next[idx] = v ?? next[idx];
242251
set(next);
243252
}}
244253
/>

src/state-builder-ui/components/fields/ComponentFields.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function SelectorDialog({
6464
<span className='truncate'>{preview}</span>
6565
</Button>
6666

67-
<DialogContent className='sm:max-w-lg'>
67+
<DialogContent className='sm:max-w-2xl'>
6868
<DialogHeader>
6969
<DialogTitle>Build Component Selector</DialogTitle>
7070
</DialogHeader>

src/state-builder-ui/components/fields/StructureFields.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { Input } from '../../ui/input.tsx';
2-
import { Label } from '../../ui/label.tsx';
3-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../ui/select.tsx';
1+
import { Input } from '../../base/input.tsx';
2+
import { Label } from '../../base/label.tsx';
3+
import { NumericInput } from '../NumericInput.tsx';
4+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../base/select.tsx';
45
import { STRUCTURE_TYPES, getActiveValues } from '../../../state-builder/index.ts';
56

67
interface StructureFieldsProps {
@@ -27,16 +28,14 @@ function IjkInput({
2728
<Label className='text-xs'>{label}</Label>
2829
<div className='flex gap-1 mt-1'>
2930
{(['i', 'j', 'k'] as const).map((axis, idx) => (
30-
<Input
31+
<NumericInput
3132
key={axis}
3233
className='h-8 text-xs font-mono w-14'
33-
type='number'
34-
step='1'
3534
placeholder={axis}
3635
value={value[idx]}
37-
onChange={(e) => {
36+
onChange={(v) => {
3837
const next = [...value] as [number, number, number];
39-
next[idx] = parseInt(e.target.value) || 0;
38+
next[idx] = v ?? next[idx];
4039
onChange(next);
4140
}}
4241
/>

src/state-builder-ui/helpers/InterpolateHelper.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useState } from 'react';
44
import type { ReactNode } from 'react';
55
import { Input } from '../base/input.tsx';
6+
import { NumericInput } from '../components/NumericInput.tsx';
67
import { Label } from '../base/label.tsx';
78
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../base/select.tsx';
89
import type { UINode } from '../../state-builder/index.ts';
@@ -113,14 +114,11 @@ export function InterpolateHelper({ node, onUpdate, open, onOpenChange, trigger,
113114
</div>
114115
<div className='flex flex-col gap-1 w-36'>
115116
<Label className='text-xs'>Duration (ms)</Label>
116-
<Input
117+
<NumericInput
117118
className='h-7 text-xs font-mono'
118-
type='number'
119-
min='0'
120-
step='100'
121119
placeholder='1000'
122120
value={durationMs}
123-
onChange={(e) => setDurationMs(parseInt(e.target.value) || 0)}
121+
onChange={(v) => setDurationMs(v ?? durationMs)}
124122
/>
125123
</div>
126124
</div>

src/state-builder-ui/helpers/StructureHelper.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Input } from '../base/input.tsx';
66
import { Label } from '../base/label.tsx';
77
import type { UINode } from '../../state-builder/index.ts';
88
import { NodeHelperBase } from './NodeHelperBase.tsx';
9+
import { NumericInput } from '../components/NumericInput.tsx';
910

1011
interface StructureHelperProps {
1112
node: UINode;
@@ -27,16 +28,14 @@ function IjkInput({ label, value, onChange }: { label: string; value: [number, n
2728
<Label className='text-xs'>{label}</Label>
2829
<div className='flex gap-1'>
2930
{(['i', 'j', 'k'] as const).map((axis, idx) => (
30-
<Input
31+
<NumericInput
3132
key={axis}
3233
className='h-7 text-xs font-mono w-14'
33-
type='number'
34-
step='1'
3534
placeholder={axis}
3635
value={value[idx]}
37-
onChange={(e) => {
36+
onChange={(v) => {
3837
const next = [...value] as [number, number, number];
39-
next[idx] = parseInt(e.target.value) || 0;
38+
next[idx] = v ?? next[idx];
4039
onChange(next);
4140
}}
4241
/>
@@ -181,7 +180,7 @@ export function StructureHelper({ node, onUpdate, open, onOpenChange, trigger, o
181180
<div className='flex flex-col gap-3'>
182181
<div className='flex flex-col gap-1 w-28'>
183182
<Label className='text-xs'>Radius (Å)</Label>
184-
<Input className='h-7 text-xs font-mono' type='number' min='0' step='1' placeholder='5' value={radius} onChange={(e) => setRadius(parseFloat(e.target.value) || 5)} />
183+
<NumericInput className='h-7 text-xs font-mono' placeholder='5' value={radius} onChange={(v) => setRadius(v ?? radius)} />
185184
</div>
186185
<SharedFields {...sharedProps} />
187186
</div>

src/state-builder-ui/helpers/primitive-helper/BoxFields.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { Input } from '../../base/input.tsx';
44
import { Label } from '../../base/label.tsx';
5+
import { NumericInput } from '../../components/NumericInput.tsx';
56
import { PositionEditor } from './PositionEditor.tsx';
67
import { positionFromParam, positionToParam } from './types.ts';
78
import type { PrimitiveKindFieldsProps } from './types.ts';
@@ -15,9 +16,9 @@ export function BoxFields({ params, onUpdate }: PrimitiveKindFieldsProps) {
1516
return [1, 1, 1];
1617
};
1718

18-
const handleExtent = (axis: 0 | 1 | 2, str: string) => {
19+
const handleExtent = (axis: 0 | 1 | 2, v: number | undefined) => {
1920
const next = [...getVec3('extent')] as [number, number, number];
20-
next[axis] = parseFloat(str) || 0;
21+
next[axis] = v ?? next[axis];
2122
onUpdate({ ...params, extent: next });
2223
};
2324

@@ -35,13 +36,10 @@ export function BoxFields({ params, onUpdate }: PrimitiveKindFieldsProps) {
3536
{(['X', 'Y', 'Z'] as const).map((ax, i) => (
3637
<div key={ax}>
3738
<Label className='text-xs text-muted-foreground'>{ax}</Label>
38-
<Input
39+
<NumericInput
3940
className='h-8 text-sm font-mono'
40-
type='number'
41-
step='0.1'
42-
min='0'
4341
value={getVec3('extent')[i]}
44-
onChange={(e) => handleExtent(i as 0 | 1 | 2, e.target.value)}
42+
onChange={(v) => handleExtent(i as 0 | 1 | 2, v)}
4543
/>
4644
</div>
4745
))}

src/state-builder-ui/helpers/primitive-helper/EllipseFields.tsx

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { Input } from '../../base/input.tsx';
44
import { Label } from '../../base/label.tsx';
5+
import { NumericInput } from '../../components/NumericInput.tsx';
56
import { PositionEditor } from './PositionEditor.tsx';
67
import { positionFromParam, positionToParam } from './types.ts';
78
import type { PrimitiveKindFieldsProps } from './types.ts';
@@ -15,9 +16,9 @@ export function EllipseFields({ params, onUpdate }: PrimitiveKindFieldsProps) {
1516
return [1, 0, 0];
1617
};
1718

18-
const handleVec3 = (key: string, axis: 0 | 1 | 2, str: string) => {
19+
const handleVec3 = (key: string, axis: 0 | 1 | 2, v: number | undefined) => {
1920
const next = [...getVec3(key)] as [number, number, number];
20-
next[axis] = parseFloat(str) || 0;
21+
next[axis] = v ?? next[axis];
2122
onUpdate({ ...params, [key]: next });
2223
};
2324

@@ -35,12 +36,10 @@ export function EllipseFields({ params, onUpdate }: PrimitiveKindFieldsProps) {
3536
{(['X', 'Y', 'Z'] as const).map((ax, i) => (
3637
<div key={ax}>
3738
<Label className='text-xs text-muted-foreground'>{ax}</Label>
38-
<Input
39+
<NumericInput
3940
className='h-8 text-sm font-mono'
40-
type='number'
41-
step='0.1'
4241
value={getVec3('major_axis')[i]}
43-
onChange={(e) => handleVec3('major_axis', i as 0 | 1 | 2, e.target.value)}
42+
onChange={(v) => handleVec3('major_axis', i as 0 | 1 | 2, v)}
4443
/>
4544
</div>
4645
))}
@@ -53,12 +52,10 @@ export function EllipseFields({ params, onUpdate }: PrimitiveKindFieldsProps) {
5352
{(['X', 'Y', 'Z'] as const).map((ax, i) => (
5453
<div key={ax}>
5554
<Label className='text-xs text-muted-foreground'>{ax}</Label>
56-
<Input
55+
<NumericInput
5756
className='h-8 text-sm font-mono'
58-
type='number'
59-
step='0.1'
6057
value={getVec3('minor_axis')[i]}
61-
onChange={(e) => handleVec3('minor_axis', i as 0 | 1 | 2, e.target.value)}
58+
onChange={(v) => handleVec3('minor_axis', i as 0 | 1 | 2, v)}
6259
/>
6360
</div>
6461
))}

src/state-builder-ui/helpers/primitive-helper/EllipsoidFields.tsx

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { Input } from '../../base/input.tsx';
44
import { Label } from '../../base/label.tsx';
5+
import { NumericInput } from '../../components/NumericInput.tsx';
56
import { PositionEditor } from './PositionEditor.tsx';
67
import { positionFromParam, positionToParam, defaultPositionState } from './types.ts';
78
import type { PrimitiveKindFieldsProps } from './types.ts';
@@ -15,9 +16,9 @@ export function EllipsoidFields({ params, onUpdate }: PrimitiveKindFieldsProps)
1516
return [0, 0, 0];
1617
};
1718

18-
const handleVec3 = (key: string, axis: 0 | 1 | 2, str: string) => {
19+
const handleVec3 = (key: string, axis: 0 | 1 | 2, v: number | undefined) => {
1920
const next = [...getVec3(key)] as [number, number, number];
20-
next[axis] = parseFloat(str) || 0;
21+
next[axis] = v ?? next[axis];
2122
onUpdate({ ...params, [key]: next });
2223
};
2324

@@ -35,12 +36,10 @@ export function EllipsoidFields({ params, onUpdate }: PrimitiveKindFieldsProps)
3536
{(['X', 'Y', 'Z'] as const).map((ax, i) => (
3637
<div key={ax}>
3738
<Label className='text-xs text-muted-foreground'>{ax}</Label>
38-
<Input
39+
<NumericInput
3940
className='h-8 text-sm font-mono'
40-
type='number'
41-
step='0.1'
4241
value={getVec3('major_axis')[i]}
43-
onChange={(e) => handleVec3('major_axis', i as 0 | 1 | 2, e.target.value)}
42+
onChange={(v) => handleVec3('major_axis', i as 0 | 1 | 2, v)}
4443
/>
4544
</div>
4645
))}
@@ -53,12 +52,10 @@ export function EllipsoidFields({ params, onUpdate }: PrimitiveKindFieldsProps)
5352
{(['X', 'Y', 'Z'] as const).map((ax, i) => (
5453
<div key={ax}>
5554
<Label className='text-xs text-muted-foreground'>{ax}</Label>
56-
<Input
55+
<NumericInput
5756
className='h-8 text-sm font-mono'
58-
type='number'
59-
step='0.1'
6057
value={getVec3('minor_axis')[i]}
61-
onChange={(e) => handleVec3('minor_axis', i as 0 | 1 | 2, e.target.value)}
58+
onChange={(v) => handleVec3('minor_axis', i as 0 | 1 | 2, v)}
6259
/>
6360
</div>
6461
))}

src/state-builder-ui/helpers/primitive-helper/PositionEditor.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import { useEffect, useRef, useState } from 'react';
44
import { Button } from '../../base/button.tsx';
5-
import { Input } from '../../base/input.tsx';
65
import { Label } from '../../base/label.tsx';
6+
import { NumericInput } from '../../components/NumericInput.tsx';
77
import type { PositionEditorState } from './types.ts';
88
import { positionToParam, tryParseExpressionJson } from './types.ts';
99
import { SelectorHelperContent } from '../SelectorHelperContent.tsx';
@@ -52,8 +52,8 @@ export function PositionEditor({ label, state, onChange }: PositionEditorProps)
5252
// before the user has had a chance to interact.
5353
};
5454

55-
const handleAxis = (axis: 'x' | 'y' | 'z', str: string) => {
56-
emitChange({ ...state, mode: 'vec3', [axis]: parseFloat(str) || 0 });
55+
const handleAxis = (axis: 'x' | 'y' | 'z', v: number | undefined) => {
56+
emitChange({ ...state, mode: 'vec3', [axis]: v ?? state[axis] });
5757
};
5858

5959
return (
@@ -124,12 +124,10 @@ export function PositionEditor({ label, state, onChange }: PositionEditorProps)
124124
{(['x', 'y', 'z'] as const).map((axis) => (
125125
<div key={axis}>
126126
<Label className='text-xs text-muted-foreground uppercase'>{axis}</Label>
127-
<Input
127+
<NumericInput
128128
className='h-8 text-sm font-mono'
129-
type='number'
130-
step='0.1'
131129
value={state[axis]}
132-
onChange={(e) => handleAxis(axis, e.target.value)}
130+
onChange={(v) => handleAxis(axis, v)}
133131
title={`${label} ${axis.toUpperCase()}`}
134132
/>
135133
</div>

0 commit comments

Comments
 (0)