@uniai-fe/uds-primitives 0.3.51 → 0.3.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-primitives",
3
- "version": "0.3.51",
3
+ "version": "0.3.52",
4
4
  "description": "UNIAI Design System; Primitives Components Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -162,10 +162,25 @@ export function SelectDefault<OptionData = unknown>({
162
162
  [mergedOptions, uncontrolledSelectedValue],
163
163
  );
164
164
 
165
- // 9) custom mode는 customOptions 존재 + custom option value 선택 상태로 판단한다.
165
+ // 9) customOptions가 있는 경우 inputProps.value가 있고 option 매칭이 없으면 custom mode로 간주한다.
166
+ const forcedCustomLabelValue = toInputText(inputProps?.value as ReactNode);
167
+ const shouldForceCustomFromInput = Boolean(
168
+ customOptions && !selectedOption && forcedCustomLabelValue,
169
+ );
170
+ const resolvedSelectedOption = useMemo(
171
+ () =>
172
+ shouldForceCustomFromInput
173
+ ? mergedOptions.find(option =>
174
+ isSameSelectedValue(option.value, SELECT_CUSTOM_OPTION_VALUE),
175
+ )
176
+ : selectedOption,
177
+ [mergedOptions, selectedOption, shouldForceCustomFromInput],
178
+ );
179
+
180
+ // 10) custom mode는 custom option selected 상태(또는 forced custom 상태)로 판단한다.
166
181
  const isCustomInputActive = Boolean(
167
182
  customOptions &&
168
- toSelectedValueKey(selectedOption?.value) ===
183
+ toSelectedValueKey(resolvedSelectedOption?.value) ===
169
184
  toSelectedValueKey(SELECT_CUSTOM_OPTION_VALUE),
170
185
  );
171
186
 
@@ -176,21 +191,22 @@ export function SelectDefault<OptionData = unknown>({
176
191
  }
177
192
  }, [isCustomInputActive]);
178
193
 
179
- // 10) 외부 displayLabel이 있으면 우선 사용하고, 없으면 선택 option label을 사용한다.
194
+ // 11) 외부 displayLabel이 있으면 우선 사용하고, 없으면 선택 option label을 사용한다.
180
195
  const resolvedDisplayLabel =
181
- displayLabel ?? (isCustomInputActive ? undefined : selectedOption?.label);
196
+ displayLabel ??
197
+ (isCustomInputActive ? undefined : resolvedSelectedOption?.label);
182
198
 
183
- // 11) open 제어형/비제어형 계약은 공용 hook으로 통합 처리한다.
199
+ // 12) open 제어형/비제어형 계약은 공용 hook으로 통합 처리한다.
184
200
  const { open: dropdownOpen, setOpen } = useSelectDropdownOpenState({
185
201
  open,
186
202
  defaultOpen,
187
203
  onOpen,
188
204
  });
189
205
 
190
- // 12) disabled/readOnly 상태에서는 open/option 선택 인터랙션을 모두 차단한다.
206
+ // 13) disabled/readOnly 상태에서는 open/option 선택 인터랙션을 모두 차단한다.
191
207
  const isInteractionBlocked = disabled || readOnly;
192
208
 
193
- // 13) open 상태 변경 처리: 차단 상태면 닫힘 고정, 아니면 nextOpen 반영.
209
+ // 14) open 상태 변경 처리: 차단 상태면 닫힘 고정, 아니면 nextOpen 반영.
194
210
  const handleOpenChange = (nextOpen: boolean) => {
195
211
  if (isInteractionBlocked) {
196
212
  setOpen(false);
@@ -199,7 +215,7 @@ export function SelectDefault<OptionData = unknown>({
199
215
  setOpen(nextOpen);
200
216
  };
201
217
 
202
- // 14) option 선택 처리: onSelectOption 호출 후, value가 변경될 때만 onSelectChange를 호출한다.
218
+ // 15) option 선택 처리: onSelectOption 호출 후, value가 변경될 때만 onSelectChange를 호출한다.
203
219
  const handleOptionSelect = (
204
220
  option: SelectDropdownOption<OptionData>,
205
221
  event: Event,
@@ -208,7 +224,7 @@ export function SelectDefault<OptionData = unknown>({
208
224
  return;
209
225
  }
210
226
 
211
- const previousOption = selectedOption;
227
+ const previousOption = resolvedSelectedOption;
212
228
  const hasChanged = !isSameSelectedValue(
213
229
  previousOption?.value,
214
230
  option.value,
@@ -224,7 +240,7 @@ export function SelectDefault<OptionData = unknown>({
224
240
  setOpen(false);
225
241
  };
226
242
 
227
- // 15) label input 값은 custom mode에서만 사용자 입력값/controlled inputProps.value를 사용한다.
243
+ // 16) label input 값은 custom mode에서만 사용자 입력값/controlled inputProps.value를 사용한다.
228
244
  const labelInputValue = isCustomInputActive
229
245
  ? typeof inputProps?.value === "string" ||
230
246
  typeof inputProps?.value === "number"
@@ -232,7 +248,7 @@ export function SelectDefault<OptionData = unknown>({
232
248
  : customLabelValue
233
249
  : toInputText(resolvedDisplayLabel);
234
250
 
235
- // 16) 렌더: Container → Dropdown.Root → Trigger → Menu.List 구조를 유지한다.
251
+ // 17) 렌더: Container → Dropdown.Root → Trigger → Menu.List 구조를 유지한다.
236
252
  return (
237
253
  <Container
238
254
  className={clsx("select-trigger-container", className)}
@@ -284,7 +300,7 @@ export function SelectDefault<OptionData = unknown>({
284
300
  valueFieldValue={
285
301
  isCustomInputActive && !customRegister
286
302
  ? labelInputValue
287
- : (selectedOption?.value ?? "")
303
+ : (resolvedSelectedOption?.value ?? "")
288
304
  }
289
305
  // 변경: custom mode 진입 시 label input에 focus를 연결한다.
290
306
  shouldFocusInput={isCustomInputActive}
@@ -310,7 +326,7 @@ export function SelectDefault<OptionData = unknown>({
310
326
  right={option.right}
311
327
  multiple={Boolean(option.multiple)}
312
328
  isSelected={isSameSelectedValue(
313
- selectedOption?.value,
329
+ resolvedSelectedOption?.value,
314
330
  option.value,
315
331
  )}
316
332
  onSelect={event => {