@seed-design/lynx-react 0.5.2 → 0.6.0

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.
Files changed (86) hide show
  1. package/lib/components/Checkbox/Checkbox.d.ts +26 -0
  2. package/lib/components/Checkbox/Checkbox.d.ts.map +1 -1
  3. package/lib/components/Checkbox/Checkbox.jsx +1 -1
  4. package/lib/components/Icon/Icon.d.ts +5 -0
  5. package/lib/components/Icon/Icon.d.ts.map +1 -1
  6. package/lib/components/Icon/Icon.jsx +14 -3
  7. package/lib/components/MannerTemp/MannerTemp.d.ts +19 -0
  8. package/lib/components/MannerTemp/MannerTemp.d.ts.map +1 -0
  9. package/lib/components/MannerTemp/MannerTemp.jsx +47 -0
  10. package/lib/components/MannerTemp/index.d.ts +2 -0
  11. package/lib/components/MannerTemp/index.d.ts.map +1 -0
  12. package/lib/components/MannerTempBadge/MannerTempBadge.d.ts +13 -0
  13. package/lib/components/MannerTempBadge/MannerTempBadge.d.ts.map +1 -0
  14. package/lib/components/MannerTempBadge/MannerTempBadge.jsx +15 -0
  15. package/lib/components/MannerTempBadge/index.d.ts +2 -0
  16. package/lib/components/MannerTempBadge/index.d.ts.map +1 -0
  17. package/lib/components/NotificationBadge/NotificationBadge.d.ts +18 -0
  18. package/lib/components/NotificationBadge/NotificationBadge.d.ts.map +1 -0
  19. package/lib/components/NotificationBadge/NotificationBadge.jsx +34 -0
  20. package/lib/components/NotificationBadge/index.d.ts +2 -0
  21. package/lib/components/NotificationBadge/index.d.ts.map +1 -0
  22. package/lib/components/PageBanner/PageBanner.d.ts +35 -0
  23. package/lib/components/PageBanner/PageBanner.d.ts.map +1 -0
  24. package/lib/components/PageBanner/PageBanner.jsx +150 -0
  25. package/lib/components/PageBanner/PageBanner.namespace.d.ts +2 -0
  26. package/lib/components/PageBanner/PageBanner.namespace.d.ts.map +1 -0
  27. package/lib/components/PageBanner/PageBanner.namespace.js +14 -0
  28. package/lib/components/PageBanner/index.d.ts +3 -0
  29. package/lib/components/PageBanner/index.d.ts.map +1 -0
  30. package/lib/components/RadioGroup/RadioGroup.d.ts +9 -0
  31. package/lib/components/RadioGroup/RadioGroup.d.ts.map +1 -1
  32. package/lib/components/RadioGroup/RadioGroup.jsx +1 -1
  33. package/lib/components/SegmentedControl/SegmentedControl.d.ts +27 -0
  34. package/lib/components/SegmentedControl/SegmentedControl.d.ts.map +1 -0
  35. package/lib/components/SegmentedControl/SegmentedControl.jsx +120 -0
  36. package/lib/components/SegmentedControl/SegmentedControl.namespace.d.ts +2 -0
  37. package/lib/components/SegmentedControl/SegmentedControl.namespace.d.ts.map +1 -0
  38. package/lib/components/SegmentedControl/SegmentedControl.namespace.js +10 -0
  39. package/lib/components/SegmentedControl/index.d.ts +3 -0
  40. package/lib/components/SegmentedControl/index.d.ts.map +1 -0
  41. package/lib/components/SelectBox/CheckSelectBox.namespace.d.ts +2 -0
  42. package/lib/components/SelectBox/CheckSelectBox.namespace.d.ts.map +1 -0
  43. package/lib/components/SelectBox/CheckSelectBox.namespace.js +17 -0
  44. package/lib/components/SelectBox/RadioSelectBox.namespace.d.ts +2 -0
  45. package/lib/components/SelectBox/RadioSelectBox.namespace.d.ts.map +1 -0
  46. package/lib/components/SelectBox/RadioSelectBox.namespace.js +15 -0
  47. package/lib/components/SelectBox/SelectBox.d.ts +85 -0
  48. package/lib/components/SelectBox/SelectBox.d.ts.map +1 -0
  49. package/lib/components/SelectBox/SelectBox.jsx +237 -0
  50. package/lib/components/SelectBox/index.d.ts +4 -0
  51. package/lib/components/SelectBox/index.d.ts.map +1 -0
  52. package/lib/components/index.d.ts +6 -0
  53. package/lib/components/index.d.ts.map +1 -1
  54. package/lib/hooks/useIconColor.d.ts +1 -0
  55. package/lib/hooks/useIconColor.d.ts.map +1 -1
  56. package/lib/hooks/useIconColor.js +4 -1
  57. package/lib/index.js +11 -1
  58. package/package.json +3 -3
  59. package/src/components/Checkbox/Checkbox.tsx +1 -1
  60. package/src/components/Icon/Icon.test.tsx +25 -1
  61. package/src/components/Icon/Icon.tsx +22 -1
  62. package/src/components/MannerTemp/MannerTemp.test.tsx +41 -0
  63. package/src/components/MannerTemp/MannerTemp.tsx +85 -0
  64. package/src/components/MannerTemp/index.ts +1 -0
  65. package/src/components/MannerTempBadge/MannerTempBadge.test.tsx +35 -0
  66. package/src/components/MannerTempBadge/MannerTempBadge.tsx +34 -0
  67. package/src/components/MannerTempBadge/index.ts +1 -0
  68. package/src/components/NotificationBadge/NotificationBadge.test.tsx +28 -0
  69. package/src/components/NotificationBadge/NotificationBadge.tsx +81 -0
  70. package/src/components/NotificationBadge/index.ts +1 -0
  71. package/src/components/PageBanner/PageBanner.namespace.ts +16 -0
  72. package/src/components/PageBanner/PageBanner.test.tsx +124 -0
  73. package/src/components/PageBanner/PageBanner.tsx +340 -0
  74. package/src/components/PageBanner/index.ts +18 -0
  75. package/src/components/RadioGroup/RadioGroup.tsx +1 -1
  76. package/src/components/SegmentedControl/SegmentedControl.namespace.ts +8 -0
  77. package/src/components/SegmentedControl/SegmentedControl.test.tsx +81 -0
  78. package/src/components/SegmentedControl/SegmentedControl.tsx +236 -0
  79. package/src/components/SegmentedControl/index.ts +10 -0
  80. package/src/components/SelectBox/CheckSelectBox.namespace.ts +22 -0
  81. package/src/components/SelectBox/RadioSelectBox.namespace.ts +18 -0
  82. package/src/components/SelectBox/SelectBox.test.tsx +169 -0
  83. package/src/components/SelectBox/SelectBox.tsx +533 -0
  84. package/src/components/SelectBox/index.ts +3 -0
  85. package/src/components/index.ts +6 -0
  86. package/src/hooks/useIconColor.ts +6 -1
