@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,67 +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
|
-
};
|
|
46
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
34
47
|
|
|
35
|
-
|
|
36
|
-
children?: React.ReactNode;
|
|
48
|
+
interface ActionButtonContentProps extends LynxElementProps {
|
|
37
49
|
isIconOnly: boolean;
|
|
38
|
-
icon?:
|
|
39
|
-
prefixIcon?:
|
|
40
|
-
suffixIcon?:
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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) => {
|
|
65
86
|
const props = { layout: "withText" as const, ...innerProps };
|
|
66
87
|
const [variantProps, otherProps] = actionButton.splitVariantProps(props);
|
|
67
88
|
const classNames = actionButton(variantProps);
|
|
@@ -81,31 +102,58 @@ const ActionButtonRoot = React.forwardRef<
|
|
|
81
102
|
variantProps.loading,
|
|
82
103
|
],
|
|
83
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
|
+
);
|
|
84
129
|
return (
|
|
85
130
|
<ClassNamesProvider value={classNames}>
|
|
86
131
|
<PropsProvider value={propsForContext}>
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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>
|
|
94
141
|
</PropsProvider>
|
|
95
142
|
</ClassNamesProvider>
|
|
96
143
|
);
|
|
97
144
|
});
|
|
98
145
|
ActionButtonRoot.displayName = "ActionButtonRoot";
|
|
99
146
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
147
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
148
|
+
|
|
149
|
+
interface ActionButtonTextSlotProps extends LynxElementProps {}
|
|
150
|
+
|
|
151
|
+
const ActionButtonTextSlot = React.forwardRef<unknown, ActionButtonTextSlotProps>((props, ref) => {
|
|
104
152
|
const { children, className: userClassName, ...rest } = props;
|
|
105
153
|
const classNames = useClassNames();
|
|
106
154
|
return (
|
|
107
155
|
<text
|
|
108
|
-
{...(ref ? { ref: ref as
|
|
156
|
+
{...(ref ? { ref: ref as LynxTextRef } : {})}
|
|
109
157
|
{...rest}
|
|
110
158
|
className={clsx(classNames.text, userClassName)}
|
|
111
159
|
>
|
|
@@ -115,55 +163,7 @@ const ActionButtonTextSlot = React.forwardRef<
|
|
|
115
163
|
});
|
|
116
164
|
ActionButtonTextSlot.displayName = "ActionButtonTextSlot";
|
|
117
165
|
|
|
118
|
-
|
|
119
|
-
* rootage vars 에서 현재 variant/size/layout 조합의 slot `size` 토큰
|
|
120
|
-
* (예: `"var(--seed-dimension-x4)"`) 을 꺼낸다. 아이콘 컴포넌트가 inline
|
|
121
|
-
* `style={{ width, height }}` 를 박기 때문에 style prop 으로 덮어 씌워야 recipe 사이즈가 적용된다.
|
|
122
|
-
*/
|
|
123
|
-
function resolveIconSize(
|
|
124
|
-
variantProps: ActionButtonVariantProps | null,
|
|
125
|
-
slot: IconSlotKey,
|
|
126
|
-
): string | undefined {
|
|
127
|
-
const size = variantProps?.size ?? "medium";
|
|
128
|
-
// `icon` slot is only rendered under `layout="iconOnly"` and keyed at
|
|
129
|
-
// `sizeXxxLayoutIconOnly.enabled.icon.size`. prefixIcon/suffixIcon follow
|
|
130
|
-
// the current `layout` (`withText` by default) path.
|
|
131
|
-
const layout = slot === "icon" ? "iconOnly" : (variantProps?.layout ?? "withText");
|
|
132
|
-
return resolveRecipeToken(actionButtonVars, [
|
|
133
|
-
`size${capitalize(size)}Layout${capitalize(layout)}`,
|
|
134
|
-
"enabled",
|
|
135
|
-
slot,
|
|
136
|
-
"size",
|
|
137
|
-
]);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* `prefixIcon` / `suffixIcon` prop 으로 전달된 아이콘 element 에 slot className +
|
|
142
|
-
* size(style) + main-thread tint-color ref 를 주입한다.
|
|
143
|
-
*/
|
|
144
|
-
function ActionButtonIconSlot({
|
|
145
|
-
icon,
|
|
146
|
-
slot,
|
|
147
|
-
}: {
|
|
148
|
-
icon: ReactElement<IconElementProps>;
|
|
149
|
-
slot: IconSlotKey;
|
|
150
|
-
}) {
|
|
151
|
-
const classNames = useClassNames();
|
|
152
|
-
const variantProps = useProps() as ActionButtonVariantProps | null;
|
|
153
|
-
const { ref } = useIconColor([
|
|
154
|
-
variantProps?.variant ?? null,
|
|
155
|
-
variantProps?.disabled ?? false,
|
|
156
|
-
variantProps?.loading ?? false,
|
|
157
|
-
]);
|
|
158
|
-
const sizeVar = resolveIconSize(variantProps, slot);
|
|
159
|
-
const childProps = icon.props;
|
|
160
|
-
return cloneElement(icon, {
|
|
161
|
-
className: clsx(classNames[slot], childProps.className),
|
|
162
|
-
style:
|
|
163
|
-
sizeVar != null ? { width: sizeVar, height: sizeVar, ...childProps.style } : childProps.style,
|
|
164
|
-
ref: ref as React.Ref<MainThread.Element>,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
166
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
167
167
|
|
|
168
168
|
function ActionButtonContent({
|
|
169
169
|
children,
|
|
@@ -172,25 +172,49 @@ function ActionButtonContent({
|
|
|
172
172
|
prefixIcon,
|
|
173
173
|
suffixIcon,
|
|
174
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
|
+
|
|
175
200
|
if (isIconOnly) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
) : null;
|
|
201
|
+
if (icon != null && isValidElement(icon)) return <Icon icon={icon} />;
|
|
202
|
+
return iconChildren.length > 0 ? <>{iconChildren}</> : null;
|
|
179
203
|
}
|
|
180
204
|
|
|
181
205
|
return (
|
|
182
206
|
<>
|
|
183
|
-
{prefixIcon != null && isValidElement(prefixIcon) ?
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
{suffixIcon != null && isValidElement(suffixIcon) ?
|
|
188
|
-
<ActionButtonIconSlot icon={suffixIcon} slot="suffixIcon" />
|
|
189
|
-
) : 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}
|
|
190
212
|
</>
|
|
191
213
|
);
|
|
192
214
|
}
|
|
193
215
|
|
|
216
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
217
|
+
|
|
194
218
|
function ActionButtonLoadingContent(props: ActionButtonContentProps) {
|
|
195
219
|
const classNames = useClassNames();
|
|
196
220
|
|
|
@@ -201,60 +225,63 @@ function ActionButtonLoadingContent(props: ActionButtonContentProps) {
|
|
|
201
225
|
);
|
|
202
226
|
}
|
|
203
227
|
|
|
204
|
-
|
|
228
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
229
|
+
|
|
230
|
+
function ActionButtonLoadingIndicator({ size }: { size: ActionButtonVariantProps["size"] }) {
|
|
205
231
|
const classNames = useClassNames();
|
|
232
|
+
const progressCircleSize = resolveProgressCircleSize(size);
|
|
206
233
|
|
|
207
234
|
return (
|
|
208
235
|
<view className={classNames.loadingIndicator}>
|
|
209
|
-
<ProgressCircleRoot size={
|
|
236
|
+
<ProgressCircleRoot size={progressCircleSize} tone="inherit">
|
|
210
237
|
<ProgressCircleRange />
|
|
211
238
|
</ProgressCircleRoot>
|
|
212
239
|
</view>
|
|
213
240
|
);
|
|
214
241
|
}
|
|
215
242
|
|
|
243
|
+
////////////////////////////////////////////////////////////////////////////////////
|
|
244
|
+
|
|
216
245
|
/**
|
|
217
246
|
* @platform Lynx
|
|
218
247
|
*
|
|
219
248
|
* 웹 대비 차이:
|
|
220
|
-
* - 아이콘
|
|
221
|
-
* `
|
|
222
|
-
*
|
|
249
|
+
* - 아이콘 렌더링: 웹의 SVG `currentColor` 대신 Lynx `<image>` 의 `tint-color` 를
|
|
250
|
+
* `Icon` / `PrefixIcon` / `SuffixIcon` wrapper 가 동기화한다.
|
|
251
|
+
* - 호환 API: 기존 `prefixIcon` / `suffixIcon` / `icon` prop 도 유지한다.
|
|
223
252
|
* - 미지원 prop: `color`, `fontWeight`, `bleedX`, `bleedY` (CSS variable 동적 주입 제한)
|
|
224
253
|
*
|
|
225
254
|
* ```tsx
|
|
226
255
|
* import IconPlusFill from "@karrotmarket/lynx-monochrome-icon/IconPlusFill";
|
|
227
256
|
* import IconChevronDownFill from "@karrotmarket/lynx-monochrome-icon/IconChevronDownFill";
|
|
257
|
+
* import { Icon, PrefixIcon, SuffixIcon } from "@seed-design/lynx-react";
|
|
228
258
|
*
|
|
229
259
|
* // withText (기본)
|
|
230
|
-
* <ActionButton
|
|
231
|
-
*
|
|
232
|
-
* prefixIcon={<IconPlusFill />}
|
|
233
|
-
* suffixIcon={<IconChevronDownFill />}
|
|
234
|
-
* >
|
|
260
|
+
* <ActionButton variant="brandSolid">
|
|
261
|
+
* <PrefixIcon icon={<IconPlusFill />} />
|
|
235
262
|
* 라벨
|
|
263
|
+
* <SuffixIcon icon={<IconChevronDownFill />} />
|
|
236
264
|
* </ActionButton>
|
|
237
265
|
*
|
|
238
|
-
* // iconOnly —
|
|
266
|
+
* // iconOnly — `<Icon />` child 와 `accessibility-label` 필수
|
|
239
267
|
* <ActionButton
|
|
240
268
|
* layout="iconOnly"
|
|
241
269
|
* variant="neutralSolid"
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
* />
|
|
270
|
+
* accessibility-label="추가"
|
|
271
|
+
* >
|
|
272
|
+
* <Icon icon={<IconPlusFill />} />
|
|
273
|
+
* </ActionButton>
|
|
245
274
|
* ```
|
|
246
275
|
*/
|
|
247
|
-
export interface ActionButtonProps
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
"
|
|
256
|
-
bindtap?: () => void;
|
|
257
|
-
"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"];
|
|
258
285
|
}
|
|
259
286
|
|
|
260
287
|
export const ActionButton = React.forwardRef<unknown, ActionButtonProps>((props, ref) => {
|
|
@@ -267,15 +294,25 @@ export const ActionButton = React.forwardRef<unknown, ActionButtonProps>((props,
|
|
|
267
294
|
suffixIcon,
|
|
268
295
|
bindtap,
|
|
269
296
|
"main-thread:bindtap": mainThreadBindtap,
|
|
297
|
+
"accessibility-element": accessibilityElement = true,
|
|
298
|
+
"accessibility-label": accessibilityLabel,
|
|
299
|
+
"accessibility-traits": accessibilityTraits = "button",
|
|
270
300
|
...variantAndRest
|
|
271
301
|
} = props;
|
|
272
302
|
const { disabled = false, loading = false } = variantAndRest;
|
|
273
303
|
const isInteractive = !disabled && !loading;
|
|
274
304
|
const isIconOnly = layout === "iconOnly";
|
|
275
|
-
const size = variantAndRest.size
|
|
305
|
+
const size = variantAndRest.size;
|
|
276
306
|
|
|
277
|
-
if (
|
|
278
|
-
|
|
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
|
+
);
|
|
279
316
|
}
|
|
280
317
|
|
|
281
318
|
const { pressed, ...pressTapHandlers } = usePressTap({
|
|
@@ -285,37 +322,42 @@ export const ActionButton = React.forwardRef<unknown, ActionButtonProps>((props,
|
|
|
285
322
|
});
|
|
286
323
|
|
|
287
324
|
return (
|
|
288
|
-
<
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
|
300
351
|
isIconOnly={isIconOnly}
|
|
301
352
|
icon={icon}
|
|
302
353
|
prefixIcon={prefixIcon}
|
|
303
354
|
suffixIcon={suffixIcon}
|
|
304
355
|
>
|
|
305
356
|
{children}
|
|
306
|
-
</
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
isIconOnly={isIconOnly}
|
|
311
|
-
icon={icon}
|
|
312
|
-
prefixIcon={prefixIcon}
|
|
313
|
-
suffixIcon={suffixIcon}
|
|
314
|
-
>
|
|
315
|
-
{children}
|
|
316
|
-
</ActionButtonContent>
|
|
317
|
-
)}
|
|
318
|
-
</ActionButtonRoot>
|
|
357
|
+
</ActionButtonContent>
|
|
358
|
+
)}
|
|
359
|
+
</ActionButtonRoot>
|
|
360
|
+
</IconRequired>
|
|
319
361
|
);
|
|
320
362
|
});
|
|
321
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";
|