Skip to content

Commit 3396679

Browse files
committed
tmp
1 parent 9d31df2 commit 3396679

File tree

2 files changed

+372
-42
lines changed

2 files changed

+372
-42
lines changed

shell-ui/src/navbar/ChipsInput.tsx

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
import styled from 'styled-components';
2+
import { Modal } from '@scality/core-ui/dist/components/modal/Modal.component';
3+
import { Text } from '@scality/core-ui/dist/components/text/Text.component';
4+
import { Stack } from '@scality/core-ui/dist/spacing';
5+
import { Tooltip } from '@scality/core-ui/dist/components/tooltip/Tooltip.component';
6+
import { InfoMessage } from '@scality/core-ui/dist/components/infomessage/InfoMessage.component';
7+
import { SecondaryText } from '@scality/core-ui/dist/components/text/Text.component';
8+
import { Loader } from '@scality/core-ui/dist/components/loader/Loader.component';
9+
// import type { InputProps } from '@scality/core-ui/src/lib/components/inputv2/inputv2';
10+
// import { } from '@scality/core-ui/dist/next';
11+
import { Button } from '@scality/core-ui/dist/components/buttonv2/Buttonv2.component';
12+
import { Box } from '@scality/core-ui/dist/components/box/Box';
13+
// import { type InputProps } from '@scality/core-ui/dist/next';
14+
// import { useForm } from 'react-hook-form';
15+
// import { UseMutationResult } from 'react-query';
16+
import { useEffect, useRef, useState } from 'react';
17+
18+
import { spacing } from '@scality/core-ui/dist/spacing';
19+
20+
// ─── Styles ──────────────────────────────────────────────────────────────────
21+
22+
const NameText = styled.span<{ $hovered: boolean; $disabled: boolean }>`
23+
display: inline-flex;
24+
align-items: center;
25+
gap: ${spacing.r4};
26+
font-size: 1rem;
27+
font-family: 'Lato';
28+
color: ${(props) => props.theme.textPrimary};
29+
white-space: nowrap;
30+
transition: border-color 0.15s ease;
31+
opacity: ${(props) => (props.$disabled ? 0.6 : 1)};
32+
`;
33+
34+
const ModalDivider = styled.hr`
35+
border: none;
36+
border-top: 1px solid ${(props) => props.theme.backgroundLevel3};
37+
margin: 0;
38+
`;
39+
40+
const InlineLoaderWrapper = styled.span`
41+
display: inline-flex;
42+
align-items: center;
43+
svg {
44+
width: 1.25em;
45+
height: 1.25em;
46+
}
47+
`;
48+
49+
const NameInput = styled.input`
50+
font-size: 1rem;
51+
font-family: 'Lato';
52+
color: ${(props) => props.theme.textPrimary};
53+
background: ${(props) => props.theme.backgroundLevel2};
54+
border: 1px solid ${(props) => props.theme.infoPrimary};
55+
border-radius: ${spacing.r4};
56+
padding: ${spacing.r4} ${spacing.r8};
57+
outline: none;
58+
min-width: 180px;
59+
60+
&:focus {
61+
border-color: ${(props) => props.theme.selectedActive};
62+
box-shadow: 0 0 0 2px ${(props) => props.theme.selectedActive}33;
63+
}
64+
`;
65+
66+
const KeyValueGrid = styled.dl`
67+
display: grid;
68+
grid-template-columns: max-content 1fr;
69+
gap: ${spacing.r8} ${spacing.r16};
70+
margin: 0;
71+
`;
72+
73+
const KeyLabel = styled.dt`
74+
color: ${(props) => props.theme.textSecondary};
75+
margin: 0;
76+
`;
77+
78+
const KeyValue = styled.dd`
79+
color: ${(props) => props.theme.textPrimary};
80+
margin: 0;
81+
`;
82+
83+
// Shared hover wrapper — interaction only, no text style
84+
const HoverWrapper = styled.span<{ $hovered: boolean; $pill?: boolean }>`
85+
display: inline-flex;
86+
align-items: center;
87+
padding: ${spacing.r4} ${spacing.r8};
88+
border-radius: ${(props) => (props.$pill ? spacing.r16 : spacing.r4)};
89+
border: 1px solid ${(props) => (props.$hovered ? props.theme.infoPrimary : 'transparent')};
90+
background: ${(props) =>
91+
props.$pill ? (props.$hovered ? props.theme.highlight : props.theme.backgroundLevel3) : 'transparent'};
92+
cursor: pointer;
93+
white-space: nowrap;
94+
transition: border-color 0.15s ease, background 0.15s ease;
95+
`;
96+
97+
// ─── Editable Deployment Name ─────────────────────────────────────────────────
98+
99+
export function EditableDeploymentName({
100+
name,
101+
isPropagating,
102+
onChange,
103+
}: {
104+
name: string;
105+
isPropagating: boolean;
106+
onChange: (newName: string) => void;
107+
}) {
108+
const [isEditing, setIsEditing] = useState(false);
109+
const [isHovered, setIsHovered] = useState(false);
110+
const [pendingName, setPendingName] = useState(name);
111+
const [modalOpen, setModalOpen] = useState(false);
112+
const inputRef = useRef<HTMLInputElement>(null);
113+
114+
useEffect(() => {
115+
if (isEditing) {
116+
setPendingName(name);
117+
inputRef.current?.focus();
118+
inputRef.current?.select();
119+
}
120+
}, [isEditing, name]);
121+
122+
const handleEditStart = () => {
123+
if (isPropagating) return;
124+
setIsEditing(true);
125+
setIsHovered(false);
126+
};
127+
128+
const trySubmit = () => {
129+
const trimmed = pendingName.trim();
130+
if (trimmed && trimmed !== name) {
131+
setIsEditing(false);
132+
setModalOpen(true);
133+
} else {
134+
setIsEditing(false);
135+
}
136+
};
137+
138+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
139+
if (e.key === 'Enter') trySubmit();
140+
else if (e.key === 'Escape') setIsEditing(false);
141+
};
142+
143+
const handleConfirm = () => {
144+
onChange(pendingName.trim());
145+
setModalOpen(false);
146+
};
147+
148+
const handleModalCancel = () => {
149+
setModalOpen(false);
150+
};
151+
152+
return (
153+
<>
154+
<Box gap={spacing.r4} style={{ alignItems: 'center' }}>
155+
{isEditing ? (
156+
<NameInput
157+
ref={inputRef}
158+
value={pendingName}
159+
onChange={(e) => setPendingName(e.target.value)}
160+
onKeyDown={handleKeyDown}
161+
onBlur={trySubmit}
162+
aria-label="Deployment name"
163+
/>
164+
) : (
165+
<Tooltip
166+
overlay={isPropagating ? 'Cannot edit while propagating' : 'Edit deployment name'}
167+
placement="bottom"
168+
>
169+
<HoverWrapper
170+
$hovered={isHovered && !isPropagating && !modalOpen}
171+
$pill={true}
172+
onMouseEnter={() => setIsHovered(true)}
173+
onMouseLeave={() => setIsHovered(false)}
174+
onClick={handleEditStart}
175+
role={isPropagating ? undefined : 'button'}
176+
tabIndex={isPropagating ? undefined : 0}
177+
onKeyDown={(e) => !isPropagating && e.key === 'Enter' && handleEditStart()}
178+
>
179+
<NameText
180+
$hovered={isHovered && !isPropagating && !modalOpen}
181+
$disabled={isPropagating || modalOpen}
182+
onClick={handleEditStart}
183+
role={isPropagating ? undefined : 'button'}
184+
tabIndex={isPropagating ? undefined : 0}
185+
onKeyDown={(e) => !isPropagating && e.key === 'Enter' && handleEditStart()}
186+
>
187+
{modalOpen ? pendingName.trim() : name}
188+
{isPropagating && (
189+
<InlineLoaderWrapper>
190+
<Loader size="smaller" />
191+
</InlineLoaderWrapper>
192+
)}
193+
</NameText>
194+
</HoverWrapper>
195+
</Tooltip>
196+
)}
197+
</Box>
198+
199+
<Modal
200+
isOpen={modalOpen}
201+
close={handleModalCancel}
202+
title="Rename deployment?"
203+
footer={
204+
<Stack gap="r8" direction="horizontal" style={{ justifyContent: 'flex-end' }}>
205+
<Button variant="outline" label="Cancel" onClick={handleModalCancel} />
206+
<Button variant="primary" label="Rename" onClick={handleConfirm} />
207+
</Stack>
208+
}
209+
>
210+
<Stack direction="vertical" gap="r24" style={{ width: '500px' }}>
211+
<InfoMessage
212+
title="About deployment names"
213+
content="The deployment name is a label for this instance, visible in the UI and potentially shared with external systems. It is auto-generated at installation. Renaming it early helps distinguish this deployment from others when several are running in parallel."
214+
/>
215+
<Text>Are you sure you want to rename this deployment?</Text>
216+
<KeyValueGrid>
217+
<KeyLabel>Current name</KeyLabel>
218+
<KeyValue>{name}</KeyValue>
219+
<KeyLabel>New name</KeyLabel>
220+
<KeyValue>{pendingName.trim()}</KeyValue>
221+
</KeyValueGrid>
222+
<ModalDivider />
223+
<SecondaryText style={{ fontStyle: 'italic' }}>
224+
This change may take a few minutes to propagate across all services.
225+
</SecondaryText>
226+
</Stack>
227+
</Modal>
228+
</>
229+
);
230+
}
231+
232+
// const MonospaceText = styled.span`
233+
// font-family: 'Courier New', Courier, monospace;
234+
// font-size: 0.875rem;
235+
// color: ${(props) => props.theme.textPrimary};
236+
// `;
237+
238+
// const VerticalDivider = styled.span`
239+
// width: 1px;
240+
// height: 1rem;
241+
// background: ${(props) => props.theme.textSecondary};
242+
// opacity: 0.3;
243+
// flex-shrink: 0;
244+
// `;
245+
246+
// function PreviewName({
247+
// secondary = false,
248+
// pill = false,
249+
// monospace = false,
250+
// }: {
251+
// secondary?: boolean;
252+
// pill?: boolean;
253+
// monospace?: boolean;
254+
// }) {
255+
// const [hovered, setHovered] = useState(false);
256+
// return (
257+
// <Tooltip overlay="Edit deployment name" placement="bottom">
258+
// <HoverWrapper
259+
// $hovered={hovered}
260+
// $pill={pill}
261+
// onMouseEnter={() => setHovered(true)}
262+
// onMouseLeave={() => setHovered(false)}
263+
// >
264+
// {monospace ? (
265+
// <MonospaceText>magic-sandbox</MonospaceText>
266+
// ) : secondary ? (
267+
// <SecondaryText>magic-sandbox</SecondaryText>
268+
// ) : (
269+
// <Text>magic-sandbox</Text>
270+
// )}
271+
// </HoverWrapper>
272+
// </Tooltip>
273+
// );
274+
// }

0 commit comments

Comments
 (0)