@@ -0,0 +1,533 @@
1
+ import * as React from "@lynx-js/react";
2
+ import clsx from "clsx";
3
+
4
+ import { selectBox, type SelectBoxVariantProps } from "@seed-design/lynx-css/recipes/select-box";
5
+ import {
6
+ selectBoxCheckmark,
7
+ type SelectBoxCheckmarkVariantProps,
8
+ } from "@seed-design/lynx-css/recipes/select-box-checkmark";
9
+ import { selectBoxGroup } from "@seed-design/lynx-css/recipes/select-box-group";
10
+
11
+ import type {
12
+ LynxAccessibilityProps,
13
+ LynxStyledElementProps,
14
+ LynxTextRef,
15
+ LynxViewProps,
16
+ LynxViewRef,
17
+ } from "../../types";
18
+ import { createSlotRecipeContext } from "../../utils/create-slot-recipe-context";
19
+ import { CheckboxRoot, type CheckboxRootProps, useCheckboxContext } from "../Checkbox/Checkbox";
20
+ import { IconSlotProvider, InternalIcon, type InternalIconProps } from "../Icon/Icon";
21
+ import {
22
+ RadioGroupItem,
23
+ type RadioGroupItemProps,
24
+ useRadioGroupItemContext,
25
+ } from "../RadioGroup/RadioGroup";
26
+
27
+ /**
28
+ * @platform Lynx
29
+ *
30
+ * 웹 대비 미지원 기능:
31
+ * - HiddenInput / name / required / invalid: Lynx에 native form 제출 모델이 없음
32
+ * - focus / focusVisible: Lynx에 키보드 포커스 개념이 없음
33
+ * - DOM field wrapper: Lynx Registry는 label/error 연결용 wrapper를 제공하지 않음
34
+ */
35
+
36
+ type FooterVisibility = "when-selected" | "when-not-selected" | "always";
37
+ type PublicSelectBoxVariantProps = Omit<
38
+ SelectBoxVariantProps,
39
+ "selected" | "pressed" | "disabled" | "footerOpen"
40
+ >;
41
+
42
+ interface SelectBoxRuntimeContextValue {
43
+ selected: boolean;
44
+ pressed: boolean;
45
+ disabled: boolean;
46
+ footerVisibility: FooterVisibility;
47
+ variantProps: PublicSelectBoxVariantProps;
48
+ accessibilityRole: "checkbox" | "radio";
49
+ }
50
+
51
+ const SelectBoxRuntimeContext = React.createContext<SelectBoxRuntimeContextValue | null>(null);
52
+ const SelectBoxLayoutContext = React.createContext<PublicSelectBoxVariantProps>({});
53
+ const { ClassNamesProvider, useClassNames } = createSlotRecipeContext(selectBox);
54
+
55
+ function useSelectBoxRuntimeContext(consumer: string): SelectBoxRuntimeContextValue {
56
+ const context = React.useContext(SelectBoxRuntimeContext);
57
+ if (!context) {
58
+ throw new Error(`<${consumer}/> must be rendered inside a SelectBox root or item.`);
59
+ }
60
+ return context;
61
+ }
62
+
63
+ interface SelectBoxSurfaceProps extends LynxStyledElementProps {
64
+ accessibilityProps: LynxAccessibilityProps;
65
+ }
66
+
67
+ function SelectBoxSurface({
68
+ children,
69
+ className,
70
+ style,
71
+ accessibilityProps,
72
+ }: SelectBoxSurfaceProps) {
73
+ const context = useSelectBoxRuntimeContext("SelectBoxSurface");
74
+ const footerOpen =
75
+ context.footerVisibility === "always" ||
76
+ (context.footerVisibility === "when-selected" ? context.selected : !context.selected);
77
+ const classes = selectBox({
78
+ ...context.variantProps,
79
+ selected: context.selected,
80
+ pressed: context.pressed,
81
+ disabled: context.disabled,
82
+ footerOpen,
83
+ });
84
+
85
+ return (
86
+ <ClassNamesProvider value={classes}>
87
+ <IconSlotProvider
88
+ value={{
89
+ classNames: { prefixIcon: classes.prefixIcon },
90
+ deps: [context.selected, context.pressed, context.disabled],
91
+ }}
92
+ >
93
+ <view
94
+ className={clsx(classes.root, className)}
95
+ style={style}
96
+ accessibility-element={true}
97
+ accessibility-role-description={context.accessibilityRole}
98
+ accessibility-value={context.selected ? "selected" : "not selected"}
99
+ accessibility-traits={context.disabled ? "disabled" : undefined}
100
+ {...accessibilityProps}
101
+ >
102
+ {children}
103
+ <view className={classes.selectedStroke} accessibility-elements-hidden={true} />
104
+ </view>
105
+ </IconSlotProvider>
106
+ </ClassNamesProvider>
107
+ );
108
+ }
109
+
110
+ ////////////////////////////////////////////////////////////////////////////////////
111
+
112
+ export interface SelectBoxGroupProps extends LynxStyledElementProps {
113
+ /**
114
+ * 열 개수입니다. 2 이상이면 자식 Select Box의 기본 layout이 vertical이 됩니다.
115
+ * @default 1
116
+ */
117
+ columns?: number;
118
+ }
119
+
120
+ const SelectBoxGroup = React.forwardRef<unknown, SelectBoxGroupProps>((props, ref) => {
121
+ const { children, columns = 1, className, style, ...nativeProps } = props;
122
+ const classes = selectBoxGroup({ multiColumn: columns > 1 });
123
+ const layout: PublicSelectBoxVariantProps["layout"] = columns > 1 ? "vertical" : "horizontal";
124
+
125
+ return (
126
+ <SelectBoxLayoutContext.Provider value={{ layout }}>
127
+ <view
128
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
129
+ className={clsx(classes, className)}
130
+ style={{ ...style, gridTemplateColumns: `repeat(${columns}, 1fr)` }}
131
+ {...nativeProps}
132
+ >
133
+ {children}
134
+ </view>
135
+ </SelectBoxLayoutContext.Provider>
136
+ );
137
+ });
138
+ SelectBoxGroup.displayName = "SelectBoxGroup";
139
+
140
+ export interface CheckSelectBoxGroupProps extends SelectBoxGroupProps {}
141
+ export const CheckSelectBoxGroup = SelectBoxGroup;
142
+
143
+ export interface RadioSelectBoxGroupProps extends SelectBoxGroupProps {}
144
+ export const RadioSelectBoxGroup = SelectBoxGroup;
145
+
146
+ ////////////////////////////////////////////////////////////////////////////////////
147
+
148
+ export interface CheckSelectBoxRootProps
149
+ extends PublicSelectBoxVariantProps,
150
+ LynxStyledElementProps,
151
+ LynxAccessibilityProps,
152
+ Pick<
153
+ CheckboxRootProps,
154
+ "checked" | "defaultChecked" | "indeterminate" | "disabled" | "onCheckedChange"
155
+ > {
156
+ /** @default "when-selected" */
157
+ footerVisibility?: FooterVisibility;
158
+ }
159
+
160
+ interface CheckSelectBoxSurfaceProps extends SelectBoxSurfaceProps {
161
+ footerVisibility: FooterVisibility;
162
+ variantProps: PublicSelectBoxVariantProps;
163
+ }
164
+
165
+ function CheckSelectBoxSurface(props: CheckSelectBoxSurfaceProps) {
166
+ const checkbox = useCheckboxContext("CheckSelectBoxRoot");
167
+ const contextValue = React.useMemo<SelectBoxRuntimeContextValue>(
168
+ () => ({
169
+ selected: checkbox.checked,
170
+ pressed: checkbox.pressed,
171
+ disabled: checkbox.disabled,
172
+ footerVisibility: props.footerVisibility,
173
+ variantProps: props.variantProps,
174
+ accessibilityRole: "checkbox",
175
+ }),
176
+ [
177
+ checkbox.checked,
178
+ checkbox.disabled,
179
+ checkbox.pressed,
180
+ props.footerVisibility,
181
+ props.variantProps,
182
+ ],
183
+ );
184
+
185
+ return (
186
+ <SelectBoxRuntimeContext.Provider value={contextValue}>
187
+ <SelectBoxSurface {...props} />
188
+ </SelectBoxRuntimeContext.Provider>
189
+ );
190
+ }
191
+
192
+ export const CheckSelectBoxRoot = React.forwardRef<unknown, CheckSelectBoxRootProps>(
193
+ (props, ref) => {
194
+ const {
195
+ children,
196
+ className,
197
+ style,
198
+ checked,
199
+ defaultChecked,
200
+ indeterminate,
201
+ disabled,
202
+ onCheckedChange,
203
+ footerVisibility = "when-selected",
204
+ ...restProps
205
+ } = props;
206
+ const inheritedVariantProps = React.useContext(SelectBoxLayoutContext);
207
+ const [variantProps, accessibilityProps] = selectBox.splitVariantProps(restProps);
208
+ const resolvedVariantProps = {
209
+ ...variantProps,
210
+ layout: variantProps.layout ?? inheritedVariantProps.layout,
211
+ };
212
+ const interactionRootClassName = selectBox(resolvedVariantProps).interactionRoot;
213
+
214
+ return (
215
+ <CheckboxRoot
216
+ ref={ref}
217
+ className={interactionRootClassName}
218
+ checked={checked}
219
+ defaultChecked={defaultChecked}
220
+ indeterminate={indeterminate}
221
+ disabled={disabled}
222
+ onCheckedChange={onCheckedChange}
223
+ accessibility-element={false}
224
+ >
225
+ <CheckSelectBoxSurface
226
+ className={className}
227
+ style={style}
228
+ footerVisibility={footerVisibility}
229
+ variantProps={resolvedVariantProps}
230
+ accessibilityProps={accessibilityProps}
231
+ >
232
+ {children}
233
+ </CheckSelectBoxSurface>
234
+ </CheckboxRoot>
235
+ );
236
+ },
237
+ );
238
+ CheckSelectBoxRoot.displayName = "CheckSelectBoxRoot";
239
+
240
+ ////////////////////////////////////////////////////////////////////////////////////
241
+
242
+ export interface RadioSelectBoxItemProps
243
+ extends PublicSelectBoxVariantProps,
244
+ LynxStyledElementProps,
245
+ LynxAccessibilityProps,
246
+ Pick<RadioGroupItemProps, "value" | "disabled"> {
247
+ /** @default "when-selected" */
248
+ footerVisibility?: FooterVisibility;
249
+ }
250
+
251
+ interface RadioSelectBoxSurfaceProps extends SelectBoxSurfaceProps {
252
+ footerVisibility: FooterVisibility;
253
+ variantProps: PublicSelectBoxVariantProps;
254
+ }
255
+
256
+ function RadioSelectBoxSurface(props: RadioSelectBoxSurfaceProps) {
257
+ const item = useRadioGroupItemContext("RadioSelectBoxItem");
258
+ const contextValue = React.useMemo<SelectBoxRuntimeContextValue>(
259
+ () => ({
260
+ selected: item.checked,
261
+ pressed: item.pressed,
262
+ disabled: item.disabled,
263
+ footerVisibility: props.footerVisibility,
264
+ variantProps: props.variantProps,
265
+ accessibilityRole: "radio",
266
+ }),
267
+ [item.checked, item.disabled, item.pressed, props.footerVisibility, props.variantProps],
268
+ );
269
+
270
+ return (
271
+ <SelectBoxRuntimeContext.Provider value={contextValue}>
272
+ <SelectBoxSurface {...props} />
273
+ </SelectBoxRuntimeContext.Provider>
274
+ );
275
+ }
276
+
277
+ export const RadioSelectBoxItem = React.forwardRef<unknown, RadioSelectBoxItemProps>(
278
+ (props, ref) => {
279
+ const {
280
+ children,
281
+ className,
282
+ style,
283
+ value,
284
+ disabled,
285
+ footerVisibility = "when-selected",
286
+ ...restProps
287
+ } = props;
288
+ const inheritedVariantProps = React.useContext(SelectBoxLayoutContext);
289
+ const [variantProps, accessibilityProps] = selectBox.splitVariantProps(restProps);
290
+ const resolvedVariantProps = {
291
+ ...variantProps,
292
+ layout: variantProps.layout ?? inheritedVariantProps.layout,
293
+ };
294
+ const interactionRootClassName = selectBox(resolvedVariantProps).interactionRoot;
295
+
296
+ return (
297
+ <RadioGroupItem
298
+ ref={ref}
299
+ className={interactionRootClassName}
300
+ value={value}
301
+ disabled={disabled}
302
+ accessibility-element={false}
303
+ >
304
+ <RadioSelectBoxSurface
305
+ className={className}
306
+ style={style}
307
+ footerVisibility={footerVisibility}
308
+ variantProps={resolvedVariantProps}
309
+ accessibilityProps={accessibilityProps}
310
+ >
311
+ {children}
312
+ </RadioSelectBoxSurface>
313
+ </RadioGroupItem>
314
+ );
315
+ },
316
+ );
317
+ RadioSelectBoxItem.displayName = "RadioSelectBoxItem";
318
+
319
+ ////////////////////////////////////////////////////////////////////////////////////
320
+
321
+ function createViewSlot(displayName: string, slot: keyof ReturnType<typeof selectBox>) {
322
+ const Component = React.forwardRef<unknown, LynxStyledElementProps>((props, ref) => {
323
+ const { children, className, ...nativeProps } = props;
324
+ const classes = useClassNames();
325
+
326
+ return (
327
+ <view
328
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
329
+ className={clsx(classes[slot], className)}
330
+ {...nativeProps}
331
+ >
332
+ {children}
333
+ </view>
334
+ );
335
+ });
336
+ Component.displayName = displayName;
337
+ return Component;
338
+ }
339
+
340
+ function createTextSlot(displayName: string, slot: keyof ReturnType<typeof selectBox>) {
341
+ const Component = React.forwardRef<unknown, LynxStyledElementProps>((props, ref) => {
342
+ const { children, className, ...nativeProps } = props;
343
+ const classes = useClassNames();
344
+
345
+ return (
346
+ <text
347
+ {...(ref ? { ref: ref as LynxTextRef } : {})}
348
+ className={clsx(classes[slot], className)}
349
+ {...nativeProps}
350
+ >
351
+ {children}
352
+ </text>
353
+ );
354
+ });
355
+ Component.displayName = displayName;
356
+ return Component;
357
+ }
358
+
359
+ function createLabelSlot(displayName: string) {
360
+ const Component = React.forwardRef<unknown, LynxStyledElementProps>((props, ref) => {
361
+ const { children, className, ...nativeProps } = props;
362
+ const classes = useClassNames();
363
+ const labelChildren =
364
+ typeof children === "string" || typeof children === "number" ? (
365
+ <text>{children}</text>
366
+ ) : (
367
+ children
368
+ );
369
+
370
+ return (
371
+ <view
372
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
373
+ className={clsx(classes.label, className)}
374
+ {...nativeProps}
375
+ >
376
+ {labelChildren}
377
+ </view>
378
+ );
379
+ });
380
+ Component.displayName = displayName;
381
+ return Component;
382
+ }
383
+
384
+ export interface CheckSelectBoxTriggerProps extends LynxStyledElementProps {}
385
+ export const CheckSelectBoxTrigger = createViewSlot("CheckSelectBoxTrigger", "trigger");
386
+ export interface CheckSelectBoxContentProps extends LynxStyledElementProps {}
387
+ export const CheckSelectBoxContent = createViewSlot("CheckSelectBoxContent", "content");
388
+ export interface CheckSelectBoxBodyProps extends LynxStyledElementProps {}
389
+ export const CheckSelectBoxBody = createViewSlot("CheckSelectBoxBody", "body");
390
+ export interface CheckSelectBoxLabelProps extends LynxStyledElementProps {}
391
+ export const CheckSelectBoxLabel = createLabelSlot("CheckSelectBoxLabel");
392
+ export interface CheckSelectBoxDescriptionProps extends LynxStyledElementProps {}
393
+ export const CheckSelectBoxDescription = createTextSlot("CheckSelectBoxDescription", "description");
394
+
395
+ export interface RadioSelectBoxTriggerProps extends LynxStyledElementProps {}
396
+ export const RadioSelectBoxTrigger = createViewSlot("RadioSelectBoxTrigger", "trigger");
397
+ export interface RadioSelectBoxContentProps extends LynxStyledElementProps {}
398
+ export const RadioSelectBoxContent = createViewSlot("RadioSelectBoxContent", "content");
399
+ export interface RadioSelectBoxBodyProps extends LynxStyledElementProps {}
400
+ export const RadioSelectBoxBody = createViewSlot("RadioSelectBoxBody", "body");
401
+ export interface RadioSelectBoxLabelProps extends LynxStyledElementProps {}
402
+ export const RadioSelectBoxLabel = createLabelSlot("RadioSelectBoxLabel");
403
+ export interface RadioSelectBoxDescriptionProps extends LynxStyledElementProps {}
404
+ export const RadioSelectBoxDescription = createTextSlot("RadioSelectBoxDescription", "description");
405
+
406
+ ////////////////////////////////////////////////////////////////////////////////////
407
+
408
+ export interface SelectBoxFooterProps extends LynxStyledElementProps, LynxAccessibilityProps {}
409
+
410
+ type FooterLayoutChangeHandler = NonNullable<LynxViewProps["bindlayoutchange"]>;
411
+
412
+ function getFooterLayoutHeight(event: Parameters<FooterLayoutChangeHandler>[0]): number | null {
413
+ const eventWithHeight = event as Parameters<FooterLayoutChangeHandler>[0] & { height?: number };
414
+ const height = event.detail?.height ?? event.params?.height ?? eventWithHeight.height;
415
+ if (typeof height !== "number" || !Number.isFinite(height)) return null;
416
+ return Math.max(0, height);
417
+ }
418
+
419
+ const SelectBoxFooter = React.forwardRef<unknown, SelectBoxFooterProps>((props, ref) => {
420
+ const {
421
+ children,
422
+ className,
423
+ style,
424
+ "accessibility-elements-hidden": accessibilityElementsHidden = false,
425
+ ...nativeProps
426
+ } = props;
427
+ const context = useSelectBoxRuntimeContext("SelectBoxFooter");
428
+ const classes = useClassNames();
429
+ const [contentHeight, setContentHeight] = React.useState(0);
430
+ const open =
431
+ context.footerVisibility === "always" ||
432
+ (context.footerVisibility === "when-selected" ? context.selected : !context.selected);
433
+ const handleLayoutChange = React.useCallback<FooterLayoutChangeHandler>((event) => {
434
+ const height = getFooterLayoutHeight(event);
435
+ if (height !== null) setContentHeight((current) => (current === height ? current : height));
436
+ }, []);
437
+
438
+ return (
439
+ <view
440
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
441
+ className={clsx(classes.footer, className)}
442
+ style={{
443
+ ...style,
444
+ height:
445
+ context.footerVisibility === "always" || (open && contentHeight === 0)
446
+ ? "auto"
447
+ : open
448
+ ? `${contentHeight}px`
449
+ : "0px",
450
+ }}
451
+ accessibility-elements-hidden={!open || accessibilityElementsHidden}
452
+ {...nativeProps}
453
+ >
454
+ <view className={classes.footerInner} bindlayoutchange={handleLayoutChange}>
455
+ {children}
456
+ </view>
457
+ </view>
458
+ );
459
+ });
460
+ SelectBoxFooter.displayName = "SelectBoxFooter";
461
+
462
+ export interface CheckSelectBoxFooterProps extends SelectBoxFooterProps {}
463
+ export const CheckSelectBoxFooter = SelectBoxFooter;
464
+ export interface RadioSelectBoxFooterProps extends SelectBoxFooterProps {}
465
+ export const RadioSelectBoxFooter = SelectBoxFooter;
466
+
467
+ ////////////////////////////////////////////////////////////////////////////////////
468
+
469
+ interface SelectBoxCheckmarkContextValue {
470
+ iconClassName: string;
471
+ variantProps: SelectBoxCheckmarkVariantProps;
472
+ }
473
+
474
+ const SelectBoxCheckmarkContext = React.createContext<SelectBoxCheckmarkContextValue | null>(null);
475
+
476
+ export interface CheckSelectBoxCheckmarkControlProps extends LynxStyledElementProps {}
477
+
478
+ export const CheckSelectBoxCheckmarkControl = React.forwardRef<
479
+ unknown,
480
+ CheckSelectBoxCheckmarkControlProps
481
+ >((props, ref) => {
482
+ const { children, className, ...nativeProps } = props;
483
+ const context = useSelectBoxRuntimeContext("CheckSelectBoxCheckmarkControl");
484
+ const variantProps = {
485
+ selected: context.selected,
486
+ pressed: context.pressed,
487
+ disabled: context.disabled,
488
+ };
489
+ const classes = selectBoxCheckmark(variantProps);
490
+
491
+ return (
492
+ <SelectBoxCheckmarkContext.Provider value={{ iconClassName: classes.icon, variantProps }}>
493
+ <view
494
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
495
+ className={clsx(classes.root, className)}
496
+ accessibility-elements-hidden={true}
497
+ {...nativeProps}
498
+ >
499
+ {children}
500
+ </view>
501
+ </SelectBoxCheckmarkContext.Provider>
502
+ );
503
+ });
504
+ CheckSelectBoxCheckmarkControl.displayName = "CheckSelectBoxCheckmarkControl";
505
+
506
+ export interface CheckSelectBoxCheckmarkIconProps extends Omit<InternalIconProps, "deps"> {}
507
+
508
+ export const CheckSelectBoxCheckmarkIcon = React.forwardRef<
509
+ unknown,
510
+ CheckSelectBoxCheckmarkIconProps
511
+ >((props, ref) => {
512
+ const { className, ...otherProps } = props;
513
+ const context = React.useContext(SelectBoxCheckmarkContext);
514
+ if (!context) {
515
+ throw new Error(
516
+ "<CheckSelectBoxCheckmarkIcon/> must be rendered inside <CheckSelectBoxCheckmarkControl/>.",
517
+ );
518
+ }
519
+
520
+ return (
521
+ <InternalIcon
522
+ ref={ref}
523
+ className={clsx(context.iconClassName, className)}
524
+ deps={[
525
+ context.variantProps.selected,
526
+ context.variantProps.pressed,
527
+ context.variantProps.disabled,
528
+ ]}
529
+ {...otherProps}
530
+ />
531
+ );
532
+ });
533
+ CheckSelectBoxCheckmarkIcon.displayName = "CheckSelectBoxCheckmarkIcon";
@@ -0,0 +1,3 @@
1
+ export * from "./SelectBox";
2
+ export * as CheckSelectBox from "./CheckSelectBox.namespace";
3
+ export * as RadioSelectBox from "./RadioSelectBox.namespace";
@@ -11,8 +11,14 @@ export * from "./Divider";
11
11
  export * from "./Field";
12
12
  export * from "./Icon";
13
13
  export * from "./KeyboardAvoidingScrollView";
14
+ export * from "./MannerTemp";
15
+ export * from "./MannerTempBadge";
16
+ export * from "./NotificationBadge";
17
+ export * from "./PageBanner";
14
18
  export * from "./ProgressCircle";
15
19
  export * from "./RadioGroup";
20
+ export * from "./SegmentedControl";
21
+ export * from "./SelectBox";
16
22
  export * from "./Stack";
17
23
  export * from "./Switch";
18
24
  export * from "./Tabs";
@@ -9,6 +9,7 @@ type IconElement = MainThread.Element & {
9
9
 
10
10
  export interface UseIconColorOptions {
11
11
  sourceRef?: RefObject<MainThread.Element>;
12
+ enabled?: boolean;
12
13
  }
13
14
 
14
15
  // Lynx `<image>` 의 `tint-color` attribute 에 CSS variable 문자열을 직접 넣는 경로는
@@ -86,20 +87,24 @@ export function useIconColor(
86
87
  } {
87
88
  const ref = useMainThreadRef<IconElement>(null);
88
89
  const sourceRef = options?.sourceRef as RefObject<IconElement> | undefined;
90
+ const enabled = options?.enabled ?? true;
89
91
  const frameRef = useMainThreadRef<number>(0);
90
92
 
91
93
  function syncOnUiAppear() {
92
94
  "main thread";
95
+ if (!enabled) return;
93
96
  syncTintColorOnce(ref, sourceRef);
94
97
  }
95
98
 
96
99
  // deps 는 caller 가 책임.
97
100
  useEffect(() => {
101
+ if (!enabled) return;
102
+
98
103
  runOnMainThread(scheduleTintColorSync)(ref, sourceRef, frameRef);
99
104
  return () => {
100
105
  runOnMainThread(cancelTintColorSync)(frameRef);
101
106
  };
102
- }, deps);
107
+ }, [...deps, enabled]);
103
108
 
104
109
  return { ref, "main-thread:binduiappear": syncOnUiAppear };
105
110
  }