@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,68 +1,88 @@
|
|
|
1
1
|
import { actionButton } from "@seed-design/lynx-css/recipes/action-button";
|
|
2
2
|
import type { ActionButtonVariantProps } from "@seed-design/lynx-css/recipes/action-button";
|
|
3
|
+
import { progressCircleVariantMap } from "@seed-design/lynx-css/recipes/progress-circle";
|
|
3
4
|
import { actionButton as actionButtonVars } from "@seed-design/lynx-css/vars/component";
|
|
4
|
-
import type { MainThread } from "@lynx-js/types";
|
|
5
5
|
import clsx from "clsx";
|
|
6
|
-
import * as React from "react";
|
|
7
|
-
import {
|
|
6
|
+
import * as React from "@lynx-js/react";
|
|
7
|
+
import { isValidElement, useMemo } from "@lynx-js/react";
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
9
|
+
import { usePressTap } from "../../hooks/usePressTap";
|
|
10
|
+
import type {
|
|
11
|
+
LynxElementProps,
|
|
12
|
+
LynxPressableProps,
|
|
13
|
+
LynxStyledElementProps,
|
|
14
|
+
LynxTextRef,
|
|
15
|
+
LynxTouchProps,
|
|
16
|
+
LynxViewProps,
|
|
17
|
+
LynxViewRef,
|
|
18
|
+
} from "../../types";
|
|
11
19
|
import { createSlotRecipeContext } from "../../utils/create-slot-recipe-context";
|
|
20
|
+
import { toArray } from "../../utils/children";
|
|
12
21
|
import { capitalize, resolveRecipeToken } from "../../utils/resolve-recipe-token";
|
|
22
|
+
import { resolveFlexValue, type StyleProps } from "../../utils/styled";
|
|
13
23
|
import {
|
|
14
24
|
ProgressCircleRange,
|
|
15
25
|
ProgressCircleRoot,
|
|
16
26
|
type ProgressCircleRootProps,
|
|
17
27
|
} from "../ProgressCircle";
|
|
28
|
+
import {
|
|
29
|
+
Icon,
|
|
30
|
+
IconRequired,
|
|
31
|
+
IconSlotProvider,
|
|
32
|
+
PrefixIcon,
|
|
33
|
+
SuffixIcon,
|
|
34
|
+
getIconSlotName,
|
|
35
|
+
type IconProps,
|
|
36
|
+
type PrefixIconProps,
|
|
37
|
+
type SuffixIconProps,
|
|
38
|
+
} from "../Icon/Icon";
|
|
18
39
|
|
|
19
40
|
// Root/TextSlot 은 `withProvider("view", ...)` / `withContext("text", ...)` 를 쓰지 않는다.
|
|
20
41
|
// intrinsic string 인자는 `React.createElement("view", ...)` 로 컴파일되어 Lynx 컴파일러의
|
|
21
42
|
// 리터럴 JSX 정적 분석을 우회하고 `BackgroundSnapshot not found: view` 런타임 에러를 유발한다.
|
|
22
43
|
// (자세한 내용: `packages/lynx-react/AGENTS.md` 의 "Native tag literal JSX constraint" 섹션)
|
|
23
|
-
const { ClassNamesProvider, useClassNames, PropsProvider
|
|
24
|
-
createSlotRecipeContext(actionButton);
|
|
25
|
-
|
|
26
|
-
type IconSlotKey = "prefixIcon" | "suffixIcon" | "icon";
|
|
27
|
-
type ActionButtonSize = NonNullable<ActionButtonVariantProps["size"]>;
|
|
44
|
+
const { ClassNamesProvider, useClassNames, PropsProvider } = createSlotRecipeContext(actionButton);
|
|
28
45
|
|
|
29
|
-
|
|
30
|
-
className?: string;
|
|
31
|
-
style?: React.CSSProperties;
|
|
32
|
-
ref?: React.Ref<MainThread.Element>;
|
|
33
|
-
"main-thread:binduiappear"?: () => void;
|
|
34
|
-
};
|
|
46
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
35
47
|
|
|
36
|
-
|
|
37
|
-
children?: React.ReactNode;
|
|
48
|
+
interface ActionButtonContentProps extends LynxElementProps {
|
|
38
49
|
isIconOnly: boolean;
|
|
39
|
-
icon?:
|
|
40
|
-
prefixIcon?:
|
|
41
|
-
suffixIcon?:
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
50
|
+
icon?: IconProps["icon"];
|
|
51
|
+
prefixIcon?: PrefixIconProps["icon"];
|
|
52
|
+
suffixIcon?: SuffixIconProps["icon"];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface ActionButtonAccessibilityProps {
|
|
56
|
+
"accessibility-label"?: LynxViewProps["accessibility-label"];
|
|
57
|
+
"accessibility-element"?: LynxViewProps["accessibility-element"];
|
|
58
|
+
"accessibility-traits"?: LynxViewProps["accessibility-traits"];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface ActionButtonRootOwnProps
|
|
62
|
+
extends LynxStyledElementProps,
|
|
63
|
+
LynxTouchProps,
|
|
64
|
+
ActionButtonAccessibilityProps {}
|
|
65
|
+
|
|
66
|
+
interface ActionButtonRootProps extends ActionButtonVariantProps, ActionButtonRootOwnProps {}
|
|
67
|
+
|
|
68
|
+
function resolveProgressCircleSize(
|
|
69
|
+
actionButtonSize: ActionButtonVariantProps["size"],
|
|
70
|
+
): ProgressCircleRootProps["size"] {
|
|
71
|
+
const size = actionButtonSize ?? "medium";
|
|
72
|
+
const token = resolveRecipeToken(actionButtonVars, [
|
|
73
|
+
`size${capitalize(size)}`,
|
|
74
|
+
"enabled",
|
|
75
|
+
"progressCircle",
|
|
76
|
+
"size",
|
|
77
|
+
]);
|
|
78
|
+
const normalized = token?.endsWith("px") ? token.slice(0, -2) : token;
|
|
79
|
+
|
|
80
|
+
return progressCircleVariantMap.size.find((size) => size === normalized) ?? "16";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
84
|
+
|
|
85
|
+
const ActionButtonRoot = React.forwardRef<unknown, ActionButtonRootProps>((innerProps, ref) => {
|
|
66
86
|
const props = { layout: "withText" as const, ...innerProps };
|
|
67
87
|
const [variantProps, otherProps] = actionButton.splitVariantProps(props);
|
|
68
88
|
const classNames = actionButton(variantProps);
|
|
@@ -82,31 +102,58 @@ const ActionButtonRoot = React.forwardRef<
|
|
|
82
102
|
variantProps.loading,
|
|
83
103
|
],
|
|
84
104
|
);
|
|
105
|
+
const iconSlotContextValue = useMemo(
|
|
106
|
+
() => ({
|
|
107
|
+
classNames: {
|
|
108
|
+
icon: classNames.icon,
|
|
109
|
+
prefixIcon: classNames.prefixIcon,
|
|
110
|
+
suffixIcon: classNames.suffixIcon,
|
|
111
|
+
},
|
|
112
|
+
deps: [
|
|
113
|
+
variantProps.variant ?? null,
|
|
114
|
+
variantProps.disabled ?? false,
|
|
115
|
+
variantProps.loading ?? false,
|
|
116
|
+
variantProps.pressed ?? false,
|
|
117
|
+
],
|
|
118
|
+
}),
|
|
119
|
+
[
|
|
120
|
+
classNames.icon,
|
|
121
|
+
classNames.prefixIcon,
|
|
122
|
+
classNames.suffixIcon,
|
|
123
|
+
variantProps.variant,
|
|
124
|
+
variantProps.disabled,
|
|
125
|
+
variantProps.loading,
|
|
126
|
+
variantProps.pressed,
|
|
127
|
+
],
|
|
128
|
+
);
|
|
85
129
|
return (
|
|
86
130
|
<ClassNamesProvider value={classNames}>
|
|
87
131
|
<PropsProvider value={propsForContext}>
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
132
|
+
<IconSlotProvider value={iconSlotContextValue}>
|
|
133
|
+
<view
|
|
134
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
135
|
+
{...rest}
|
|
136
|
+
className={clsx(classNames.root, userClassName)}
|
|
137
|
+
>
|
|
138
|
+
{children as React.ReactNode}
|
|
139
|
+
</view>
|
|
140
|
+
</IconSlotProvider>
|
|
95
141
|
</PropsProvider>
|
|
96
142
|
</ClassNamesProvider>
|
|
97
143
|
);
|
|
98
144
|
});
|
|
99
145
|
ActionButtonRoot.displayName = "ActionButtonRoot";
|
|
100
146
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
147
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
148
|
+
|
|
149
|
+
interface ActionButtonTextSlotProps extends LynxElementProps {}
|
|
150
|
+
|
|
151
|
+
const ActionButtonTextSlot = React.forwardRef<unknown, ActionButtonTextSlotProps>((props, ref) => {
|
|
105
152
|
const { children, className: userClassName, ...rest } = props;
|
|
106
153
|
const classNames = useClassNames();
|
|
107
154
|
return (
|
|
108
155
|
<text
|
|
109
|
-
{...(ref ? { ref: ref as
|
|
156
|
+
{...(ref ? { ref: ref as LynxTextRef } : {})}
|
|
110
157
|
{...rest}
|
|
111
158
|
className={clsx(classNames.text, userClassName)}
|
|
112
159
|
>
|
|
@@ -116,55 +163,7 @@ const ActionButtonTextSlot = React.forwardRef<
|
|
|
116
163
|
});
|
|
117
164
|
ActionButtonTextSlot.displayName = "ActionButtonTextSlot";
|
|
118
165
|
|
|
119
|
-
|
|
120
|
-
* rootage vars 에서 현재 variant/size/layout 조합의 slot `size` 토큰
|
|
121
|
-
* (예: `"var(--seed-dimension-x4)"`) 을 꺼낸다. 아이콘 컴포넌트가 inline
|
|
122
|
-
* `style={{ width, height }}` 를 박기 때문에 style prop 으로 덮어 씌워야 recipe 사이즈가 적용된다.
|
|
123
|
-
*/
|
|
124
|
-
function resolveIconSize(
|
|
125
|
-
variantProps: ActionButtonVariantProps | null,
|
|
126
|
-
slot: IconSlotKey,
|
|
127
|
-
): string | undefined {
|
|
128
|
-
const size = variantProps?.size ?? "medium";
|
|
129
|
-
// `icon` slot is only rendered under `layout="iconOnly"` and keyed at
|
|
130
|
-
// `sizeXxxLayoutIconOnly.enabled.icon.size`. prefixIcon/suffixIcon follow
|
|
131
|
-
// the current `layout` (`withText` by default) path.
|
|
132
|
-
const layout = slot === "icon" ? "iconOnly" : (variantProps?.layout ?? "withText");
|
|
133
|
-
return resolveRecipeToken(actionButtonVars, [
|
|
134
|
-
`size${capitalize(size)}Layout${capitalize(layout)}`,
|
|
135
|
-
"enabled",
|
|
136
|
-
slot,
|
|
137
|
-
"size",
|
|
138
|
-
]);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* `prefixIcon` / `suffixIcon` prop 으로 전달된 아이콘 element 에 slot className +
|
|
143
|
-
* size(style) + main-thread tint-color ref 를 주입한다.
|
|
144
|
-
*/
|
|
145
|
-
function ActionButtonIconSlot({
|
|
146
|
-
icon,
|
|
147
|
-
slot,
|
|
148
|
-
}: {
|
|
149
|
-
icon: ReactElement<IconElementProps>;
|
|
150
|
-
slot: IconSlotKey;
|
|
151
|
-
}) {
|
|
152
|
-
const classNames = useClassNames();
|
|
153
|
-
const variantProps = useProps() as ActionButtonVariantProps | null;
|
|
154
|
-
const iconColor = useIconColor([
|
|
155
|
-
variantProps?.variant ?? null,
|
|
156
|
-
variantProps?.disabled ?? false,
|
|
157
|
-
variantProps?.loading ?? false,
|
|
158
|
-
]);
|
|
159
|
-
const sizeVar = resolveIconSize(variantProps, slot);
|
|
160
|
-
const childProps = icon.props;
|
|
161
|
-
return cloneElement(icon, {
|
|
162
|
-
...iconColor,
|
|
163
|
-
className: clsx(classNames[slot], childProps.className),
|
|
164
|
-
style:
|
|
165
|
-
sizeVar != null ? { width: sizeVar, height: sizeVar, ...childProps.style } : childProps.style,
|
|
166
|
-
});
|
|
167
|
-
}
|
|
166
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
168
167
|
|
|
169
168
|
function ActionButtonContent({
|
|
170
169
|
children,
|
|
@@ -173,25 +172,49 @@ function ActionButtonContent({
|
|
|
173
172
|
prefixIcon,
|
|
174
173
|
suffixIcon,
|
|
175
174
|
}: ActionButtonContentProps) {
|
|
175
|
+
const childArray = toArray(children);
|
|
176
|
+
const prefixIconChildren: React.ReactNode[] = [];
|
|
177
|
+
const suffixIconChildren: React.ReactNode[] = [];
|
|
178
|
+
const iconChildren: React.ReactNode[] = [];
|
|
179
|
+
const textChildren: React.ReactNode[] = [];
|
|
180
|
+
|
|
181
|
+
for (const child of childArray) {
|
|
182
|
+
const slotName = getIconSlotName(child);
|
|
183
|
+
|
|
184
|
+
if (slotName === "prefixIcon") {
|
|
185
|
+
prefixIconChildren.push(child);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (slotName === "suffixIcon") {
|
|
189
|
+
suffixIconChildren.push(child);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (slotName === "icon") {
|
|
193
|
+
iconChildren.push(child);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
textChildren.push(child);
|
|
198
|
+
}
|
|
199
|
+
|
|
176
200
|
if (isIconOnly) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
) : null;
|
|
201
|
+
if (icon != null && isValidElement(icon)) return <Icon icon={icon} />;
|
|
202
|
+
return iconChildren.length > 0 ? <>{iconChildren}</> : null;
|
|
180
203
|
}
|
|
181
204
|
|
|
182
205
|
return (
|
|
183
206
|
<>
|
|
184
|
-
{prefixIcon != null && isValidElement(prefixIcon) ?
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
{suffixIcon != null && isValidElement(suffixIcon) ?
|
|
189
|
-
<ActionButtonIconSlot icon={suffixIcon} slot="suffixIcon" />
|
|
190
|
-
) : null}
|
|
207
|
+
{prefixIcon != null && isValidElement(prefixIcon) ? <PrefixIcon icon={prefixIcon} /> : null}
|
|
208
|
+
{prefixIconChildren}
|
|
209
|
+
{textChildren.length > 0 ? <ActionButtonTextSlot>{textChildren}</ActionButtonTextSlot> : null}
|
|
210
|
+
{suffixIconChildren}
|
|
211
|
+
{suffixIcon != null && isValidElement(suffixIcon) ? <SuffixIcon icon={suffixIcon} /> : null}
|
|
191
212
|
</>
|
|
192
213
|
);
|
|
193
214
|
}
|
|
194
215
|
|
|
216
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
217
|
+
|
|
195
218
|
function ActionButtonLoadingContent(props: ActionButtonContentProps) {
|
|
196
219
|
const classNames = useClassNames();
|
|
197
220
|
|
|
@@ -202,60 +225,63 @@ function ActionButtonLoadingContent(props: ActionButtonContentProps) {
|
|
|
202
225
|
);
|
|
203
226
|
}
|
|
204
227
|
|
|
205
|
-
|
|
228
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
229
|
+
|
|
230
|
+
function ActionButtonLoadingIndicator({ size }: { size: ActionButtonVariantProps["size"] }) {
|
|
206
231
|
const classNames = useClassNames();
|
|
232
|
+
const progressCircleSize = resolveProgressCircleSize(size);
|
|
207
233
|
|
|
208
234
|
return (
|
|
209
235
|
<view className={classNames.loadingIndicator}>
|
|
210
|
-
<ProgressCircleRoot size={
|
|
236
|
+
<ProgressCircleRoot size={progressCircleSize} tone="inherit">
|
|
211
237
|
<ProgressCircleRange />
|
|
212
238
|
</ProgressCircleRoot>
|
|
213
239
|
</view>
|
|
214
240
|
);
|
|
215
241
|
}
|
|
216
242
|
|
|
243
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
244
|
+
|
|
217
245
|
/**
|
|
218
246
|
* @platform Lynx
|
|
219
247
|
*
|
|
220
248
|
* 웹 대비 차이:
|
|
221
|
-
* - 아이콘
|
|
222
|
-
* `
|
|
223
|
-
*
|
|
249
|
+
* - 아이콘 렌더링: 웹의 SVG `currentColor` 대신 Lynx `<image>` 의 `tint-color` 를
|
|
250
|
+
* `Icon` / `PrefixIcon` / `SuffixIcon` wrapper 가 동기화한다.
|
|
251
|
+
* - 호환 API: 기존 `prefixIcon` / `suffixIcon` / `icon` prop 도 유지한다.
|
|
224
252
|
* - 미지원 prop: `color`, `fontWeight`, `bleedX`, `bleedY` (CSS variable 동적 주입 제한)
|
|
225
253
|
*
|
|
226
254
|
* ```tsx
|
|
227
255
|
* import IconPlusFill from "@karrotmarket/lynx-monochrome-icon/IconPlusFill";
|
|
228
256
|
* import IconChevronDownFill from "@karrotmarket/lynx-monochrome-icon/IconChevronDownFill";
|
|
257
|
+
* import { Icon, PrefixIcon, SuffixIcon } from "@seed-design/lynx-react";
|
|
229
258
|
*
|
|
230
259
|
* // withText (기본)
|
|
231
|
-
* <ActionButton
|
|
232
|
-
*
|
|
233
|
-
* prefixIcon={<IconPlusFill />}
|
|
234
|
-
* suffixIcon={<IconChevronDownFill />}
|
|
235
|
-
* >
|
|
260
|
+
* <ActionButton variant="brandSolid">
|
|
261
|
+
* <PrefixIcon icon={<IconPlusFill />} />
|
|
236
262
|
* 라벨
|
|
263
|
+
* <SuffixIcon icon={<IconChevronDownFill />} />
|
|
237
264
|
* </ActionButton>
|
|
238
265
|
*
|
|
239
|
-
* // iconOnly —
|
|
266
|
+
* // iconOnly — `<Icon />` child 와 `accessibility-label` 필수
|
|
240
267
|
* <ActionButton
|
|
241
268
|
* layout="iconOnly"
|
|
242
269
|
* variant="neutralSolid"
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
* />
|
|
270
|
+
* accessibility-label="추가"
|
|
271
|
+
* >
|
|
272
|
+
* <Icon icon={<IconPlusFill />} />
|
|
273
|
+
* </ActionButton>
|
|
246
274
|
* ```
|
|
247
275
|
*/
|
|
248
|
-
export interface ActionButtonProps
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
"
|
|
257
|
-
bindtap?: () => void;
|
|
258
|
-
"main-thread:bindtap"?: () => void;
|
|
276
|
+
export interface ActionButtonProps
|
|
277
|
+
extends Omit<ActionButtonVariantProps, "pressed">,
|
|
278
|
+
Pick<StyleProps, "flexGrow">,
|
|
279
|
+
LynxElementProps,
|
|
280
|
+
LynxPressableProps,
|
|
281
|
+
ActionButtonAccessibilityProps {
|
|
282
|
+
icon?: IconProps["icon"];
|
|
283
|
+
prefixIcon?: PrefixIconProps["icon"];
|
|
284
|
+
suffixIcon?: SuffixIconProps["icon"];
|
|
259
285
|
}
|
|
260
286
|
|
|
261
287
|
export const ActionButton = React.forwardRef<unknown, ActionButtonProps>((props, ref) => {
|
|
@@ -268,15 +294,25 @@ export const ActionButton = React.forwardRef<unknown, ActionButtonProps>((props,
|
|
|
268
294
|
suffixIcon,
|
|
269
295
|
bindtap,
|
|
270
296
|
"main-thread:bindtap": mainThreadBindtap,
|
|
297
|
+
"accessibility-element": accessibilityElement = true,
|
|
298
|
+
"accessibility-label": accessibilityLabel,
|
|
299
|
+
"accessibility-traits": accessibilityTraits = "button",
|
|
271
300
|
...variantAndRest
|
|
272
301
|
} = props;
|
|
273
302
|
const { disabled = false, loading = false } = variantAndRest;
|
|
274
303
|
const isInteractive = !disabled && !loading;
|
|
275
304
|
const isIconOnly = layout === "iconOnly";
|
|
276
|
-
const size = variantAndRest.size
|
|
305
|
+
const size = variantAndRest.size;
|
|
277
306
|
|
|
278
|
-
if (
|
|
279
|
-
|
|
307
|
+
if (
|
|
308
|
+
process.env.NODE_ENV !== "production" &&
|
|
309
|
+
isIconOnly &&
|
|
310
|
+
accessibilityElement &&
|
|
311
|
+
!accessibilityLabel
|
|
312
|
+
) {
|
|
313
|
+
console.warn(
|
|
314
|
+
'ActionButton: `layout="iconOnly"` requires `accessibility-label` for accessibility.',
|
|
315
|
+
);
|
|
280
316
|
}
|
|
281
317
|
|
|
282
318
|
const { pressed, ...pressTapHandlers } = usePressTap({
|
|
@@ -286,37 +322,42 @@ export const ActionButton = React.forwardRef<unknown, ActionButtonProps>((props,
|
|
|
286
322
|
});
|
|
287
323
|
|
|
288
324
|
return (
|
|
289
|
-
<
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
325
|
+
<IconRequired enabled={isIconOnly}>
|
|
326
|
+
<ActionButtonRoot
|
|
327
|
+
{...variantAndRest}
|
|
328
|
+
layout={layout}
|
|
329
|
+
pressed={pressed}
|
|
330
|
+
ref={ref}
|
|
331
|
+
style={flexGrow != null ? { flexGrow: resolveFlexValue(flexGrow) } : undefined}
|
|
332
|
+
accessibility-element={accessibilityElement}
|
|
333
|
+
accessibility-label={accessibilityLabel}
|
|
334
|
+
accessibility-traits={accessibilityTraits}
|
|
335
|
+
{...pressTapHandlers}
|
|
336
|
+
>
|
|
337
|
+
{loading ? (
|
|
338
|
+
<>
|
|
339
|
+
<ActionButtonLoadingIndicator size={size} />
|
|
340
|
+
<ActionButtonLoadingContent
|
|
341
|
+
isIconOnly={isIconOnly}
|
|
342
|
+
icon={icon}
|
|
343
|
+
prefixIcon={prefixIcon}
|
|
344
|
+
suffixIcon={suffixIcon}
|
|
345
|
+
>
|
|
346
|
+
{children}
|
|
347
|
+
</ActionButtonLoadingContent>
|
|
348
|
+
</>
|
|
349
|
+
) : (
|
|
350
|
+
<ActionButtonContent
|
|
301
351
|
isIconOnly={isIconOnly}
|
|
302
352
|
icon={icon}
|
|
303
353
|
prefixIcon={prefixIcon}
|
|
304
354
|
suffixIcon={suffixIcon}
|
|
305
355
|
>
|
|
306
356
|
{children}
|
|
307
|
-
</
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
isIconOnly={isIconOnly}
|
|
312
|
-
icon={icon}
|
|
313
|
-
prefixIcon={prefixIcon}
|
|
314
|
-
suffixIcon={suffixIcon}
|
|
315
|
-
>
|
|
316
|
-
{children}
|
|
317
|
-
</ActionButtonContent>
|
|
318
|
-
)}
|
|
319
|
-
</ActionButtonRoot>
|
|
357
|
+
</ActionButtonContent>
|
|
358
|
+
)}
|
|
359
|
+
</ActionButtonRoot>
|
|
360
|
+
</IconRequired>
|
|
320
361
|
);
|
|
321
362
|
});
|
|
322
363
|
ActionButton.displayName = "ActionButton";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export {
|
|
2
|
+
AppBarIconButton as IconButton,
|
|
3
|
+
AppBarLeft as Left,
|
|
4
|
+
AppBarMain as Main,
|
|
5
|
+
AppBarRight as Right,
|
|
6
|
+
AppBarRoot as Root,
|
|
7
|
+
AppBarSlot as Slot,
|
|
8
|
+
AppBarSubtitle as Subtitle,
|
|
9
|
+
AppBarTitle as Title,
|
|
10
|
+
type AppBarIconButtonProps as IconButtonProps,
|
|
11
|
+
type AppBarLeftProps as LeftProps,
|
|
12
|
+
type AppBarMainProps as MainProps,
|
|
13
|
+
type AppBarRightProps as RightProps,
|
|
14
|
+
type AppBarRootProps as RootProps,
|
|
15
|
+
type AppBarSlotProps as SlotProps,
|
|
16
|
+
type AppBarSubtitleProps as SubtitleProps,
|
|
17
|
+
type AppBarTitleProps as TitleProps,
|
|
18
|
+
} from "./AppBar";
|