@seed-design/lynx-react 0.1.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.
- package/lib/_virtual/_rolldown/runtime.js +13 -0
- package/lib/components/ActionButton/ActionButton.d.ts +44 -0
- package/lib/components/ActionButton/ActionButton.d.ts.map +1 -0
- package/lib/components/ActionButton/ActionButton.jsx +158 -0
- package/lib/components/ActionButton/index.d.ts +2 -0
- package/lib/components/ActionButton/index.d.ts.map +1 -0
- package/lib/components/BottomSheet/BottomSheet.d.ts +87 -0
- package/lib/components/BottomSheet/BottomSheet.d.ts.map +1 -0
- package/lib/components/BottomSheet/BottomSheet.jsx +150 -0
- package/lib/components/BottomSheet/BottomSheet.namespace.d.ts +2 -0
- package/lib/components/BottomSheet/BottomSheet.namespace.d.ts.map +1 -0
- package/lib/components/BottomSheet/BottomSheet.namespace.js +18 -0
- package/lib/components/BottomSheet/index.d.ts +3 -0
- package/lib/components/BottomSheet/index.d.ts.map +1 -0
- package/lib/components/Box/Box.d.ts +20 -0
- package/lib/components/Box/Box.d.ts.map +1 -0
- package/lib/components/Box/Box.jsx +14 -0
- package/lib/components/Box/index.d.ts +2 -0
- package/lib/components/Box/index.d.ts.map +1 -0
- package/lib/components/Checkbox/Checkbox.d.ts +35 -0
- package/lib/components/Checkbox/Checkbox.d.ts.map +1 -0
- package/lib/components/Checkbox/Checkbox.jsx +130 -0
- package/lib/components/Checkbox/Checkbox.namespace.d.ts +2 -0
- package/lib/components/Checkbox/Checkbox.namespace.d.ts.map +1 -0
- package/lib/components/Checkbox/Checkbox.namespace.js +12 -0
- package/lib/components/Checkbox/index.d.ts +3 -0
- package/lib/components/Checkbox/index.d.ts.map +1 -0
- package/lib/components/Icon/Icon.d.ts +37 -0
- package/lib/components/Icon/Icon.d.ts.map +1 -0
- package/lib/components/Icon/Icon.jsx +143 -0
- package/lib/components/Icon/index.d.ts +2 -0
- package/lib/components/Icon/index.d.ts.map +1 -0
- package/lib/components/ProgressCircle/ProgressCircle.d.ts +25 -0
- package/lib/components/ProgressCircle/ProgressCircle.d.ts.map +1 -0
- package/lib/components/ProgressCircle/ProgressCircle.jsx +281 -0
- package/lib/components/ProgressCircle/ProgressCircle.namespace.d.ts +2 -0
- package/lib/components/ProgressCircle/ProgressCircle.namespace.d.ts.map +1 -0
- package/lib/components/ProgressCircle/ProgressCircle.namespace.js +9 -0
- package/lib/components/ProgressCircle/index.d.ts +3 -0
- package/lib/components/ProgressCircle/index.d.ts.map +1 -0
- package/lib/components/RadioGroup/RadioGroup.d.ts +34 -0
- package/lib/components/RadioGroup/RadioGroup.d.ts.map +1 -0
- package/lib/components/RadioGroup/RadioGroup.jsx +160 -0
- package/lib/components/RadioGroup/RadioGroup.namespace.d.ts +2 -0
- package/lib/components/RadioGroup/RadioGroup.namespace.d.ts.map +1 -0
- package/lib/components/RadioGroup/RadioGroup.namespace.js +12 -0
- package/lib/components/RadioGroup/index.d.ts +3 -0
- package/lib/components/RadioGroup/index.d.ts.map +1 -0
- package/lib/components/Stack/Stack.d.ts +33 -0
- package/lib/components/Stack/Stack.d.ts.map +1 -0
- package/lib/components/Stack/Stack.jsx +39 -0
- package/lib/components/Stack/index.d.ts +2 -0
- package/lib/components/Stack/index.d.ts.map +1 -0
- package/lib/components/Switch/Switch.d.ts +21 -0
- package/lib/components/Switch/Switch.d.ts.map +1 -0
- package/lib/components/Switch/Switch.jsx +101 -0
- package/lib/components/Switch/Switch.namespace.d.ts +2 -0
- package/lib/components/Switch/Switch.namespace.d.ts.map +1 -0
- package/lib/components/Switch/Switch.namespace.js +11 -0
- package/lib/components/Switch/index.d.ts +3 -0
- package/lib/components/Switch/index.d.ts.map +1 -0
- package/lib/components/TagGroup/TagGroup.d.ts +34 -0
- package/lib/components/TagGroup/TagGroup.d.ts.map +1 -0
- package/lib/components/TagGroup/TagGroup.jsx +57 -0
- package/lib/components/TagGroup/TagGroup.namespace.d.ts +2 -0
- package/lib/components/TagGroup/TagGroup.namespace.d.ts.map +1 -0
- package/lib/components/TagGroup/TagGroup.namespace.js +10 -0
- package/lib/components/TagGroup/index.d.ts +3 -0
- package/lib/components/TagGroup/index.d.ts.map +1 -0
- package/lib/components/Text/Text.d.ts +7 -0
- package/lib/components/Text/Text.d.ts.map +1 -0
- package/lib/components/Text/Text.jsx +28 -0
- package/lib/components/Text/index.d.ts +2 -0
- package/lib/components/Text/index.d.ts.map +1 -0
- package/lib/components/index.d.ts +12 -0
- package/lib/components/index.d.ts.map +1 -0
- package/lib/hooks/index.d.ts +5 -0
- package/lib/hooks/index.d.ts.map +1 -0
- package/lib/hooks/useControllableState.d.ts +15 -0
- package/lib/hooks/useControllableState.d.ts.map +1 -0
- package/lib/hooks/useControllableState.js +27 -0
- package/lib/hooks/useIconColor.d.ts +19 -0
- package/lib/hooks/useIconColor.d.ts.map +1 -0
- package/lib/hooks/useIconColor.js +38 -0
- package/lib/hooks/usePressTap.d.ts +27 -0
- package/lib/hooks/usePressTap.d.ts.map +1 -0
- package/lib/hooks/usePressTap.js +35 -0
- package/lib/hooks/useSafeArea.d.ts +19 -0
- package/lib/hooks/useSafeArea.d.ts.map +1 -0
- package/lib/hooks/useSafeArea.js +33 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +26 -0
- package/lib/types.d.ts +33 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/utils/children.d.ts +3 -0
- package/lib/utils/children.d.ts.map +1 -0
- package/lib/utils/children.js +8 -0
- package/lib/utils/create-slot-recipe-context.d.ts +27 -0
- package/lib/utils/create-slot-recipe-context.d.ts.map +1 -0
- package/lib/utils/create-slot-recipe-context.jsx +94 -0
- package/lib/utils/get-seed-class-name.d.ts +22 -0
- package/lib/utils/get-seed-class-name.d.ts.map +1 -0
- package/lib/utils/get-seed-class-name.js +28 -0
- package/lib/utils/index.d.ts +5 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/resolve-recipe-token.d.ts +38 -0
- package/lib/utils/resolve-recipe-token.d.ts.map +1 -0
- package/lib/utils/resolve-recipe-token.js +49 -0
- package/lib/utils/split-multiple-variants-props.d.ts +29 -0
- package/lib/utils/split-multiple-variants-props.d.ts.map +1 -0
- package/lib/utils/split-multiple-variants-props.js +34 -0
- package/lib/utils/styled.d.ts +83 -0
- package/lib/utils/styled.d.ts.map +1 -0
- package/lib/utils/styled.js +144 -0
- package/package.json +54 -0
- package/src/components/ActionButton/ActionButton.test.tsx +137 -0
- package/src/components/ActionButton/ActionButton.tsx +340 -0
- package/src/components/ActionButton/index.ts +1 -0
- package/src/components/BottomSheet/BottomSheet.namespace.ts +24 -0
- package/src/components/BottomSheet/BottomSheet.test.tsx +153 -0
- package/src/components/BottomSheet/BottomSheet.tsx +397 -0
- package/src/components/BottomSheet/index.ts +27 -0
- package/src/components/Box/Box.test.tsx +85 -0
- package/src/components/Box/Box.tsx +39 -0
- package/src/components/Box/index.ts +1 -0
- package/src/components/Checkbox/Checkbox.namespace.ts +12 -0
- package/src/components/Checkbox/Checkbox.tsx +282 -0
- package/src/components/Checkbox/index.ts +14 -0
- package/src/components/Icon/Icon.tsx +265 -0
- package/src/components/Icon/index.ts +8 -0
- package/src/components/ProgressCircle/ProgressCircle.namespace.ts +5 -0
- package/src/components/ProgressCircle/ProgressCircle.tsx +424 -0
- package/src/components/ProgressCircle/index.ts +9 -0
- package/src/components/RadioGroup/RadioGroup.namespace.ts +12 -0
- package/src/components/RadioGroup/RadioGroup.tsx +320 -0
- package/src/components/RadioGroup/index.ts +14 -0
- package/src/components/Stack/Stack.tsx +97 -0
- package/src/components/Stack/index.ts +1 -0
- package/src/components/Switch/Switch.namespace.ts +10 -0
- package/src/components/Switch/Switch.tsx +200 -0
- package/src/components/Switch/index.ts +12 -0
- package/src/components/TagGroup/TagGroup.namespace.ts +8 -0
- package/src/components/TagGroup/TagGroup.tsx +127 -0
- package/src/components/TagGroup/index.ts +10 -0
- package/src/components/Text/Text.tsx +64 -0
- package/src/components/Text/index.ts +1 -0
- package/src/components/index.ts +11 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/useControllableState.test.ts +120 -0
- package/src/hooks/useControllableState.ts +38 -0
- package/src/hooks/useIconColor.test.ts +13 -0
- package/src/hooks/useIconColor.ts +67 -0
- package/src/hooks/usePressTap.test.ts +129 -0
- package/src/hooks/usePressTap.ts +58 -0
- package/src/hooks/useSafeArea.test.ts +83 -0
- package/src/hooks/useSafeArea.ts +56 -0
- package/src/index.ts +4 -0
- package/src/types.ts +45 -0
- package/src/utils/children.ts +13 -0
- package/src/utils/create-slot-recipe-context.test.tsx +96 -0
- package/src/utils/create-slot-recipe-context.tsx +187 -0
- package/src/utils/get-seed-class-name.ts +47 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/resolve-recipe-token.ts +47 -0
- package/src/utils/split-multiple-variants-props.test.ts +61 -0
- package/src/utils/split-multiple-variants-props.ts +59 -0
- package/src/utils/styled.ts +442 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import * as React from "@lynx-js/react";
|
|
2
|
+
import { isValidElement, type ReactElement } from "@lynx-js/react";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
|
|
5
|
+
import { checkbox } from "@seed-design/lynx-css/recipes/checkbox";
|
|
6
|
+
import type { CheckboxVariantProps } from "@seed-design/lynx-css/recipes/checkbox";
|
|
7
|
+
import { checkmark } from "@seed-design/lynx-css/recipes/checkmark";
|
|
8
|
+
import type { CheckmarkVariantProps } from "@seed-design/lynx-css/recipes/checkmark";
|
|
9
|
+
import { checkboxGroup } from "@seed-design/lynx-css/recipes/checkbox-group";
|
|
10
|
+
|
|
11
|
+
import { useControllableState } from "../../hooks/useControllableState";
|
|
12
|
+
import { usePressTap } from "../../hooks/usePressTap";
|
|
13
|
+
import type {
|
|
14
|
+
LynxIconElementProps,
|
|
15
|
+
LynxStyledElementProps,
|
|
16
|
+
LynxTextRef,
|
|
17
|
+
LynxViewRef,
|
|
18
|
+
} from "../../types";
|
|
19
|
+
import { splitMultipleVariantsProps } from "../../utils/split-multiple-variants-props";
|
|
20
|
+
import { InternalIcon } from "../Icon/Icon";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @platform Lynx
|
|
24
|
+
*
|
|
25
|
+
* 웹 대비 미지원 기능:
|
|
26
|
+
* - HiddenInput / name / value / required / invalid: Lynx에 native form 제출 모델이 없음
|
|
27
|
+
* - focus / focusVisible: Lynx에 키보드 포커스 개념이 없음
|
|
28
|
+
* - onChange (raw DOM event): 의미 없음. 토글 이벤트는 onCheckedChange로만 노출
|
|
29
|
+
* - weight="default" | "stronger" 호환 매핑: Lynx 신규 컴포넌트이므로 처음부터 "regular" | "bold" 만 노출
|
|
30
|
+
*
|
|
31
|
+
* Indicator 는 `@karrotmarket/lynx-monochrome-icon` 의 monochrome icon 컴포넌트를
|
|
32
|
+
* 받는다. 내부에서 `<image tint-color=...>` 로 렌더되므로 `useIconColor` 훅이
|
|
33
|
+
* recipe 의 `color` 토큰을 `tint-color` 로 동기화한다. raw SVG 주입은 Lynx 범위 밖.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
interface CheckboxContextValue {
|
|
37
|
+
checked: boolean;
|
|
38
|
+
indeterminate: boolean;
|
|
39
|
+
disabled: boolean;
|
|
40
|
+
pressed: boolean;
|
|
41
|
+
checkboxVariantProps: CheckboxVariantProps;
|
|
42
|
+
checkmarkVariantProps: CheckmarkVariantProps;
|
|
43
|
+
toggle: () => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const CheckboxContext = React.createContext<CheckboxContextValue | null>(null);
|
|
47
|
+
|
|
48
|
+
function useCheckboxContext(consumer: string): CheckboxContextValue {
|
|
49
|
+
const ctx = React.useContext(CheckboxContext);
|
|
50
|
+
if (!ctx) {
|
|
51
|
+
throw new Error(`<${consumer}/> must be rendered inside <CheckboxRoot/>.`);
|
|
52
|
+
}
|
|
53
|
+
return ctx;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface CheckmarkControlContextValue {
|
|
57
|
+
iconClassName: string;
|
|
58
|
+
checkmarkVariantProps: CheckmarkVariantProps;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const CheckmarkControlContext = React.createContext<CheckmarkControlContextValue | null>(null);
|
|
62
|
+
|
|
63
|
+
function useCheckmarkControlContext(consumer: string): CheckmarkControlContextValue {
|
|
64
|
+
const ctx = React.useContext(CheckmarkControlContext);
|
|
65
|
+
if (!ctx) {
|
|
66
|
+
throw new Error(`<${consumer}/> must be rendered inside <CheckboxControl/>.`);
|
|
67
|
+
}
|
|
68
|
+
return ctx;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
72
|
+
|
|
73
|
+
export interface CheckboxRootProps
|
|
74
|
+
extends CheckboxVariantProps,
|
|
75
|
+
Omit<CheckmarkVariantProps, "size" | "checked" | "disabled" | "indeterminate">,
|
|
76
|
+
LynxStyledElementProps {
|
|
77
|
+
checked?: boolean;
|
|
78
|
+
defaultChecked?: boolean;
|
|
79
|
+
indeterminate?: boolean;
|
|
80
|
+
disabled?: boolean;
|
|
81
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const CheckboxRoot = React.forwardRef<unknown, CheckboxRootProps>((props, ref) => {
|
|
85
|
+
const {
|
|
86
|
+
children,
|
|
87
|
+
className,
|
|
88
|
+
checked: checkedProp,
|
|
89
|
+
defaultChecked = false,
|
|
90
|
+
indeterminate = false,
|
|
91
|
+
disabled = false,
|
|
92
|
+
onCheckedChange,
|
|
93
|
+
...restProps
|
|
94
|
+
} = props;
|
|
95
|
+
const [{ checkbox: checkboxVariantProps, checkmark: checkmarkVariantProps }, nativeProps] =
|
|
96
|
+
splitMultipleVariantsProps(restProps, { checkbox, checkmark });
|
|
97
|
+
|
|
98
|
+
const [checked, setChecked] = useControllableState({
|
|
99
|
+
value: checkedProp,
|
|
100
|
+
defaultValue: defaultChecked,
|
|
101
|
+
onChange: onCheckedChange,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const toggle = React.useCallback(() => setChecked(!checked), [checked, setChecked]);
|
|
105
|
+
|
|
106
|
+
const { pressed, ...pressHandlers } = usePressTap({
|
|
107
|
+
disabled,
|
|
108
|
+
onTap: toggle,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const rootClassName = checkbox({ ...checkboxVariantProps, disabled }).root;
|
|
112
|
+
|
|
113
|
+
const contextValue = React.useMemo<CheckboxContextValue>(
|
|
114
|
+
() => ({
|
|
115
|
+
checked,
|
|
116
|
+
indeterminate,
|
|
117
|
+
disabled,
|
|
118
|
+
pressed,
|
|
119
|
+
checkboxVariantProps,
|
|
120
|
+
checkmarkVariantProps,
|
|
121
|
+
toggle,
|
|
122
|
+
}),
|
|
123
|
+
[
|
|
124
|
+
checked,
|
|
125
|
+
indeterminate,
|
|
126
|
+
disabled,
|
|
127
|
+
pressed,
|
|
128
|
+
checkboxVariantProps,
|
|
129
|
+
checkmarkVariantProps,
|
|
130
|
+
toggle,
|
|
131
|
+
],
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<CheckboxContext.Provider value={contextValue}>
|
|
136
|
+
<view
|
|
137
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
138
|
+
className={clsx(rootClassName, className)}
|
|
139
|
+
{...pressHandlers}
|
|
140
|
+
{...nativeProps}
|
|
141
|
+
>
|
|
142
|
+
{children}
|
|
143
|
+
</view>
|
|
144
|
+
</CheckboxContext.Provider>
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
CheckboxRoot.displayName = "CheckboxRoot";
|
|
148
|
+
|
|
149
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
150
|
+
|
|
151
|
+
export interface CheckboxControlProps
|
|
152
|
+
extends Pick<CheckmarkVariantProps, "tone" | "variant" | "size">,
|
|
153
|
+
LynxStyledElementProps {}
|
|
154
|
+
|
|
155
|
+
export const CheckboxControl = React.forwardRef<unknown, CheckboxControlProps>((props, ref) => {
|
|
156
|
+
const [variantProps, restProps] = checkmark.splitVariantProps(props);
|
|
157
|
+
const { children, className, ...nativeProps } = restProps;
|
|
158
|
+
const context = useCheckboxContext("CheckboxControl");
|
|
159
|
+
const checkmarkVariantProps: CheckmarkVariantProps = {
|
|
160
|
+
...context.checkmarkVariantProps,
|
|
161
|
+
...variantProps,
|
|
162
|
+
checked: context.checked,
|
|
163
|
+
disabled: context.disabled,
|
|
164
|
+
indeterminate: context.indeterminate,
|
|
165
|
+
pressed: context.pressed,
|
|
166
|
+
};
|
|
167
|
+
const classes = checkmark(checkmarkVariantProps);
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<CheckmarkControlContext.Provider
|
|
171
|
+
value={{ iconClassName: classes.icon, checkmarkVariantProps }}
|
|
172
|
+
>
|
|
173
|
+
<view
|
|
174
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
175
|
+
className={clsx(classes.root, className)}
|
|
176
|
+
{...nativeProps}
|
|
177
|
+
>
|
|
178
|
+
{children}
|
|
179
|
+
</view>
|
|
180
|
+
</CheckmarkControlContext.Provider>
|
|
181
|
+
);
|
|
182
|
+
});
|
|
183
|
+
CheckboxControl.displayName = "CheckboxControl";
|
|
184
|
+
|
|
185
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
186
|
+
|
|
187
|
+
export interface CheckboxIndicatorProps
|
|
188
|
+
extends Pick<LynxStyledElementProps, "className" | "style"> {
|
|
189
|
+
/** Icon rendered when neither checked nor indeterminate. Optional. */
|
|
190
|
+
unchecked?: ReactElement<LynxIconElementProps>;
|
|
191
|
+
/** Icon rendered when `checked=true` and `indeterminate=false`. */
|
|
192
|
+
checked: ReactElement<LynxIconElementProps>;
|
|
193
|
+
/** Icon rendered when `indeterminate=true`. */
|
|
194
|
+
indeterminate?: ReactElement<LynxIconElementProps>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function CheckboxIndicator(props: CheckboxIndicatorProps) {
|
|
198
|
+
const {
|
|
199
|
+
unchecked,
|
|
200
|
+
checked: checkedIcon,
|
|
201
|
+
indeterminate: indeterminateIcon,
|
|
202
|
+
className,
|
|
203
|
+
style,
|
|
204
|
+
} = props;
|
|
205
|
+
const context = useCheckboxContext("CheckboxIndicator");
|
|
206
|
+
const { iconClassName, checkmarkVariantProps } = useCheckmarkControlContext("CheckboxIndicator");
|
|
207
|
+
|
|
208
|
+
if (process.env.NODE_ENV !== "production" && context.indeterminate && !indeterminateIcon) {
|
|
209
|
+
console.warn(
|
|
210
|
+
"[seed-design] CheckboxIndicator: `indeterminate` prop must be provided when the checkbox is in an indeterminate state.",
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const icon = context.indeterminate
|
|
215
|
+
? indeterminateIcon
|
|
216
|
+
: context.checked
|
|
217
|
+
? checkedIcon
|
|
218
|
+
: unchecked;
|
|
219
|
+
if (!icon || !isValidElement<LynxIconElementProps>(icon)) return null;
|
|
220
|
+
|
|
221
|
+
return (
|
|
222
|
+
<InternalIcon
|
|
223
|
+
icon={icon}
|
|
224
|
+
className={clsx(iconClassName, className)}
|
|
225
|
+
style={style}
|
|
226
|
+
deps={[
|
|
227
|
+
context.checked,
|
|
228
|
+
context.indeterminate,
|
|
229
|
+
context.disabled,
|
|
230
|
+
context.pressed,
|
|
231
|
+
checkmarkVariantProps.tone,
|
|
232
|
+
checkmarkVariantProps.variant,
|
|
233
|
+
checkmarkVariantProps.size,
|
|
234
|
+
]}
|
|
235
|
+
/>
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
CheckboxIndicator.displayName = "CheckboxIndicator";
|
|
239
|
+
|
|
240
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
241
|
+
|
|
242
|
+
export interface CheckboxLabelProps extends LynxStyledElementProps {}
|
|
243
|
+
|
|
244
|
+
export const CheckboxLabel = React.forwardRef<unknown, CheckboxLabelProps>((props, ref) => {
|
|
245
|
+
const { children, className, ...nativeProps } = props;
|
|
246
|
+
const context = useCheckboxContext("CheckboxLabel");
|
|
247
|
+
const labelClassName = checkbox({
|
|
248
|
+
...context.checkboxVariantProps,
|
|
249
|
+
disabled: context.disabled,
|
|
250
|
+
}).label;
|
|
251
|
+
|
|
252
|
+
return (
|
|
253
|
+
<text
|
|
254
|
+
{...(ref ? { ref: ref as LynxTextRef } : {})}
|
|
255
|
+
className={clsx(labelClassName, className)}
|
|
256
|
+
{...nativeProps}
|
|
257
|
+
>
|
|
258
|
+
{children}
|
|
259
|
+
</text>
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
CheckboxLabel.displayName = "CheckboxLabel";
|
|
263
|
+
|
|
264
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
265
|
+
|
|
266
|
+
export interface CheckboxGroupProps extends LynxStyledElementProps {}
|
|
267
|
+
|
|
268
|
+
export const CheckboxGroup = React.forwardRef<unknown, CheckboxGroupProps>((props, ref) => {
|
|
269
|
+
const { children, className, ...nativeProps } = props;
|
|
270
|
+
const classes = checkboxGroup();
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<view
|
|
274
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
275
|
+
className={clsx(classes.root, className)}
|
|
276
|
+
{...nativeProps}
|
|
277
|
+
>
|
|
278
|
+
{children}
|
|
279
|
+
</view>
|
|
280
|
+
);
|
|
281
|
+
});
|
|
282
|
+
CheckboxGroup.displayName = "CheckboxGroup";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export {
|
|
2
|
+
CheckboxRoot,
|
|
3
|
+
CheckboxControl,
|
|
4
|
+
CheckboxIndicator,
|
|
5
|
+
CheckboxLabel,
|
|
6
|
+
CheckboxGroup,
|
|
7
|
+
type CheckboxRootProps,
|
|
8
|
+
type CheckboxControlProps,
|
|
9
|
+
type CheckboxIndicatorProps,
|
|
10
|
+
type CheckboxLabelProps,
|
|
11
|
+
type CheckboxGroupProps,
|
|
12
|
+
} from "./Checkbox";
|
|
13
|
+
|
|
14
|
+
export * as Checkbox from "./Checkbox.namespace";
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { useMainThreadRef } from "@lynx-js/react";
|
|
2
|
+
import type { CSSProperties, MainThread } from "@lynx-js/types";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import * as React from "@lynx-js/react";
|
|
5
|
+
import {
|
|
6
|
+
cloneElement,
|
|
7
|
+
isValidElement,
|
|
8
|
+
type DependencyList,
|
|
9
|
+
type ReactElement,
|
|
10
|
+
} from "@lynx-js/react";
|
|
11
|
+
|
|
12
|
+
import { useIconColor } from "../../hooks/useIconColor";
|
|
13
|
+
import type { LynxIconElementProps, LynxStyledElementProps, LynxViewRef } from "../../types";
|
|
14
|
+
import { handleColor, handleDimension, type StyleProps } from "../../utils/styled";
|
|
15
|
+
|
|
16
|
+
export type IconSlotName = "icon" | "prefixIcon" | "suffixIcon";
|
|
17
|
+
|
|
18
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
19
|
+
|
|
20
|
+
interface IconSlotContextValue {
|
|
21
|
+
classNames?: Partial<Record<IconSlotName, string>>;
|
|
22
|
+
deps?: DependencyList;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const IconSlotContext = React.createContext<IconSlotContextValue | null>(null);
|
|
26
|
+
|
|
27
|
+
interface IconRequiredContextValue {
|
|
28
|
+
register: () => void;
|
|
29
|
+
unregister: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const IconRequiredContext = React.createContext<IconRequiredContextValue | null>(null);
|
|
33
|
+
|
|
34
|
+
export function IconRequired({
|
|
35
|
+
children,
|
|
36
|
+
enabled,
|
|
37
|
+
}: {
|
|
38
|
+
children: React.ReactNode;
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
}) {
|
|
41
|
+
const registeredCountRef = React.useRef(0);
|
|
42
|
+
const parentContext = React.useContext(IconRequiredContext);
|
|
43
|
+
|
|
44
|
+
const register = React.useCallback(() => {
|
|
45
|
+
registeredCountRef.current += 1;
|
|
46
|
+
|
|
47
|
+
if (process.env.NODE_ENV !== "production" && registeredCountRef.current > 1) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
"Icon-only Component must render only one <Icon /> under children. Check if you are rendering multiple <Icon />.",
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}, []);
|
|
53
|
+
|
|
54
|
+
const unregister = React.useCallback(() => {
|
|
55
|
+
registeredCountRef.current = Math.max(0, registeredCountRef.current - 1);
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
React.useLayoutEffect(() => {
|
|
59
|
+
if (!enabled) return;
|
|
60
|
+
|
|
61
|
+
if (process.env.NODE_ENV !== "production") {
|
|
62
|
+
if (parentContext) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
"Icon-only Component must not be nested within another Icon-Only. Check if you are using Icon-Only inside another Icon-Only.",
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (registeredCountRef.current === 0) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
"Icon-only Component must render <Icon /> as a child. Check if you are using raw icon element instead of <Icon icon={} />.",
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}, [enabled, parentContext]);
|
|
74
|
+
|
|
75
|
+
const value = React.useMemo<IconRequiredContextValue | null>(() => {
|
|
76
|
+
if (!enabled) return parentContext ?? null;
|
|
77
|
+
return { register, unregister };
|
|
78
|
+
}, [enabled, parentContext, register, unregister]);
|
|
79
|
+
|
|
80
|
+
return <IconRequiredContext.Provider value={value}>{children}</IconRequiredContext.Provider>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function IconSlotProvider({
|
|
84
|
+
value,
|
|
85
|
+
children,
|
|
86
|
+
}: {
|
|
87
|
+
value: IconSlotContextValue;
|
|
88
|
+
children: React.ReactNode;
|
|
89
|
+
}) {
|
|
90
|
+
return <IconSlotContext.Provider value={value}>{children}</IconSlotContext.Provider>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
94
|
+
|
|
95
|
+
const iconSlotMarker = Symbol.for("@seed-design/lynx-react/icon-slot");
|
|
96
|
+
|
|
97
|
+
interface IconSlotComponent {
|
|
98
|
+
[iconSlotMarker]?: IconSlotName;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function getIconSlotName(node: React.ReactNode): IconSlotName | null {
|
|
102
|
+
if (!isValidElement(node)) return null;
|
|
103
|
+
|
|
104
|
+
return ((node.type as IconSlotComponent)[iconSlotMarker] ?? null) as IconSlotName | null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
108
|
+
|
|
109
|
+
export interface IconProps extends LynxStyledElementProps {
|
|
110
|
+
icon: ReactElement<LynxIconElementProps>;
|
|
111
|
+
size?: StyleProps["height"] | number;
|
|
112
|
+
color?: StyleProps["color"];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface PrefixIconProps extends IconProps {}
|
|
116
|
+
|
|
117
|
+
export interface SuffixIconProps extends IconProps {}
|
|
118
|
+
|
|
119
|
+
interface IconSlotBaseProps extends IconProps {
|
|
120
|
+
slot: IconSlotName | null;
|
|
121
|
+
baseClassName?: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function mergeWrapperStyle({
|
|
125
|
+
size,
|
|
126
|
+
color,
|
|
127
|
+
style,
|
|
128
|
+
}: Pick<IconProps, "size" | "color" | "style">): CSSProperties | undefined {
|
|
129
|
+
const dimension = handleDimension(size);
|
|
130
|
+
const resolvedColor = handleColor(color);
|
|
131
|
+
|
|
132
|
+
if (dimension == null && resolvedColor == null) return style;
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
...(dimension != null ? { width: dimension, height: dimension } : {}),
|
|
136
|
+
...(resolvedColor != null ? { color: resolvedColor } : {}),
|
|
137
|
+
...style,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function getStyleColor(style: CSSProperties | undefined): CSSProperties["color"] {
|
|
142
|
+
return style?.color;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const IconSlotBase = React.forwardRef<unknown, IconSlotBaseProps>((props, ref) => {
|
|
146
|
+
const {
|
|
147
|
+
icon,
|
|
148
|
+
slot,
|
|
149
|
+
baseClassName,
|
|
150
|
+
className,
|
|
151
|
+
style,
|
|
152
|
+
size,
|
|
153
|
+
color,
|
|
154
|
+
children: _children,
|
|
155
|
+
...nativeProps
|
|
156
|
+
} = props;
|
|
157
|
+
const context = React.useContext(IconSlotContext);
|
|
158
|
+
const iconRequiredContext = React.useContext(IconRequiredContext);
|
|
159
|
+
const sourceRef = useMainThreadRef<MainThread.Element>(null);
|
|
160
|
+
const slotClassName = slot != null ? context?.classNames?.[slot] : undefined;
|
|
161
|
+
const styleColor = getStyleColor(style);
|
|
162
|
+
const iconColor = useIconColor(
|
|
163
|
+
[baseClassName, slotClassName, className, size, color, styleColor, ...(context?.deps ?? [])],
|
|
164
|
+
{ sourceRef },
|
|
165
|
+
);
|
|
166
|
+
const hasValidIcon = isValidElement<LynxIconElementProps>(icon);
|
|
167
|
+
|
|
168
|
+
React.useLayoutEffect(() => {
|
|
169
|
+
if (slot !== "icon" || !hasValidIcon) return;
|
|
170
|
+
|
|
171
|
+
iconRequiredContext?.register();
|
|
172
|
+
return () => {
|
|
173
|
+
iconRequiredContext?.unregister();
|
|
174
|
+
};
|
|
175
|
+
}, [hasValidIcon, iconRequiredContext, slot]);
|
|
176
|
+
|
|
177
|
+
if (!hasValidIcon) return null;
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<view
|
|
181
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
182
|
+
{...nativeProps}
|
|
183
|
+
main-thread:ref={sourceRef}
|
|
184
|
+
className={clsx(baseClassName, slotClassName, className)}
|
|
185
|
+
style={mergeWrapperStyle({ size, color, style })}
|
|
186
|
+
>
|
|
187
|
+
{cloneElement(icon, {
|
|
188
|
+
...iconColor,
|
|
189
|
+
className: icon.props.className,
|
|
190
|
+
style: {
|
|
191
|
+
...icon.props.style,
|
|
192
|
+
width: "100%",
|
|
193
|
+
height: "100%",
|
|
194
|
+
},
|
|
195
|
+
})}
|
|
196
|
+
</view>
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
IconSlotBase.displayName = "IconSlotBase";
|
|
200
|
+
|
|
201
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
202
|
+
|
|
203
|
+
function createIconComponent<Props extends IconProps>(
|
|
204
|
+
displayName: string,
|
|
205
|
+
slot: IconSlotName,
|
|
206
|
+
baseClassName: string,
|
|
207
|
+
) {
|
|
208
|
+
const Component = React.forwardRef<unknown, Props>((props, ref) => {
|
|
209
|
+
return <IconSlotBase ref={ref} slot={slot} baseClassName={baseClassName} {...props} />;
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
Component.displayName = displayName;
|
|
213
|
+
(Component as IconSlotComponent)[iconSlotMarker] = slot;
|
|
214
|
+
|
|
215
|
+
return Component;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export const Icon = createIconComponent<IconProps>("Icon", "icon", "seed-icon");
|
|
219
|
+
export const PrefixIcon = createIconComponent<PrefixIconProps>(
|
|
220
|
+
"PrefixIcon",
|
|
221
|
+
"prefixIcon",
|
|
222
|
+
"seed-prefix-icon",
|
|
223
|
+
);
|
|
224
|
+
export const SuffixIcon = createIconComponent<SuffixIconProps>(
|
|
225
|
+
"SuffixIcon",
|
|
226
|
+
"suffixIcon",
|
|
227
|
+
"seed-suffix-icon",
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
231
|
+
|
|
232
|
+
export interface InternalIconProps extends LynxStyledElementProps {
|
|
233
|
+
icon: ReactElement<LynxIconElementProps>;
|
|
234
|
+
deps?: DependencyList;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export const InternalIcon = React.forwardRef<unknown, InternalIconProps>((props, ref) => {
|
|
238
|
+
const { icon, deps = [], className, style, children: _children, ...nativeProps } = props;
|
|
239
|
+
const sourceRef = useMainThreadRef<MainThread.Element>(null);
|
|
240
|
+
const styleColor = getStyleColor(style);
|
|
241
|
+
const iconColor = useIconColor([className, styleColor, ...(deps ?? [])], { sourceRef });
|
|
242
|
+
|
|
243
|
+
if (!isValidElement<LynxIconElementProps>(icon)) return null;
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<view
|
|
247
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
248
|
+
{...nativeProps}
|
|
249
|
+
main-thread:ref={sourceRef}
|
|
250
|
+
className={className}
|
|
251
|
+
style={style}
|
|
252
|
+
>
|
|
253
|
+
{cloneElement(icon, {
|
|
254
|
+
...iconColor,
|
|
255
|
+
className: icon.props.className,
|
|
256
|
+
style: {
|
|
257
|
+
...icon.props.style,
|
|
258
|
+
width: "100%",
|
|
259
|
+
height: "100%",
|
|
260
|
+
},
|
|
261
|
+
})}
|
|
262
|
+
</view>
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
InternalIcon.displayName = "InternalIcon";
|