Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit47aeb17

Browse files
committed
fix state issues + add edit functionality
1 parentc890393 commit47aeb17

File tree

1 file changed

+52
-51
lines changed

1 file changed

+52
-51
lines changed

‎client/packages/lowcoder/src/comps/comps/tagsComp/tagsCompView.tsx‎

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ type TagOption = {
3434
margin?:string;
3535
padding?:string;
3636
width?:string;
37-
icon?:React.ReactNode|string;// ignored at runtime to keep tags clean
37+
icon?:any;
3838
};
3939

4040
constcolors=PresetStatusColorTypes;
@@ -108,8 +108,7 @@ const multiTags = (function () {
108108
display: inline-flex;
109109
align-items: center;
110110
min-width: fit-content;
111-
width:${(props)=>props.$customStyle?.width||"auto"};
112-
max-width: 100%;
111+
113112
background:${(props)=>props.$customStyle?.backgroundColor||props.$style?.background};
114113
color:${(props)=>props.$customStyle?.color||props.$style?.text};
115114
border-radius:${(props)=>props.$customStyle?.borderRadius||props.$style?.borderRadius};
@@ -129,17 +128,38 @@ const multiTags = (function () {
129128
opacity: 0.9;
130129
`;
131130

132-
constEditableSpan=styled.span`
131+
constEditInput=styled.input`
132+
border: none;
133133
outline: none;
134-
white-space: nowrap;
134+
background: transparent;
135+
font-size: inherit;
136+
font-weight: inherit;
137+
color: inherit;
138+
`;
139+
140+
constTagIcon=styled.span`
141+
display: inline-flex;
142+
align-items: center;
143+
margin-right: 4px;
144+
145+
&.icon-right {
146+
margin-right: 0;
147+
margin-left: 4px;
148+
}
149+
`;
150+
151+
constTagContent=styled.span`
152+
display: inline-flex;
153+
align-items: center;
135154
`;
136155

156+
157+
137158
constchildrenMap={
138159
options:TagsCompOptionsControl,// initial tags (PropertyView)
139160
style:styleControl(InputLikeStyle,"style"),
140161
onEvent:ButtonEventHandlerControl,
141162
editable:BoolControl,// editable switch field
142-
allowEdit:BoolCodeControl,// enable runtime CRUD
143163
preventDuplicates:BoolCodeControl,// runtime de-dupe
144164
allowEmptyEdits:BoolCodeControl,// allow blank labels on edit
145165
maxTags:BoolCodeControl,// truthy => 50 (or provide number if your control supports)
@@ -160,27 +180,24 @@ const multiTags = (function () {
160180

161181
// State
162182
const[editingIndex,setEditingIndex]=useState<number|null>(null);
183+
const[editValue,setEditValue]=useState<string>("");
163184
const[draft,setDraft]=useState<string>("");// typing buffer for creating a new tag
164185
constcontainerRef=useRef<HTMLDivElement>(null);
165-
consteditableRef=useRef<HTMLSpanElement>(null);
166-
constinitRef=useRef<boolean>(false);
167186

168187
constpreventDuplicates=!!props.preventDuplicates;
169188
constallowEmptyEdits=!!props.allowEmptyEdits;
170189
constmaxTags=toMax(props.maxTags);
171-
// Seed runtimeOptions from design-time options once
172-
consttoJsonSafe=(opts:TagOption[])=>opts.map(({ icon, ...rest})=>({ ...rest}));
173-
useEffect(()=>{
174-
if(!initRef.current){
175-
dispatch(changeChildAction("runtimeOptions",toJsonSafe(props.options),false));
176-
initRef.current=true;
177-
}
178-
},[dispatch,props.options]);
179-
180-
constdisplayOptions=(propsasany).runtimeOptions?.length
190+
191+
192+
constdisplayOptions=(propsasany).runtimeOptions?.length&&props.editable
181193
?((propsasany).runtimeOptionsasTagOption[])
182194
:props.options;
183195

196+
useEffect(()=>{
197+
// every time the editable prop changes, we need to update the runtimeOptions
198+
dispatch(changeChildAction("runtimeOptions",[...props.options]asTagOption[],false));
199+
},[props.editable]);
200+
184201
// Events helper
185202
constfireEvent=(type:"add"|"edit"|"delete"|"change"|"click",payload:any)=>{
186203
try{if(props.onEvent)(props.onEventasany)(type,payload);}catch{}
@@ -221,33 +238,18 @@ const multiTags = (function () {
221238
width:"",
222239
};
223240
constnext=[...displayOptions,newTag];
224-
dispatch(changeChildAction("runtimeOptions",toJsonSafe(next),false));
241+
dispatch(changeChildAction("runtimeOptions",next,false));
225242
setDraft("");
226243
fireEvent("add",{ label,value:next});
227244
};
228245

229246
conststartEdit=(index:number)=>{
230247
setEditingIndex(index);
231-
// set content when span mounts via effect-less ref trick below
232-
// we'll fill it in render via default textContent
233-
requestAnimationFrame(()=>{
234-
editableRef.current?.focus();
235-
// place caret at end
236-
constrange=document.createRange();
237-
constnode=editableRef.current;
238-
if(node&&node.firstChild){
239-
range.setStart(node.firstChild,node.firstChild.textContent?.length||0);
240-
range.collapse(true);
241-
constsel=window.getSelection();
242-
sel?.removeAllRanges();
243-
sel?.addRange(range);
244-
}
245-
});
248+
setEditValue(displayOptions[index]?.label||"");
246249
};
247250

248251
constconfirmEdit=(index:number)=>{
249-
constraw=editableRef.current?.textContent??"";
250-
constval=normalize(raw);
252+
constval=normalize(editValue);
251253
if(!val&&!allowEmptyEdits){
252254
cancelEdit();
253255
return;
@@ -258,25 +260,27 @@ const multiTags = (function () {
258260
}
259261
constprev=displayOptions[index]?.label??"";
260262
constnext=displayOptions.map((t,i)=>(i===index ?{ ...t,label:val} :t));
261-
dispatch(changeChildAction("runtimeOptions",toJsonSafe(next),false));
263+
dispatch(changeChildAction("runtimeOptions",next,false));
262264
setEditingIndex(null);
265+
setEditValue("");
263266
fireEvent("edit",{from:prev,to:val, index,value:next});
264267
};
265268

266269
constcancelEdit=()=>{
267270
setEditingIndex(null);
271+
setEditValue("");
268272
};
269273

270274
constdeleteTag=(index:number)=>{
271275
constremoved=displayOptions[index]?.label;
272276
constnext=displayOptions.filter((_,i)=>i!==index);
273-
dispatch(changeChildAction("runtimeOptions",toJsonSafe(next),false));
277+
dispatch(changeChildAction("runtimeOptions",next,false));
274278
fireEvent("delete",{ removed, index,value:next});
275279
};
276280

277281
// Container keyboard handling for *adding* without inputs
278282
constonContainerKeyDown:React.KeyboardEventHandler<HTMLDivElement>=(e)=>{
279-
if(!props.allowEdit)return;
283+
if(!props.editable)return;
280284

281285
const{ key, ctrlKey, metaKey, altKey}=e;
282286

@@ -335,34 +339,32 @@ const multiTags = (function () {
335339
{displayOptions.map((tag,index)=>{
336340
consttagColor=getTagColor(tag.label,displayOptions);
337341
consttagStyle=getTagStyle(tag.label,displayOptions,props.style);
338-
constisEditing=props.allowEdit&&editingIndex===index;
342+
constisEditing=props.editable&&editingIndex===index;
339343

340344
return(
341345
<StyledTag
342346
key={`tag-${index}`}
343347
$style={props.style}
344348
$customStyle={tagStyle}
349+
icon={tag.icon}
345350
color={tagColor}
346-
closable={props.allowEdit}
351+
closable={props.editable}
347352
onClose={(e)=>{e.preventDefault();deleteTag(index);}}
348353
onDoubleClick={()=>startEdit(index)}// double-click to edit
349354
onClick={()=>onTagClick(tag,index)}// normal click event
350355
>
351356
{isEditing ?(
352-
<EditableSpan
353-
ref={editableRef}
354-
contentEditable
355-
suppressContentEditableWarning
357+
<EditInput
358+
autoFocus
359+
value={editValue}
360+
onChange={(e)=>setEditValue(e.target.value)}
356361
onBlur={()=>confirmEdit(index)}
357362
onKeyDown={(e)=>{
358363
if(e.key==="Enter"){e.preventDefault();confirmEdit(index);}
359364
if(e.key==="Escape"){e.preventDefault();cancelEdit();}
360-
// stop container from also capturing these keystrokes
361365
e.stopPropagation();
362366
}}
363-
>
364-
{tag.label}
365-
</EditableSpan>
367+
/>
366368
) :(
367369
tag.label
368370
)}
@@ -371,7 +373,7 @@ const multiTags = (function () {
371373
})}
372374

373375
{/* Draft chip appears only while typing; press Enter to commit, Esc to cancel */}
374-
{props.allowEdit&&draft&&(
376+
{props.editable&&draft&&(
375377
<DraftTag$style={props.style}$customStyle={{}}color="default">
376378
{draft}
377379
</DraftTag>
@@ -385,7 +387,6 @@ const multiTags = (function () {
385387
<Sectionname={sectionNames.basic}>
386388
{children.options.propertyView({label:"Initial Tags (PropertyView)"})}
387389
{children.editable.propertyView({label:"Editable"})}
388-
{children.allowEdit.propertyView({label:"Allow Runtime Editing"})}
389390
{children.preventDuplicates.propertyView({label:"Prevent Duplicates (Runtime)"})}
390391
{children.allowEmptyEdits.propertyView({label:"Allow Empty Edit (Runtime)"})}
391392
{children.maxTags.propertyView({label:"Set Max Tags (Runtime) — true=50"})}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp