@seed-design/lynx-react 0.2.0-alpha.3 → 0.3.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 +26 -33
- package/lib/components/ActionButton/ActionButton.d.ts.map +1 -1
- package/lib/components/ActionButton/ActionButton.jsx +139 -120
- package/lib/components/ActionButton/index.d.ts +1 -1
- package/lib/components/AppBar/AppBar.d.ts +35 -0
- package/lib/components/AppBar/AppBar.d.ts.map +1 -0
- package/lib/components/AppBar/AppBar.jsx +132 -0
- package/lib/components/AppBar/AppBar.namespace.d.ts +2 -0
- package/lib/components/AppBar/AppBar.namespace.d.ts.map +1 -0
- package/lib/components/AppBar/AppBar.namespace.js +15 -0
- package/lib/components/AppBar/context.d.ts +13 -0
- package/lib/components/AppBar/context.d.ts.map +1 -0
- package/lib/components/AppBar/context.js +12 -0
- package/lib/components/AppBar/index.d.ts +3 -0
- package/lib/components/AppBar/index.d.ts.map +1 -0
- package/lib/components/AppBar/useAppBar.d.ts +15 -0
- package/lib/components/AppBar/useAppBar.d.ts.map +1 -0
- package/lib/components/AppBar/useAppBar.js +72 -0
- package/lib/components/Badge/Badge.d.ts +14 -0
- package/lib/components/Badge/Badge.d.ts.map +1 -0
- package/lib/components/Badge/Badge.jsx +15 -0
- package/lib/components/Badge/index.d.ts +2 -0
- package/lib/components/Badge/index.d.ts.map +1 -0
- package/lib/components/BottomSheet/BottomSheet.d.ts +22 -27
- package/lib/components/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/lib/components/BottomSheet/BottomSheet.jsx +127 -142
- package/lib/components/BottomSheet/BottomSheet.namespace.d.ts +1 -1
- package/lib/components/BottomSheet/BottomSheet.namespace.js +18 -1
- package/lib/components/BottomSheet/index.d.ts +2 -2
- 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 +14 -31
- package/lib/components/Checkbox/Checkbox.d.ts.map +1 -1
- package/lib/components/Checkbox/Checkbox.jsx +107 -98
- package/lib/components/Checkbox/Checkbox.namespace.d.ts +1 -1
- package/lib/components/Checkbox/Checkbox.namespace.js +12 -1
- package/lib/components/Checkbox/index.d.ts +2 -2
- 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 +4 -8
- package/lib/components/ProgressCircle/ProgressCircle.d.ts.map +1 -1
- package/lib/components/ProgressCircle/ProgressCircle.jsx +255 -260
- package/lib/components/ProgressCircle/ProgressCircle.namespace.d.ts +1 -1
- package/lib/components/ProgressCircle/ProgressCircle.namespace.js +9 -1
- package/lib/components/ProgressCircle/index.d.ts +2 -2
- package/lib/components/RadioGroup/RadioGroup.d.ts +12 -29
- package/lib/components/RadioGroup/RadioGroup.d.ts.map +1 -1
- package/lib/components/RadioGroup/RadioGroup.jsx +134 -118
- package/lib/components/RadioGroup/RadioGroup.namespace.d.ts +1 -1
- package/lib/components/RadioGroup/RadioGroup.namespace.js +12 -1
- package/lib/components/RadioGroup/index.d.ts +2 -2
- 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 +8 -14
- package/lib/components/Switch/Switch.d.ts.map +1 -1
- package/lib/components/Switch/Switch.jsx +82 -56
- package/lib/components/Switch/Switch.namespace.d.ts +1 -1
- package/lib/components/Switch/Switch.namespace.js +11 -1
- package/lib/components/Switch/index.d.ts +2 -2
- package/lib/components/TagGroup/TagGroup.d.ts +8 -14
- package/lib/components/TagGroup/TagGroup.d.ts.map +1 -1
- package/lib/components/TagGroup/TagGroup.jsx +42 -39
- package/lib/components/TagGroup/TagGroup.namespace.d.ts +1 -1
- package/lib/components/TagGroup/TagGroup.namespace.js +10 -1
- package/lib/components/TagGroup/index.d.ts +2 -2
- 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 +13 -7
- package/lib/components/index.d.ts.map +1 -1
- package/lib/hooks/index.d.ts +5 -3
- package/lib/hooks/index.d.ts.map +1 -1
- package/lib/hooks/useControllableState.js +23 -23
- package/lib/hooks/useIconColor.d.ts +6 -3
- package/lib/hooks/useIconColor.d.ts.map +1 -1
- package/lib/hooks/useIconColor.js +34 -37
- package/lib/hooks/usePressTap.d.ts +11 -8
- package/lib/hooks/usePressTap.d.ts.map +1 -1
- package/lib/hooks/usePressTap.js +31 -31
- 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/hooks/useSeedClassName.d.ts +27 -0
- package/lib/hooks/useSeedClassName.d.ts.map +1 -0
- package/lib/hooks/useSeedClassName.js +32 -0
- package/lib/index.d.ts +4 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +30 -3
- package/lib/types.d.ts +51 -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 +13 -12
- package/lib/utils/create-slot-recipe-context.d.ts.map +1 -1
- package/lib/utils/create-slot-recipe-context.jsx +88 -70
- package/lib/utils/get-seed-class-name.d.ts +9 -7
- package/lib/utils/get-seed-class-name.d.ts.map +1 -1
- package/lib/utils/get-seed-class-name.js +29 -37
- package/lib/utils/index.d.ts +4 -4
- package/lib/utils/resolve-recipe-token.js +45 -43
- package/lib/utils/split-multiple-variants-props.js +32 -32
- 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 +10 -9
- package/src/components/ActionButton/ActionButton.test.tsx +167 -0
- package/src/components/ActionButton/ActionButton.tsx +210 -169
- package/src/components/AppBar/AppBar.namespace.ts +18 -0
- package/src/components/AppBar/AppBar.test.tsx +270 -0
- package/src/components/AppBar/AppBar.tsx +288 -0
- package/src/components/AppBar/context.ts +27 -0
- package/src/components/AppBar/index.ts +21 -0
- package/src/components/AppBar/useAppBar.ts +100 -0
- package/src/components/Badge/Badge.test.tsx +54 -0
- package/src/components/Badge/Badge.tsx +32 -0
- package/src/components/Badge/index.ts +1 -0
- package/src/components/BottomSheet/BottomSheet.test.tsx +219 -0
- package/src/components/BottomSheet/BottomSheet.tsx +120 -83
- 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.tsx +106 -116
- package/src/components/Icon/Icon.tsx +265 -0
- package/src/components/Icon/index.ts +8 -0
- package/src/components/ProgressCircle/ProgressCircle.tsx +39 -25
- package/src/components/RadioGroup/RadioGroup.tsx +98 -117
- package/src/components/Stack/Stack.tsx +97 -0
- package/src/components/Stack/index.ts +1 -0
- package/src/components/Switch/Switch.tsx +59 -50
- package/src/components/TagGroup/TagGroup.tsx +27 -25
- package/src/components/Text/Text.tsx +64 -0
- package/src/components/Text/index.ts +1 -0
- package/src/components/index.ts +6 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/{__tests__/useControllableState.test.ts → useControllableState.test.ts} +1 -1
- package/src/hooks/{__tests__/useIconColor.test.ts → useIconColor.test.ts} +1 -1
- package/src/hooks/useIconColor.ts +22 -12
- package/src/hooks/{__tests__/usePressTap.test.ts → usePressTap.test.ts} +1 -1
- package/src/hooks/usePressTap.ts +14 -10
- package/src/hooks/useSafeArea.test.ts +83 -0
- package/src/hooks/useSafeArea.ts +56 -0
- package/src/hooks/useSeedClassName.ts +35 -0
- package/src/index.ts +1 -0
- package/src/types.ts +76 -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 +55 -15
- package/src/utils/get-seed-class-name.ts +10 -12
- package/src/utils/{__tests__/split-multiple-variants-props.test.ts → split-multiple-variants-props.test.ts} +1 -1
- package/src/utils/styled.ts +442 -0
- package/lib/components/ActionButton/index.js +0 -1
- package/lib/components/BottomSheet/index.js +0 -2
- package/lib/components/Checkbox/index.js +0 -2
- package/lib/components/ProgressCircle/index.js +0 -2
- package/lib/components/RadioGroup/index.js +0 -2
- package/lib/components/Switch/index.js +0 -2
- package/lib/components/TagGroup/index.js +0 -2
- package/lib/components/index.js +0 -7
- package/lib/hooks/index.js +0 -3
- package/lib/utils/index.js +0 -4
- package/src/components/ActionButton/__tests__/ActionButton.test.tsx +0 -35
- package/src/components/BottomSheet/__tests__/BottomSheet.test.tsx +0 -119
- package/src/components/__tests__/ProgressCircle.test.ts +0 -33
- package/src/components/__tests__/exports.test.ts +0 -38
- package/src/components/__tests__/switch.test.ts +0 -34
- package/src/utils/__tests__/create-slot-recipe-context.test.tsx +0 -228
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {
|
|
1
|
+
import * as React from "@lynx-js/react";
|
|
2
|
+
import { isValidElement, type ReactElement } from "@lynx-js/react";
|
|
3
3
|
import clsx from "clsx";
|
|
4
|
-
import type { MainThread } from "@lynx-js/types";
|
|
5
4
|
|
|
6
5
|
import { checkbox } from "@seed-design/lynx-css/recipes/checkbox";
|
|
7
6
|
import type { CheckboxVariantProps } from "@seed-design/lynx-css/recipes/checkbox";
|
|
8
7
|
import { checkmark } from "@seed-design/lynx-css/recipes/checkmark";
|
|
9
8
|
import type { CheckmarkVariantProps } from "@seed-design/lynx-css/recipes/checkmark";
|
|
10
9
|
import { checkboxGroup } from "@seed-design/lynx-css/recipes/checkbox-group";
|
|
11
|
-
import { vars as checkmarkVars } from "@seed-design/lynx-css/vars/component/checkmark";
|
|
12
10
|
|
|
13
11
|
import { useControllableState } from "../../hooks/useControllableState";
|
|
14
|
-
import { useIconColor } from "../../hooks/useIconColor";
|
|
15
12
|
import { usePressTap } from "../../hooks/usePressTap";
|
|
16
|
-
import {
|
|
13
|
+
import type {
|
|
14
|
+
LynxIconElementProps,
|
|
15
|
+
LynxStyledElementProps,
|
|
16
|
+
LynxTextRef,
|
|
17
|
+
LynxViewRef,
|
|
18
|
+
} from "../../types";
|
|
17
19
|
import { splitMultipleVariantsProps } from "../../utils/split-multiple-variants-props";
|
|
20
|
+
import { InternalIcon } from "../Icon/Icon";
|
|
18
21
|
|
|
19
22
|
/**
|
|
20
23
|
* @platform Lynx
|
|
@@ -30,20 +33,14 @@ import { splitMultipleVariantsProps } from "../../utils/split-multiple-variants-
|
|
|
30
33
|
* recipe 의 `color` 토큰을 `tint-color` 로 동기화한다. raw SVG 주입은 Lynx 범위 밖.
|
|
31
34
|
*/
|
|
32
35
|
|
|
33
|
-
type CheckboxSize = NonNullable<CheckboxVariantProps["size"]>;
|
|
34
|
-
type CheckboxWeight = NonNullable<CheckboxVariantProps["weight"]>;
|
|
35
|
-
type CheckmarkTone = NonNullable<CheckmarkVariantProps["tone"]>;
|
|
36
|
-
type CheckmarkVariant = NonNullable<CheckmarkVariantProps["variant"]>;
|
|
37
|
-
|
|
38
36
|
interface CheckboxContextValue {
|
|
39
37
|
checked: boolean;
|
|
40
38
|
indeterminate: boolean;
|
|
41
39
|
disabled: boolean;
|
|
42
40
|
pressed: boolean;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
variant: CheckmarkVariant;
|
|
41
|
+
checkboxVariantProps: CheckboxVariantProps;
|
|
42
|
+
checkmarkVariantProps: CheckmarkVariantProps;
|
|
43
|
+
toggle: () => void;
|
|
47
44
|
}
|
|
48
45
|
|
|
49
46
|
const CheckboxContext = React.createContext<CheckboxContextValue | null>(null);
|
|
@@ -56,10 +53,15 @@ function useCheckboxContext(consumer: string): CheckboxContextValue {
|
|
|
56
53
|
return ctx;
|
|
57
54
|
}
|
|
58
55
|
|
|
59
|
-
|
|
56
|
+
interface CheckmarkControlContextValue {
|
|
57
|
+
iconClassName: string;
|
|
58
|
+
checkmarkVariantProps: CheckmarkVariantProps;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const CheckmarkControlContext = React.createContext<CheckmarkControlContextValue | null>(null);
|
|
60
62
|
|
|
61
|
-
function
|
|
62
|
-
const ctx = React.useContext(
|
|
63
|
+
function useCheckmarkControlContext(consumer: string): CheckmarkControlContextValue {
|
|
64
|
+
const ctx = React.useContext(CheckmarkControlContext);
|
|
63
65
|
if (!ctx) {
|
|
64
66
|
throw new Error(`<${consumer}/> must be rendered inside <CheckboxControl/>.`);
|
|
65
67
|
}
|
|
@@ -70,9 +72,8 @@ function useCheckmarkIconClass(consumer: string): string {
|
|
|
70
72
|
|
|
71
73
|
export interface CheckboxRootProps
|
|
72
74
|
extends CheckboxVariantProps,
|
|
73
|
-
Omit<CheckmarkVariantProps, "size" | "checked" | "disabled" | "indeterminate"
|
|
74
|
-
|
|
75
|
-
className?: string;
|
|
75
|
+
Omit<CheckmarkVariantProps, "size" | "checked" | "disabled" | "indeterminate">,
|
|
76
|
+
LynxStyledElementProps {
|
|
76
77
|
checked?: boolean;
|
|
77
78
|
defaultChecked?: boolean;
|
|
78
79
|
indeterminate?: boolean;
|
|
@@ -93,10 +94,6 @@ export const CheckboxRoot = React.forwardRef<unknown, CheckboxRootProps>((props,
|
|
|
93
94
|
} = props;
|
|
94
95
|
const [{ checkbox: checkboxVariantProps, checkmark: checkmarkVariantProps }, nativeProps] =
|
|
95
96
|
splitMultipleVariantsProps(restProps, { checkbox, checkmark });
|
|
96
|
-
const weight: CheckboxWeight = checkboxVariantProps.weight ?? "regular";
|
|
97
|
-
const size: CheckboxSize = checkboxVariantProps.size ?? "medium";
|
|
98
|
-
const tone: CheckmarkTone = checkmarkVariantProps.tone ?? "brand";
|
|
99
|
-
const variant: CheckmarkVariant = checkmarkVariantProps.variant ?? "square";
|
|
100
97
|
|
|
101
98
|
const [checked, setChecked] = useControllableState({
|
|
102
99
|
value: checkedProp,
|
|
@@ -104,22 +101,40 @@ export const CheckboxRoot = React.forwardRef<unknown, CheckboxRootProps>((props,
|
|
|
104
101
|
onChange: onCheckedChange,
|
|
105
102
|
});
|
|
106
103
|
|
|
104
|
+
const toggle = React.useCallback(() => setChecked(!checked), [checked, setChecked]);
|
|
105
|
+
|
|
107
106
|
const { pressed, ...pressHandlers } = usePressTap({
|
|
108
107
|
disabled,
|
|
109
|
-
onTap:
|
|
108
|
+
onTap: toggle,
|
|
110
109
|
});
|
|
111
110
|
|
|
112
|
-
const rootClassName = checkbox({
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
() => ({
|
|
116
|
-
|
|
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
|
+
],
|
|
117
132
|
);
|
|
118
133
|
|
|
119
134
|
return (
|
|
120
|
-
<CheckboxContext.Provider value={
|
|
135
|
+
<CheckboxContext.Provider value={contextValue}>
|
|
121
136
|
<view
|
|
122
|
-
{...(ref ? { ref: ref as
|
|
137
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
123
138
|
className={clsx(rootClassName, className)}
|
|
124
139
|
{...pressHandlers}
|
|
125
140
|
{...nativeProps}
|
|
@@ -134,65 +149,49 @@ CheckboxRoot.displayName = "CheckboxRoot";
|
|
|
134
149
|
////////////////////////////////////////////////////////////////////////////////////
|
|
135
150
|
|
|
136
151
|
export interface CheckboxControlProps
|
|
137
|
-
extends Pick<CheckmarkVariantProps, "tone" | "variant" | "size"
|
|
138
|
-
|
|
139
|
-
className?: string;
|
|
140
|
-
}
|
|
152
|
+
extends Pick<CheckmarkVariantProps, "tone" | "variant" | "size">,
|
|
153
|
+
LynxStyledElementProps {}
|
|
141
154
|
|
|
142
155
|
export const CheckboxControl = React.forwardRef<unknown, CheckboxControlProps>((props, ref) => {
|
|
143
156
|
const [variantProps, restProps] = checkmark.splitVariantProps(props);
|
|
144
157
|
const { children, className, ...nativeProps } = restProps;
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const tone: CheckmarkTone = variantProps.tone ?? ctxTone;
|
|
156
|
-
const variant: CheckmarkVariant = variantProps.variant ?? ctxVariant;
|
|
157
|
-
const size: CheckboxSize = (variantProps.size as CheckboxSize | undefined) ?? ctxSize;
|
|
158
|
-
|
|
159
|
-
const classes = React.useMemo(
|
|
160
|
-
() => checkmark({ variant, tone, size, checked, disabled, indeterminate, pressed }),
|
|
161
|
-
[variant, tone, size, checked, disabled, indeterminate, pressed],
|
|
162
|
-
);
|
|
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);
|
|
163
168
|
|
|
164
169
|
return (
|
|
165
|
-
<
|
|
170
|
+
<CheckmarkControlContext.Provider
|
|
171
|
+
value={{ iconClassName: classes.icon, checkmarkVariantProps }}
|
|
172
|
+
>
|
|
166
173
|
<view
|
|
167
|
-
{...(ref ? { ref: ref as
|
|
174
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
168
175
|
className={clsx(classes.root, className)}
|
|
169
176
|
{...nativeProps}
|
|
170
177
|
>
|
|
171
178
|
{children}
|
|
172
179
|
</view>
|
|
173
|
-
</
|
|
180
|
+
</CheckmarkControlContext.Provider>
|
|
174
181
|
);
|
|
175
182
|
});
|
|
176
183
|
CheckboxControl.displayName = "CheckboxControl";
|
|
177
184
|
|
|
178
185
|
////////////////////////////////////////////////////////////////////////////////////
|
|
179
186
|
|
|
180
|
-
|
|
181
|
-
className
|
|
182
|
-
style?: CSSProperties;
|
|
183
|
-
ref?: React.Ref<MainThread.Element>;
|
|
184
|
-
"main-thread:binduiappear"?: () => void;
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
export interface CheckboxIndicatorProps {
|
|
187
|
+
export interface CheckboxIndicatorProps
|
|
188
|
+
extends Pick<LynxStyledElementProps, "className" | "style"> {
|
|
188
189
|
/** Icon rendered when neither checked nor indeterminate. Optional. */
|
|
189
|
-
unchecked?: ReactElement<
|
|
190
|
+
unchecked?: ReactElement<LynxIconElementProps>;
|
|
190
191
|
/** Icon rendered when `checked=true` and `indeterminate=false`. */
|
|
191
|
-
checked: ReactElement<
|
|
192
|
+
checked: ReactElement<LynxIconElementProps>;
|
|
192
193
|
/** Icon rendered when `indeterminate=true`. */
|
|
193
|
-
indeterminate?: ReactElement<
|
|
194
|
-
className?: string;
|
|
195
|
-
style?: CSSProperties;
|
|
194
|
+
indeterminate?: ReactElement<LynxIconElementProps>;
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
export function CheckboxIndicator(props: CheckboxIndicatorProps) {
|
|
@@ -203,62 +202,56 @@ export function CheckboxIndicator(props: CheckboxIndicatorProps) {
|
|
|
203
202
|
className,
|
|
204
203
|
style,
|
|
205
204
|
} = props;
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
const iconClassName = useCheckmarkIconClass("CheckboxIndicator");
|
|
209
|
-
const iconColor = useIconColor([
|
|
210
|
-
checked,
|
|
211
|
-
indeterminate,
|
|
212
|
-
disabled,
|
|
213
|
-
pressed,
|
|
214
|
-
tone,
|
|
215
|
-
variant,
|
|
216
|
-
size,
|
|
217
|
-
]);
|
|
205
|
+
const context = useCheckboxContext("CheckboxIndicator");
|
|
206
|
+
const { iconClassName, checkmarkVariantProps } = useCheckmarkControlContext("CheckboxIndicator");
|
|
218
207
|
|
|
219
|
-
if (process.env.NODE_ENV !== "production" && indeterminate && !indeterminateIcon) {
|
|
208
|
+
if (process.env.NODE_ENV !== "production" && context.indeterminate && !indeterminateIcon) {
|
|
220
209
|
console.warn(
|
|
221
210
|
"[seed-design] CheckboxIndicator: `indeterminate` prop must be provided when the checkbox is in an indeterminate state.",
|
|
222
211
|
);
|
|
223
212
|
}
|
|
224
213
|
|
|
225
|
-
const icon = indeterminate
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
+
);
|
|
244
237
|
}
|
|
245
238
|
CheckboxIndicator.displayName = "CheckboxIndicator";
|
|
246
239
|
|
|
247
240
|
////////////////////////////////////////////////////////////////////////////////////
|
|
248
241
|
|
|
249
|
-
export interface CheckboxLabelProps {
|
|
250
|
-
children?: React.ReactNode;
|
|
251
|
-
className?: string;
|
|
252
|
-
}
|
|
242
|
+
export interface CheckboxLabelProps extends LynxStyledElementProps {}
|
|
253
243
|
|
|
254
244
|
export const CheckboxLabel = React.forwardRef<unknown, CheckboxLabelProps>((props, ref) => {
|
|
255
245
|
const { children, className, ...nativeProps } = props;
|
|
256
|
-
const
|
|
257
|
-
const labelClassName = checkbox({
|
|
246
|
+
const context = useCheckboxContext("CheckboxLabel");
|
|
247
|
+
const labelClassName = checkbox({
|
|
248
|
+
...context.checkboxVariantProps,
|
|
249
|
+
disabled: context.disabled,
|
|
250
|
+
}).label;
|
|
258
251
|
|
|
259
252
|
return (
|
|
260
253
|
<text
|
|
261
|
-
{...(ref ? { ref: ref as
|
|
254
|
+
{...(ref ? { ref: ref as LynxTextRef } : {})}
|
|
262
255
|
className={clsx(labelClassName, className)}
|
|
263
256
|
{...nativeProps}
|
|
264
257
|
>
|
|
@@ -270,10 +263,7 @@ CheckboxLabel.displayName = "CheckboxLabel";
|
|
|
270
263
|
|
|
271
264
|
////////////////////////////////////////////////////////////////////////////////////
|
|
272
265
|
|
|
273
|
-
export interface CheckboxGroupProps {
|
|
274
|
-
children?: React.ReactNode;
|
|
275
|
-
className?: string;
|
|
276
|
-
}
|
|
266
|
+
export interface CheckboxGroupProps extends LynxStyledElementProps {}
|
|
277
267
|
|
|
278
268
|
export const CheckboxGroup = React.forwardRef<unknown, CheckboxGroupProps>((props, ref) => {
|
|
279
269
|
const { children, className, ...nativeProps } = props;
|
|
@@ -281,8 +271,8 @@ export const CheckboxGroup = React.forwardRef<unknown, CheckboxGroupProps>((prop
|
|
|
281
271
|
|
|
282
272
|
return (
|
|
283
273
|
<view
|
|
284
|
-
{...(ref ? { ref: ref as
|
|
285
|
-
className={clsx(classes, className)}
|
|
274
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
275
|
+
className={clsx(classes.root, className)}
|
|
286
276
|
{...nativeProps}
|
|
287
277
|
>
|
|
288
278
|
{children}
|
|
@@ -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";
|