@seed-design/lynx-react 0.2.0-alpha.2 → 0.2.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 -32
- 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 -30
- 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 -28
- 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 +4 -3
- package/lib/hooks/index.d.ts.map +1 -1
- package/lib/hooks/{use-controllable-state.d.ts → useControllableState.d.ts} +1 -1
- 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/{use-press-tap.d.ts → usePressTap.d.ts} +12 -9
- 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 +4 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +29 -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 +6 -7
- package/lib/utils/get-seed-class-name.d.ts.map +1 -1
- package/lib/utils/get-seed-class-name.js +26 -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 +9 -9
- package/src/components/ActionButton/ActionButton.test.tsx +167 -0
- package/src/components/ActionButton/ActionButton.tsx +210 -168
- 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 +108 -117
- 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 +100 -118
- package/src/components/Stack/Stack.tsx +97 -0
- package/src/components/Stack/index.ts +1 -0
- package/src/components/Switch/Switch.tsx +61 -52
- 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 +4 -3
- package/src/hooks/{__tests__/use-controllable-state.test.ts → useControllableState.test.ts} +1 -1
- package/src/hooks/useIconColor.test.ts +13 -0
- package/src/hooks/useIconColor.ts +67 -0
- package/src/hooks/{__tests__/use-press-tap.test.ts → usePressTap.test.ts} +1 -1
- package/src/hooks/{use-press-tap.ts → usePressTap.ts} +14 -10
- package/src/hooks/useSafeArea.test.ts +83 -0
- package/src/hooks/useSafeArea.ts +56 -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 +7 -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/hooks/use-controllable-state.d.ts.map +0 -1
- package/lib/hooks/use-controllable-state.js +0 -27
- package/lib/hooks/use-icon-color.d.ts +0 -15
- package/lib/hooks/use-icon-color.d.ts.map +0 -1
- package/lib/hooks/use-icon-color.js +0 -37
- package/lib/hooks/use-press-tap.d.ts.map +0 -1
- package/lib/hooks/use-press-tap.js +0 -35
- 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/hooks/use-icon-color.ts +0 -51
- package/src/utils/__tests__/create-slot-recipe-context.test.tsx +0 -228
- /package/src/hooks/{use-controllable-state.ts → useControllableState.ts} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type * as React from "@lynx-js/react";
|
|
2
2
|
import {
|
|
3
3
|
createContext,
|
|
4
|
+
forwardRef,
|
|
4
5
|
runOnMainThread,
|
|
5
6
|
useContext,
|
|
6
7
|
useEffect,
|
|
@@ -11,12 +12,11 @@ import type { MainThread } from "@lynx-js/types";
|
|
|
11
12
|
import clsx from "clsx";
|
|
12
13
|
import { progressCircle } from "@seed-design/lynx-css/recipes/progress-circle";
|
|
13
14
|
import type { ProgressCircleVariantProps } from "@seed-design/lynx-css/recipes/progress-circle";
|
|
15
|
+
import type { LynxStyledElementProps, LynxViewRef } from "../../types";
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
type Size = "14" | "16" | "18" | "24" | "40";
|
|
17
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
17
18
|
|
|
18
19
|
interface ProgressCircleContextValue {
|
|
19
|
-
tone: Tone;
|
|
20
20
|
numSize: number;
|
|
21
21
|
isDeterminate: boolean;
|
|
22
22
|
progress: number;
|
|
@@ -25,7 +25,7 @@ interface ProgressCircleContextValue {
|
|
|
25
25
|
|
|
26
26
|
const ProgressCircleContext = createContext<ProgressCircleContextValue | null>(null);
|
|
27
27
|
|
|
28
|
-
function
|
|
28
|
+
function useProgressCircleContext() {
|
|
29
29
|
const ctx = useContext(ProgressCircleContext);
|
|
30
30
|
if (!ctx) {
|
|
31
31
|
throw new Error("ProgressCircle compound components must be used within ProgressCircle.Root");
|
|
@@ -33,6 +33,8 @@ function useProgressCircleCtx() {
|
|
|
33
33
|
return ctx;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
37
|
+
|
|
36
38
|
// --- Background-thread-only utilities (for initial render) ---
|
|
37
39
|
|
|
38
40
|
function computeRingGeometry(numSize: number) {
|
|
@@ -55,6 +57,8 @@ function bgPieClipPath(size: number, angleDeg: number): string | undefined {
|
|
|
55
57
|
return `path("M ${c} ${c} L ${c} ${c - r} A ${r} ${r} 0 ${largeArc} 1 ${endX} ${endY} Z")`;
|
|
56
58
|
}
|
|
57
59
|
|
|
60
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
61
|
+
|
|
58
62
|
// --- Main-thread-only utilities (for animations) ---
|
|
59
63
|
// Compiled into the main thread bundle. Must NOT be called from render code.
|
|
60
64
|
|
|
@@ -104,23 +108,29 @@ function sampleIndeterminate(t: number) {
|
|
|
104
108
|
return { containerDeg, arcLength };
|
|
105
109
|
}
|
|
106
110
|
|
|
111
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
112
|
+
|
|
107
113
|
// --- Animation constants ---
|
|
108
114
|
|
|
109
115
|
const INDETERMINATE_DURATION = 1200;
|
|
110
116
|
const TRANSITION_DURATION = 300;
|
|
111
117
|
|
|
118
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
119
|
+
|
|
112
120
|
// --- Components ---
|
|
113
121
|
|
|
114
|
-
export interface ProgressCircleRootProps
|
|
122
|
+
export interface ProgressCircleRootProps
|
|
123
|
+
extends ProgressCircleVariantProps,
|
|
124
|
+
LynxStyledElementProps {
|
|
115
125
|
minValue?: number;
|
|
116
126
|
maxValue?: number;
|
|
117
127
|
value?: number;
|
|
118
|
-
children?: React.ReactNode;
|
|
119
|
-
className?: string;
|
|
120
128
|
}
|
|
121
129
|
|
|
122
130
|
export type RootProps = ProgressCircleRootProps;
|
|
123
131
|
|
|
132
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
133
|
+
|
|
124
134
|
/**
|
|
125
135
|
* Lynx에서 SVG를 사용할 수 없어 CSS clip-path 기반 pie sector로 구현.
|
|
126
136
|
*
|
|
@@ -131,42 +141,42 @@ export type RootProps = ProgressCircleRootProps;
|
|
|
131
141
|
*
|
|
132
142
|
* Lynx SVG + stroke-dasharray 지원 시 CSS-only 애니메이션으로 전환 예정.
|
|
133
143
|
*/
|
|
134
|
-
export const ProgressCircleRoot = (props
|
|
135
|
-
const
|
|
136
|
-
const
|
|
144
|
+
export const ProgressCircleRoot = forwardRef<unknown, ProgressCircleRootProps>((props, ref) => {
|
|
145
|
+
const [variantProps, otherProps] = progressCircle.splitVariantProps(props);
|
|
146
|
+
const { children, className, style, minValue, maxValue, value } = otherProps;
|
|
147
|
+
const size = variantProps.size ?? "40";
|
|
148
|
+
const tone = variantProps.tone ?? "neutral";
|
|
137
149
|
const numSize = Number(size);
|
|
138
150
|
|
|
139
|
-
const isDeterminate =
|
|
140
|
-
props.minValue !== undefined && props.maxValue !== undefined && props.value !== undefined;
|
|
151
|
+
const isDeterminate = minValue !== undefined && maxValue !== undefined && value !== undefined;
|
|
141
152
|
|
|
142
|
-
const range = (
|
|
143
|
-
const progress = isDeterminate
|
|
144
|
-
? range === 0
|
|
145
|
-
? 0
|
|
146
|
-
: ((props.value ?? 0) - (props.minValue ?? 0)) / range
|
|
147
|
-
: 0;
|
|
153
|
+
const range = (maxValue ?? 1) - (minValue ?? 0);
|
|
154
|
+
const progress = isDeterminate ? (range === 0 ? 0 : ((value ?? 0) - (minValue ?? 0)) / range) : 0;
|
|
148
155
|
|
|
149
156
|
const classes = progressCircle({ tone, size });
|
|
150
157
|
|
|
151
158
|
const ctx = useMemo(
|
|
152
|
-
() => ({
|
|
153
|
-
[
|
|
159
|
+
() => ({ numSize, isDeterminate, progress, classes }),
|
|
160
|
+
[numSize, isDeterminate, progress, classes],
|
|
154
161
|
);
|
|
155
162
|
|
|
156
163
|
return (
|
|
157
164
|
<ProgressCircleContext.Provider value={ctx}>
|
|
158
165
|
<view
|
|
159
|
-
|
|
160
|
-
|
|
166
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
167
|
+
className={clsx(classes.root, className)}
|
|
168
|
+
style={{ ...style, width: `${numSize}px`, height: `${numSize}px` }}
|
|
161
169
|
>
|
|
162
|
-
{
|
|
170
|
+
{children}
|
|
163
171
|
</view>
|
|
164
172
|
</ProgressCircleContext.Provider>
|
|
165
173
|
);
|
|
166
|
-
};
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
167
177
|
|
|
168
178
|
export const ProgressCircleRange = () => {
|
|
169
|
-
const { numSize, isDeterminate, progress, classes } =
|
|
179
|
+
const { numSize, isDeterminate, progress, classes } = useProgressCircleContext();
|
|
170
180
|
|
|
171
181
|
if (!isDeterminate) {
|
|
172
182
|
return <IndeterminateRange numSize={numSize} classes={classes} />;
|
|
@@ -177,6 +187,8 @@ export const ProgressCircleRange = () => {
|
|
|
177
187
|
|
|
178
188
|
type Classes = ReturnType<typeof progressCircle>;
|
|
179
189
|
|
|
190
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
191
|
+
|
|
180
192
|
function DeterminateRange({
|
|
181
193
|
numSize,
|
|
182
194
|
progress,
|
|
@@ -300,6 +312,8 @@ function DeterminateRange({
|
|
|
300
312
|
);
|
|
301
313
|
}
|
|
302
314
|
|
|
315
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
316
|
+
|
|
303
317
|
function IndeterminateRange({ numSize, classes }: { numSize: number; classes: Classes }) {
|
|
304
318
|
const containerRef = useMainThreadRef<MainThread.Element>(null);
|
|
305
319
|
const rangeRef = useMainThreadRef<MainThread.Element>(null);
|
|
@@ -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 { radio } from "@seed-design/lynx-css/recipes/radio";
|
|
7
6
|
import type { RadioVariantProps } from "@seed-design/lynx-css/recipes/radio";
|
|
8
7
|
import { radiomark } from "@seed-design/lynx-css/recipes/radiomark";
|
|
9
8
|
import type { RadiomarkVariantProps } from "@seed-design/lynx-css/recipes/radiomark";
|
|
10
9
|
import { radioGroup } from "@seed-design/lynx-css/recipes/radio-group";
|
|
11
|
-
import { vars as radiomarkVars } from "@seed-design/lynx-css/vars/component/radiomark";
|
|
12
10
|
|
|
13
|
-
import { useControllableState } from "../../hooks/
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
|
|
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";
|
|
17
19
|
import { splitMultipleVariantsProps } from "../../utils/split-multiple-variants-props";
|
|
20
|
+
import { InternalIcon } from "../Icon/Icon";
|
|
18
21
|
|
|
19
22
|
/**
|
|
20
23
|
* @platform Lynx
|
|
@@ -29,17 +32,12 @@ import { splitMultipleVariantsProps } from "../../utils/split-multiple-variants-
|
|
|
29
32
|
* recipe 의 `color` 토큰을 `tint-color` 로 동기화한다.
|
|
30
33
|
*/
|
|
31
34
|
|
|
32
|
-
type RadioSize = NonNullable<RadioVariantProps["size"]>;
|
|
33
|
-
type RadioWeight = NonNullable<RadioVariantProps["weight"]>;
|
|
34
|
-
type RadiomarkTone = NonNullable<RadiomarkVariantProps["tone"]>;
|
|
35
|
-
|
|
36
35
|
interface RadioGroupContextValue {
|
|
37
36
|
value: string | null;
|
|
38
37
|
setValue: (value: string) => void;
|
|
39
38
|
disabled: boolean;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
tone: RadiomarkTone;
|
|
39
|
+
radioVariantProps: RadioVariantProps;
|
|
40
|
+
radiomarkVariantProps: RadiomarkVariantProps;
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
const RadioGroupContext = React.createContext<RadioGroupContextValue | null>(null);
|
|
@@ -57,8 +55,7 @@ interface RadioGroupItemContextValue {
|
|
|
57
55
|
checked: boolean;
|
|
58
56
|
disabled: boolean;
|
|
59
57
|
pressed: boolean;
|
|
60
|
-
|
|
61
|
-
size: RadioSize;
|
|
58
|
+
select: () => void;
|
|
62
59
|
}
|
|
63
60
|
|
|
64
61
|
const RadioGroupItemContext = React.createContext<RadioGroupItemContextValue | null>(null);
|
|
@@ -71,10 +68,15 @@ function useRadioGroupItemContext(consumer: string): RadioGroupItemContextValue
|
|
|
71
68
|
return ctx;
|
|
72
69
|
}
|
|
73
70
|
|
|
74
|
-
|
|
71
|
+
interface RadiomarkControlContextValue {
|
|
72
|
+
iconClassName: string;
|
|
73
|
+
radiomarkVariantProps: RadiomarkVariantProps;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const RadiomarkControlContext = React.createContext<RadiomarkControlContextValue | null>(null);
|
|
75
77
|
|
|
76
|
-
function
|
|
77
|
-
const ctx = React.useContext(
|
|
78
|
+
function useRadiomarkControlContext(consumer: string): RadiomarkControlContextValue {
|
|
79
|
+
const ctx = React.useContext(RadiomarkControlContext);
|
|
78
80
|
if (!ctx) {
|
|
79
81
|
throw new Error(`<${consumer}/> must be rendered inside <RadioGroupItemControl/>.`);
|
|
80
82
|
}
|
|
@@ -85,9 +87,8 @@ function useRadiomarkIconClass(consumer: string): string {
|
|
|
85
87
|
|
|
86
88
|
export interface RadioGroupRootProps
|
|
87
89
|
extends RadioVariantProps,
|
|
88
|
-
Omit<RadiomarkVariantProps, "size" | "checked" | "disabled"
|
|
89
|
-
|
|
90
|
-
className?: string;
|
|
90
|
+
Omit<RadiomarkVariantProps, "size" | "checked" | "disabled">,
|
|
91
|
+
LynxStyledElementProps {
|
|
91
92
|
value?: string;
|
|
92
93
|
defaultValue?: string;
|
|
93
94
|
disabled?: boolean;
|
|
@@ -106,9 +107,6 @@ export const RadioGroupRoot = React.forwardRef<unknown, RadioGroupRootProps>((pr
|
|
|
106
107
|
} = props;
|
|
107
108
|
const [{ radio: radioVariantProps, radiomark: radiomarkVariantProps }, nativeProps] =
|
|
108
109
|
splitMultipleVariantsProps(restProps, { radio, radiomark });
|
|
109
|
-
const weight: RadioWeight = radioVariantProps.weight ?? "regular";
|
|
110
|
-
const size: RadioSize = radioVariantProps.size ?? "medium";
|
|
111
|
-
const tone: RadiomarkTone = radiomarkVariantProps.tone ?? "brand";
|
|
112
110
|
|
|
113
111
|
const handleChange = React.useCallback(
|
|
114
112
|
(next: string | null) => {
|
|
@@ -130,17 +128,23 @@ export const RadioGroupRoot = React.forwardRef<unknown, RadioGroupRootProps>((pr
|
|
|
130
128
|
[setValueInternal],
|
|
131
129
|
);
|
|
132
130
|
|
|
133
|
-
const rootClassName = radioGroup();
|
|
131
|
+
const rootClassName = radioGroup().root;
|
|
134
132
|
|
|
135
|
-
const
|
|
136
|
-
() => ({
|
|
137
|
-
|
|
133
|
+
const contextValue = React.useMemo<RadioGroupContextValue>(
|
|
134
|
+
() => ({
|
|
135
|
+
value,
|
|
136
|
+
setValue,
|
|
137
|
+
disabled,
|
|
138
|
+
radioVariantProps,
|
|
139
|
+
radiomarkVariantProps,
|
|
140
|
+
}),
|
|
141
|
+
[value, setValue, disabled, radioVariantProps, radiomarkVariantProps],
|
|
138
142
|
);
|
|
139
143
|
|
|
140
144
|
return (
|
|
141
|
-
<RadioGroupContext.Provider value={
|
|
145
|
+
<RadioGroupContext.Provider value={contextValue}>
|
|
142
146
|
<view
|
|
143
|
-
{...(ref ? { ref: ref as
|
|
147
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
144
148
|
className={clsx(rootClassName, className)}
|
|
145
149
|
{...nativeProps}
|
|
146
150
|
>
|
|
@@ -153,10 +157,8 @@ RadioGroupRoot.displayName = "RadioGroupRoot";
|
|
|
153
157
|
|
|
154
158
|
////////////////////////////////////////////////////////////////////////////////////
|
|
155
159
|
|
|
156
|
-
export interface RadioGroupItemProps {
|
|
160
|
+
export interface RadioGroupItemProps extends LynxStyledElementProps {
|
|
157
161
|
value: string;
|
|
158
|
-
children?: React.ReactNode;
|
|
159
|
-
className?: string;
|
|
160
162
|
disabled?: boolean;
|
|
161
163
|
}
|
|
162
164
|
|
|
@@ -168,44 +170,37 @@ export const RadioGroupItem = React.forwardRef<unknown, RadioGroupItemProps>((pr
|
|
|
168
170
|
className,
|
|
169
171
|
...nativeProps
|
|
170
172
|
} = props;
|
|
171
|
-
const
|
|
172
|
-
value: groupValue,
|
|
173
|
-
setValue,
|
|
174
|
-
disabled: groupDisabled,
|
|
175
|
-
size,
|
|
176
|
-
weight,
|
|
177
|
-
tone,
|
|
178
|
-
} = useRadioGroupContext("RadioGroupItem");
|
|
173
|
+
const groupContext = useRadioGroupContext("RadioGroupItem");
|
|
179
174
|
|
|
180
|
-
const disabled =
|
|
181
|
-
const checked =
|
|
175
|
+
const disabled = groupContext.disabled || itemDisabled;
|
|
176
|
+
const checked = groupContext.value === itemValue;
|
|
177
|
+
const select = React.useCallback(() => {
|
|
178
|
+
if (checked) return;
|
|
179
|
+
groupContext.setValue(itemValue);
|
|
180
|
+
}, [checked, groupContext, itemValue]);
|
|
182
181
|
|
|
183
182
|
const { pressed, ...pressHandlers } = usePressTap({
|
|
184
183
|
disabled,
|
|
185
|
-
onTap:
|
|
186
|
-
if (checked) return;
|
|
187
|
-
setValue(itemValue);
|
|
188
|
-
},
|
|
184
|
+
onTap: select,
|
|
189
185
|
});
|
|
190
186
|
|
|
191
|
-
const rootClassName = radio({
|
|
187
|
+
const rootClassName = radio({ ...groupContext.radioVariantProps, disabled }).root;
|
|
192
188
|
|
|
193
|
-
const
|
|
189
|
+
const itemContextValue = React.useMemo<RadioGroupItemContextValue>(
|
|
194
190
|
() => ({
|
|
195
191
|
value: itemValue,
|
|
196
192
|
checked,
|
|
197
193
|
disabled,
|
|
198
194
|
pressed,
|
|
199
|
-
|
|
200
|
-
size,
|
|
195
|
+
select,
|
|
201
196
|
}),
|
|
202
|
-
[itemValue, checked, disabled, pressed,
|
|
197
|
+
[itemValue, checked, disabled, pressed, select],
|
|
203
198
|
);
|
|
204
199
|
|
|
205
200
|
return (
|
|
206
|
-
<RadioGroupItemContext.Provider value={
|
|
201
|
+
<RadioGroupItemContext.Provider value={itemContextValue}>
|
|
207
202
|
<view
|
|
208
|
-
{...(ref ? { ref: ref as
|
|
203
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
209
204
|
className={clsx(rootClassName, className)}
|
|
210
205
|
{...pressHandlers}
|
|
211
206
|
{...nativeProps}
|
|
@@ -219,40 +214,37 @@ RadioGroupItem.displayName = "RadioGroupItem";
|
|
|
219
214
|
|
|
220
215
|
////////////////////////////////////////////////////////////////////////////////////
|
|
221
216
|
|
|
222
|
-
export interface RadioGroupItemControlProps
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
217
|
+
export interface RadioGroupItemControlProps
|
|
218
|
+
extends Pick<RadiomarkVariantProps, "tone">,
|
|
219
|
+
LynxStyledElementProps {}
|
|
226
220
|
|
|
227
221
|
export const RadioGroupItemControl = React.forwardRef<unknown, RadioGroupItemControlProps>(
|
|
228
222
|
(props, ref) => {
|
|
229
223
|
const [variantProps, restProps] = radiomark.splitVariantProps(props);
|
|
230
224
|
const { children, className, ...nativeProps } = restProps;
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const classes = React.useMemo(
|
|
242
|
-
() => radiomark({ tone, size, checked, disabled, pressed }),
|
|
243
|
-
[tone, size, checked, disabled, pressed],
|
|
244
|
-
);
|
|
225
|
+
const groupContext = useRadioGroupContext("RadioGroupItemControl");
|
|
226
|
+
const itemContext = useRadioGroupItemContext("RadioGroupItemControl");
|
|
227
|
+
const radiomarkVariantProps: RadiomarkVariantProps = {
|
|
228
|
+
...groupContext.radiomarkVariantProps,
|
|
229
|
+
...variantProps,
|
|
230
|
+
checked: itemContext.checked,
|
|
231
|
+
disabled: itemContext.disabled,
|
|
232
|
+
pressed: itemContext.pressed,
|
|
233
|
+
};
|
|
234
|
+
const classes = radiomark(radiomarkVariantProps);
|
|
245
235
|
|
|
246
236
|
return (
|
|
247
|
-
<
|
|
237
|
+
<RadiomarkControlContext.Provider
|
|
238
|
+
value={{ iconClassName: classes.icon, radiomarkVariantProps }}
|
|
239
|
+
>
|
|
248
240
|
<view
|
|
249
|
-
{...(ref ? { ref: ref as
|
|
241
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
250
242
|
className={clsx(classes.root, className)}
|
|
251
243
|
{...nativeProps}
|
|
252
244
|
>
|
|
253
245
|
{children}
|
|
254
246
|
</view>
|
|
255
|
-
</
|
|
247
|
+
</RadiomarkControlContext.Provider>
|
|
256
248
|
);
|
|
257
249
|
},
|
|
258
250
|
);
|
|
@@ -260,73 +252,63 @@ RadioGroupItemControl.displayName = "RadioGroupItemControl";
|
|
|
260
252
|
|
|
261
253
|
////////////////////////////////////////////////////////////////////////////////////
|
|
262
254
|
|
|
263
|
-
|
|
264
|
-
className
|
|
265
|
-
style?: CSSProperties;
|
|
266
|
-
ref?: React.Ref<MainThread.Element>;
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
export interface RadioGroupItemIndicatorProps {
|
|
255
|
+
export interface RadioGroupItemIndicatorProps
|
|
256
|
+
extends Pick<LynxStyledElementProps, "className" | "style"> {
|
|
270
257
|
/** Icon rendered when not checked. Optional — falls back to default `<view>` dot when omitted. */
|
|
271
|
-
unchecked?: ReactElement<
|
|
258
|
+
unchecked?: ReactElement<LynxIconElementProps>;
|
|
272
259
|
/** Icon rendered when the item is checked. Optional — falls back to default `<view>` dot when omitted. */
|
|
273
|
-
checked?: ReactElement<
|
|
274
|
-
className?: string;
|
|
275
|
-
style?: CSSProperties;
|
|
260
|
+
checked?: ReactElement<LynxIconElementProps>;
|
|
276
261
|
}
|
|
277
262
|
|
|
278
263
|
export function RadioGroupItemIndicator(props: RadioGroupItemIndicatorProps) {
|
|
279
264
|
const { unchecked, checked: checkedIcon, className, style } = props;
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const { ref: tintRef } = useIconColor([checked, disabled, pressed, tone, size]);
|
|
265
|
+
const itemContext = useRadioGroupItemContext("RadioGroupItemIndicator");
|
|
266
|
+
const { iconClassName, radiomarkVariantProps } =
|
|
267
|
+
useRadiomarkControlContext("RadioGroupItemIndicator");
|
|
284
268
|
|
|
285
|
-
const icon = checked ? checkedIcon : unchecked;
|
|
269
|
+
const icon = itemContext.checked ? checkedIcon : unchecked;
|
|
286
270
|
|
|
287
271
|
// 사용자가 custom icon 을 주지 않으면 default `<view>` 로 동그란 점을 그린다.
|
|
288
272
|
// radiomark.icon recipe 가 borderRadius/backgroundColor/width/height 를 적용해
|
|
289
273
|
// 라디오의 inner dot 모양을 재현. 웹 RadioGroup 의 default `<svg><circle/></svg>` 동작과 일치.
|
|
290
|
-
if (!icon || !isValidElement<
|
|
274
|
+
if (!icon || !isValidElement<LynxIconElementProps>(icon)) {
|
|
291
275
|
return <view className={clsx(iconClassName, className)} style={style} />;
|
|
292
276
|
}
|
|
293
277
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
ref: tintRef,
|
|
309
|
-
});
|
|
278
|
+
return (
|
|
279
|
+
<InternalIcon
|
|
280
|
+
icon={icon}
|
|
281
|
+
className={clsx(iconClassName, className)}
|
|
282
|
+
style={style}
|
|
283
|
+
deps={[
|
|
284
|
+
itemContext.checked,
|
|
285
|
+
itemContext.disabled,
|
|
286
|
+
itemContext.pressed,
|
|
287
|
+
radiomarkVariantProps.tone,
|
|
288
|
+
radiomarkVariantProps.size,
|
|
289
|
+
]}
|
|
290
|
+
/>
|
|
291
|
+
);
|
|
310
292
|
}
|
|
311
293
|
RadioGroupItemIndicator.displayName = "RadioGroupItemIndicator";
|
|
312
294
|
|
|
313
295
|
////////////////////////////////////////////////////////////////////////////////////
|
|
314
296
|
|
|
315
|
-
export interface RadioGroupItemLabelProps {
|
|
316
|
-
children?: React.ReactNode;
|
|
317
|
-
className?: string;
|
|
318
|
-
}
|
|
297
|
+
export interface RadioGroupItemLabelProps extends LynxStyledElementProps {}
|
|
319
298
|
|
|
320
299
|
export const RadioGroupItemLabel = React.forwardRef<unknown, RadioGroupItemLabelProps>(
|
|
321
300
|
(props, ref) => {
|
|
322
301
|
const { children, className, ...nativeProps } = props;
|
|
323
|
-
const
|
|
324
|
-
const
|
|
325
|
-
const labelClassName = radio({
|
|
302
|
+
const itemContext = useRadioGroupItemContext("RadioGroupItemLabel");
|
|
303
|
+
const groupContext = useRadioGroupContext("RadioGroupItemLabel");
|
|
304
|
+
const labelClassName = radio({
|
|
305
|
+
...groupContext.radioVariantProps,
|
|
306
|
+
disabled: itemContext.disabled,
|
|
307
|
+
}).label;
|
|
326
308
|
|
|
327
309
|
return (
|
|
328
310
|
<text
|
|
329
|
-
{...(ref ? { ref: ref as
|
|
311
|
+
{...(ref ? { ref: ref as LynxTextRef } : {})}
|
|
330
312
|
className={clsx(labelClassName, className)}
|
|
331
313
|
{...nativeProps}
|
|
332
314
|
>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
2
|
+
import * as React from "@lynx-js/react";
|
|
3
|
+
|
|
4
|
+
import type { LynxPressableProps, LynxStyledElementProps, LynxViewRef } from "../../types";
|
|
5
|
+
import { useStyleProps, type StyleProps } from "../../utils/styled";
|
|
6
|
+
|
|
7
|
+
type StackStyleProps =
|
|
8
|
+
| "display"
|
|
9
|
+
| "flexDirection"
|
|
10
|
+
| "alignItems"
|
|
11
|
+
| "justifyContent"
|
|
12
|
+
| "flexWrap"
|
|
13
|
+
| "flexGrow"
|
|
14
|
+
| "flexShrink";
|
|
15
|
+
|
|
16
|
+
interface StackBaseProps extends StyleProps, LynxStyledElementProps, LynxPressableProps {
|
|
17
|
+
bindtouchstart?: () => void;
|
|
18
|
+
bindtouchend?: () => void;
|
|
19
|
+
bindtouchcancel?: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @platform Lynx
|
|
24
|
+
*
|
|
25
|
+
* `VStack`과 `HStack`은 native `<view>`를 직접 렌더링하고 stack 방향에 필요한
|
|
26
|
+
* 최소 flex style만 적용합니다. 앱 코드에서 성능에 민감한 반복 레이아웃을 만들
|
|
27
|
+
* 때는 native `<view>`/`<text>`와 Tailwind utility className을 우선 고려하세요.
|
|
28
|
+
* 패키지 컴포넌트 내부에서는 `Box`를 합성하지 않고 native tag와 recipe className을
|
|
29
|
+
* 사용해 primitive 컴포넌트 비용이 누적되지 않도록 합니다.
|
|
30
|
+
*/
|
|
31
|
+
export interface StackProps extends Omit<StackBaseProps, StackStyleProps> {
|
|
32
|
+
align?: StackBaseProps["alignItems"];
|
|
33
|
+
justify?: StackBaseProps["justifyContent"];
|
|
34
|
+
wrap?: StackBaseProps["flexWrap"];
|
|
35
|
+
grow?: StackBaseProps["flexGrow"];
|
|
36
|
+
shrink?: StackBaseProps["flexShrink"];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface VStackProps extends StackProps {}
|
|
40
|
+
|
|
41
|
+
export interface HStackProps extends StackProps {}
|
|
42
|
+
|
|
43
|
+
function getStackProps(props: StackProps) {
|
|
44
|
+
const { align, justify, wrap, grow, shrink, ...restProps } = props;
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
alignItems: align,
|
|
48
|
+
justifyContent: justify,
|
|
49
|
+
flexWrap: wrap,
|
|
50
|
+
flexGrow: grow,
|
|
51
|
+
flexShrink: shrink,
|
|
52
|
+
...restProps,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function useStackStyleProps(props: StackProps, flexDirection: "column" | "row") {
|
|
57
|
+
return useStyleProps({
|
|
58
|
+
display: "flex",
|
|
59
|
+
flexDirection,
|
|
60
|
+
...getStackProps(props),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function renderStackView(
|
|
65
|
+
stackStyleProps: ReturnType<typeof useStackStyleProps>,
|
|
66
|
+
ref: React.ForwardedRef<unknown>,
|
|
67
|
+
) {
|
|
68
|
+
const { style, restProps } = stackStyleProps;
|
|
69
|
+
const { children, className, ...nativeProps } = restProps;
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<view
|
|
73
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
74
|
+
{...nativeProps}
|
|
75
|
+
className={clsx(className)}
|
|
76
|
+
style={style}
|
|
77
|
+
>
|
|
78
|
+
{children}
|
|
79
|
+
</view>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const VStack = React.forwardRef<unknown, VStackProps>((props, ref) => {
|
|
84
|
+
const stackStyleProps = useStackStyleProps(props, "column");
|
|
85
|
+
|
|
86
|
+
return renderStackView(stackStyleProps, ref);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
VStack.displayName = "VStack";
|
|
90
|
+
|
|
91
|
+
export const HStack = React.forwardRef<unknown, HStackProps>((props, ref) => {
|
|
92
|
+
const stackStyleProps = useStackStyleProps(props, "row");
|
|
93
|
+
|
|
94
|
+
return renderStackView(stackStyleProps, ref);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
HStack.displayName = "HStack";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { HStack, type HStackProps, type StackProps, VStack, type VStackProps } from "./Stack";
|