sparkle-design 0.9.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/LICENSE +201 -0
- package/NOTICE +5 -0
- package/README.en.md +275 -0
- package/README.md +328 -0
- package/dist/components/ui/badge/index.d.ts +67 -0
- package/dist/components/ui/badge/index.figma.d.ts +1 -0
- package/dist/components/ui/badge/index.figma.js +28 -0
- package/dist/components/ui/badge/index.js +129 -0
- package/dist/components/ui/breadcrumb/index.d.ts +70 -0
- package/dist/components/ui/breadcrumb/index.figma.d.ts +1 -0
- package/dist/components/ui/breadcrumb/index.figma.js +28 -0
- package/dist/components/ui/breadcrumb/index.js +96 -0
- package/dist/components/ui/button/index.d.ts +145 -0
- package/dist/components/ui/button/index.figma.d.ts +1 -0
- package/dist/components/ui/button/index.figma.js +59 -0
- package/dist/components/ui/button/index.js +375 -0
- package/dist/components/ui/card/index.d.ts +193 -0
- package/dist/components/ui/card/index.figma.d.ts +1 -0
- package/dist/components/ui/card/index.figma.js +28 -0
- package/dist/components/ui/card/index.js +219 -0
- package/dist/components/ui/checkbox/index.d.ts +62 -0
- package/dist/components/ui/checkbox/index.figma.d.ts +1 -0
- package/dist/components/ui/checkbox/index.figma.js +34 -0
- package/dist/components/ui/checkbox/index.js +192 -0
- package/dist/components/ui/dialog/index.d.ts +106 -0
- package/dist/components/ui/dialog/index.figma.d.ts +1 -0
- package/dist/components/ui/dialog/index.figma.js +21 -0
- package/dist/components/ui/dialog/index.js +157 -0
- package/dist/components/ui/divider/index.d.ts +44 -0
- package/dist/components/ui/divider/index.figma.d.ts +1 -0
- package/dist/components/ui/divider/index.figma.js +39 -0
- package/dist/components/ui/divider/index.js +81 -0
- package/dist/components/ui/form/index.d.ts +171 -0
- package/dist/components/ui/form/index.figma.d.ts +1 -0
- package/dist/components/ui/form/index.figma.js +129 -0
- package/dist/components/ui/form/index.js +214 -0
- package/dist/components/ui/form/label.d.ts +4 -0
- package/dist/components/ui/form/label.js +20 -0
- package/dist/components/ui/icon/index.d.ts +71 -0
- package/dist/components/ui/icon/index.figma.d.ts +1 -0
- package/dist/components/ui/icon/index.figma.js +1858 -0
- package/dist/components/ui/icon/index.js +57 -0
- package/dist/components/ui/icon-button/index.d.ts +77 -0
- package/dist/components/ui/icon-button/index.figma.d.ts +1 -0
- package/dist/components/ui/icon-button/index.figma.js +43 -0
- package/dist/components/ui/icon-button/index.js +341 -0
- package/dist/components/ui/inline-message/index.d.ts +83 -0
- package/dist/components/ui/inline-message/index.figma.d.ts +1 -0
- package/dist/components/ui/inline-message/index.figma.js +26 -0
- package/dist/components/ui/inline-message/index.js +154 -0
- package/dist/components/ui/input/index.d.ts +118 -0
- package/dist/components/ui/input/index.figma.d.ts +1 -0
- package/dist/components/ui/input/index.figma.js +36 -0
- package/dist/components/ui/input/index.js +256 -0
- package/dist/components/ui/input-password/index.d.ts +27 -0
- package/dist/components/ui/input-password/index.figma.d.ts +1 -0
- package/dist/components/ui/input-password/index.figma.js +36 -0
- package/dist/components/ui/input-password/index.js +35 -0
- package/dist/components/ui/link/index.d.ts +68 -0
- package/dist/components/ui/link/index.figma.d.ts +1 -0
- package/dist/components/ui/link/index.figma.js +22 -0
- package/dist/components/ui/link/index.js +92 -0
- package/dist/components/ui/modal/index.d.ts +83 -0
- package/dist/components/ui/modal/index.figma.d.ts +1 -0
- package/dist/components/ui/modal/index.figma.js +61 -0
- package/dist/components/ui/modal/index.js +143 -0
- package/dist/components/ui/overlay/index.d.ts +19 -0
- package/dist/components/ui/overlay/index.figma.d.ts +1 -0
- package/dist/components/ui/overlay/index.figma.js +14 -0
- package/dist/components/ui/overlay/index.js +39 -0
- package/dist/components/ui/radio/index.d.ts +94 -0
- package/dist/components/ui/radio/index.figma.d.ts +1 -0
- package/dist/components/ui/radio/index.figma.js +63 -0
- package/dist/components/ui/radio/index.js +204 -0
- package/dist/components/ui/select/index.d.ts +175 -0
- package/dist/components/ui/select/index.figma.d.ts +1 -0
- package/dist/components/ui/select/index.figma.js +30 -0
- package/dist/components/ui/select/index.js +281 -0
- package/dist/components/ui/skeleton/index.d.ts +21 -0
- package/dist/components/ui/skeleton/index.figma.d.ts +1 -0
- package/dist/components/ui/skeleton/index.figma.js +14 -0
- package/dist/components/ui/skeleton/index.js +32 -0
- package/dist/components/ui/slider/index.d.ts +75 -0
- package/dist/components/ui/slider/index.figma.d.ts +1 -0
- package/dist/components/ui/slider/index.figma.js +28 -0
- package/dist/components/ui/slider/index.js +113 -0
- package/dist/components/ui/slider/utils.d.ts +16 -0
- package/dist/components/ui/slider/utils.js +56 -0
- package/dist/components/ui/spinner/index.d.ts +42 -0
- package/dist/components/ui/spinner/index.figma.d.ts +1 -0
- package/dist/components/ui/spinner/index.figma.js +16 -0
- package/dist/components/ui/spinner/index.js +51 -0
- package/dist/components/ui/switch/index.d.ts +46 -0
- package/dist/components/ui/switch/index.figma.d.ts +1 -0
- package/dist/components/ui/switch/index.figma.js +27 -0
- package/dist/components/ui/switch/index.js +75 -0
- package/dist/components/ui/tabs/index.d.ts +53 -0
- package/dist/components/ui/tabs/index.figma.d.ts +1 -0
- package/dist/components/ui/tabs/index.figma.js +55 -0
- package/dist/components/ui/tabs/index.js +164 -0
- package/dist/components/ui/tag/index.d.ts +50 -0
- package/dist/components/ui/tag/index.figma.d.ts +1 -0
- package/dist/components/ui/tag/index.figma.js +32 -0
- package/dist/components/ui/tag/index.js +177 -0
- package/dist/components/ui/textarea/index.d.ts +49 -0
- package/dist/components/ui/textarea/index.figma.d.ts +1 -0
- package/dist/components/ui/textarea/index.figma.js +27 -0
- package/dist/components/ui/textarea/index.js +94 -0
- package/dist/components/ui/toast/index.d.ts +60 -0
- package/dist/components/ui/toast/index.figma.d.ts +1 -0
- package/dist/components/ui/toast/index.figma.js +26 -0
- package/dist/components/ui/toast/index.js +57 -0
- package/dist/components/ui/tooltip/index.d.ts +52 -0
- package/dist/components/ui/tooltip/index.figma.d.ts +1 -0
- package/dist/components/ui/tooltip/index.figma.js +23 -0
- package/dist/components/ui/tooltip/index.js +65 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +27 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/test/helpers.d.ts +56 -0
- package/dist/test/helpers.js +182 -0
- package/dist/test/setup.d.ts +1 -0
- package/dist/test/setup.js +38 -0
- package/package.json +253 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
/**
|
|
14
|
+
* Copyright 2026 Goodpatch Inc.
|
|
15
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
16
|
+
*/
|
|
17
|
+
import * as React from "react";
|
|
18
|
+
import { cva } from "class-variance-authority";
|
|
19
|
+
import { Icon } from "../../../components/ui/icon/index.js";
|
|
20
|
+
import { IconButton } from "../../../components/ui/icon-button/index.js";
|
|
21
|
+
import { cn } from "../../../lib/utils.js";
|
|
22
|
+
// ステータスごとのスタイル定義
|
|
23
|
+
const inlineMessageVariants = cva("relative w-[448px] max-w-full rounded-md border p-3 flex gap-2 items-start", {
|
|
24
|
+
variants: {
|
|
25
|
+
status: {
|
|
26
|
+
info: "bg-info-50 border-info-300",
|
|
27
|
+
warning: "bg-warning-50 border-warning-200",
|
|
28
|
+
negative: "bg-negative-50 border-negative-300",
|
|
29
|
+
success: "bg-success-50 border-success-300",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
status: "info",
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
// ステータスごとのアイコン名
|
|
37
|
+
const statusIcons = {
|
|
38
|
+
info: "info",
|
|
39
|
+
warning: "warning",
|
|
40
|
+
negative: "error",
|
|
41
|
+
success: "check_circle",
|
|
42
|
+
};
|
|
43
|
+
// ステータスごとの色クラス
|
|
44
|
+
const statusColorClasses = {
|
|
45
|
+
info: "text-info-400",
|
|
46
|
+
warning: "text-warning-500",
|
|
47
|
+
negative: "text-negative-400",
|
|
48
|
+
success: "text-success-400",
|
|
49
|
+
};
|
|
50
|
+
const InlineMessageContext = React.createContext(null);
|
|
51
|
+
/**
|
|
52
|
+
* **概要 / Overview**
|
|
53
|
+
*
|
|
54
|
+
* - インラインメッセージはユーザーにシステムやサービスの状態を伝えるために使用するコンポーネントです。
|
|
55
|
+
* - en: The InlineMessage component is used to convey system or service status to users.
|
|
56
|
+
*
|
|
57
|
+
* **使用例 / Usage Example**
|
|
58
|
+
*
|
|
59
|
+
* ```tsx
|
|
60
|
+
* <InlineMessage status="info" onClose={() => console.log('closed')}>
|
|
61
|
+
* <InlineMessageTitle>情報</InlineMessageTitle>
|
|
62
|
+
* <InlineMessageDescription>
|
|
63
|
+
* これは情報メッセージです。
|
|
64
|
+
* </InlineMessageDescription>
|
|
65
|
+
* </InlineMessage>
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @param {InlineMessageProps} props
|
|
69
|
+
*/
|
|
70
|
+
const InlineMessage = React.forwardRef((_a, ref) => {
|
|
71
|
+
var { className, status = "info", onClose, isCloseTrigger = true, children } = _a, props = __rest(_a, ["className", "status", "onClose", "isCloseTrigger", "children"]);
|
|
72
|
+
// 一意なIDを生成 / en: Generate unique IDs
|
|
73
|
+
const baseId = React.useId();
|
|
74
|
+
const titleId = `${baseId}-title`;
|
|
75
|
+
const descId = `${baseId}-desc`;
|
|
76
|
+
// 子コンポーネントの登録状態 / en: Registration state of child components
|
|
77
|
+
const [hasTitle, setHasTitle] = React.useState(false);
|
|
78
|
+
const [hasDesc, setHasDesc] = React.useState(false);
|
|
79
|
+
// 子コンポーネントが自分を登録するコールバック
|
|
80
|
+
// en: Callbacks for child components to register themselves
|
|
81
|
+
const registerTitle = React.useCallback(() => {
|
|
82
|
+
setHasTitle(true);
|
|
83
|
+
return () => setHasTitle(false);
|
|
84
|
+
}, []);
|
|
85
|
+
const registerDesc = React.useCallback(() => {
|
|
86
|
+
setHasDesc(true);
|
|
87
|
+
return () => setHasDesc(false);
|
|
88
|
+
}, []);
|
|
89
|
+
// Context値をメモ化 / en: Memoize context value
|
|
90
|
+
const contextValue = React.useMemo(() => ({ titleId, descId, registerTitle, registerDesc }), [titleId, descId, registerTitle, registerDesc]);
|
|
91
|
+
// ステータスに対応するアイコン情報の取得
|
|
92
|
+
const statusIcon = status ? statusIcons[status] : statusIcons.info;
|
|
93
|
+
// ステータスに対応する色クラスの取得
|
|
94
|
+
const colorClass = status
|
|
95
|
+
? statusColorClasses[status]
|
|
96
|
+
: statusColorClasses.info;
|
|
97
|
+
return (_jsx(InlineMessageContext.Provider, { value: contextValue, children: _jsxs("div", Object.assign({ ref: ref, role: "alert", "aria-labelledby": hasTitle ? titleId : undefined, "aria-describedby": hasDesc ? descId : undefined, className: cn(inlineMessageVariants({ status }), className) }, props, { children: [_jsx("span", { className: "inline-flex p-1 items-center justify-center", "aria-hidden": "true", children: _jsx(Icon, { icon: statusIcon, size: 6, fill: false, className: cn(colorClass) }) }), _jsx("div", { className: "flex flex-col flex-1 min-h-8", children: children }), isCloseTrigger && onClose && (_jsx(IconButton, { icon: "close", size: "sm", variant: "ghost", theme: "neutral", onClick: onClose, "aria-label": "\u9589\u3058\u308B", className: "shrink-0" }))] })) }));
|
|
98
|
+
});
|
|
99
|
+
InlineMessage.displayName = "InlineMessage";
|
|
100
|
+
/**
|
|
101
|
+
* **概要 / Overview**
|
|
102
|
+
*
|
|
103
|
+
* - インラインメッセージのタイトルコンポーネントです。
|
|
104
|
+
* - en: Title component for inline messages.
|
|
105
|
+
*
|
|
106
|
+
* **使用例 / Usage Example**
|
|
107
|
+
*
|
|
108
|
+
* ```tsx
|
|
109
|
+
* <InlineMessageTitle>タイトル</InlineMessageTitle>
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* @param {React.HTMLAttributes<HTMLSpanElement>} props
|
|
113
|
+
*/
|
|
114
|
+
const InlineMessageTitle = React.forwardRef((_a, ref) => {
|
|
115
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
116
|
+
const context = React.useContext(InlineMessageContext);
|
|
117
|
+
// マウント時に親へ登録、アンマウント時に解除
|
|
118
|
+
// en: Register on mount, unregister on unmount
|
|
119
|
+
React.useEffect(() => {
|
|
120
|
+
if (!context)
|
|
121
|
+
return;
|
|
122
|
+
return context.registerTitle();
|
|
123
|
+
}, [context]);
|
|
124
|
+
return (_jsx("div", { className: cn("flex items-center min-h-8"), children: _jsx("span", Object.assign({ ref: ref, id: context === null || context === void 0 ? void 0 : context.titleId, className: cn("character-3-bold-pro text-text-high", className) }, props)) }));
|
|
125
|
+
});
|
|
126
|
+
InlineMessageTitle.displayName = "InlineMessageTitle";
|
|
127
|
+
/**
|
|
128
|
+
* **概要 / Overview**
|
|
129
|
+
*
|
|
130
|
+
* - インラインメッセージの説明文コンポーネントです。
|
|
131
|
+
* - en: Description component for inline messages.
|
|
132
|
+
*
|
|
133
|
+
* **使用例 / Usage Example**
|
|
134
|
+
*
|
|
135
|
+
* ```tsx
|
|
136
|
+
* <InlineMessageDescription>詳細な説明文</InlineMessageDescription>
|
|
137
|
+
* ```
|
|
138
|
+
*
|
|
139
|
+
* @param {React.HTMLAttributes<HTMLParagraphElement>} props
|
|
140
|
+
*/
|
|
141
|
+
const InlineMessageDescription = React.forwardRef((_a, ref) => {
|
|
142
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
143
|
+
const context = React.useContext(InlineMessageContext);
|
|
144
|
+
// マウント時に親へ登録、アンマウント時に解除
|
|
145
|
+
// en: Register on mount, unregister on unmount
|
|
146
|
+
React.useEffect(() => {
|
|
147
|
+
if (!context)
|
|
148
|
+
return;
|
|
149
|
+
return context.registerDesc();
|
|
150
|
+
}, [context]);
|
|
151
|
+
return (_jsx("div", { className: cn("flex items-center min-h-8"), children: _jsx("p", Object.assign({ ref: ref, id: context === null || context === void 0 ? void 0 : context.descId, className: cn("character-3-regular-pro text-text-middle", className) }, props)) }));
|
|
152
|
+
});
|
|
153
|
+
InlineMessageDescription.displayName = "InlineMessageDescription";
|
|
154
|
+
export { InlineMessage, InlineMessageTitle, InlineMessageDescription };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 Goodpatch Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { type VariantProps } from "class-variance-authority";
|
|
7
|
+
declare const inputVariants: (props?: ({
|
|
8
|
+
size?: "sm" | "md" | "lg" | null | undefined;
|
|
9
|
+
isInvalid?: boolean | null | undefined;
|
|
10
|
+
isDisabled?: boolean | null | undefined;
|
|
11
|
+
isFocused?: boolean | null | undefined;
|
|
12
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
13
|
+
type InputVariantProps = VariantProps<typeof inputVariants>;
|
|
14
|
+
export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> {
|
|
15
|
+
/**
|
|
16
|
+
* Inputサイズ指定
|
|
17
|
+
* en: Input size specification
|
|
18
|
+
*/
|
|
19
|
+
size?: InputVariantProps["size"];
|
|
20
|
+
/**
|
|
21
|
+
* 無効にするかどうか
|
|
22
|
+
* en: Whether to disable the input
|
|
23
|
+
*/
|
|
24
|
+
isDisabled?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* フォーカスするかどうか
|
|
27
|
+
* en: Whether to focus the input
|
|
28
|
+
*/
|
|
29
|
+
isFocused?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* フィールドが無効かどうか
|
|
32
|
+
* en: Whether the field is invalid
|
|
33
|
+
*/
|
|
34
|
+
isInvalid?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* アイコンボタンを有効にするフラグ
|
|
37
|
+
* en: Flag to enable the icon button
|
|
38
|
+
*/
|
|
39
|
+
isTrigger?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* ボタンに表示するアイコン名
|
|
42
|
+
* en: Icon name displayed in the button
|
|
43
|
+
*/
|
|
44
|
+
triggerIcon?: string;
|
|
45
|
+
/**
|
|
46
|
+
* アイコンボタンのアクセシビリティラベル
|
|
47
|
+
* en: Accessibility label for the icon button
|
|
48
|
+
*/
|
|
49
|
+
triggerAriaLabel?: string;
|
|
50
|
+
/**
|
|
51
|
+
* アイコンボタンクリック時のコールバック
|
|
52
|
+
* en: Callback function for icon button click
|
|
53
|
+
*/
|
|
54
|
+
onIconButtonClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* **概要 / Overview**
|
|
58
|
+
*
|
|
59
|
+
* - インプットはテキストフィールドの形式でユーザーからの入力を取得するために使用するコンポーネントです。
|
|
60
|
+
* - en: The Input component is used to capture user input in the form of a text field.
|
|
61
|
+
*
|
|
62
|
+
* **アンチパターン / Anti-patterns**
|
|
63
|
+
*
|
|
64
|
+
* - Input の横にアイコンボタンを手動で配置しないでください。`isTrigger` / `triggerIcon` props を使用してください。
|
|
65
|
+
* en: Do not manually place an IconButton next to Input. Use `isTrigger` / `triggerIcon` props instead.
|
|
66
|
+
* - Input と横並びの Button は原則同じサイズにしてください。デフォルトの Input には `Button size="md"`、省スペース UI では `Input size="sm"` と `Button size="sm"` を使ってください。
|
|
67
|
+
* en: Keep Button size aligned when placing it next to Input. Use `Button size="md"` with the default Input, or pair `Input size="sm"` with `Button size="sm"` in compact UIs.
|
|
68
|
+
* - 無効状態は `isDisabled` を優先して使ってください。HTML 標準の `disabled` も互換のため受け付けますが、Sparkle Design のコードでは `isDisabled` に統一します。
|
|
69
|
+
* en: Prefer `isDisabled` for disabled state. The native `disabled` prop is still supported for compatibility, but Sparkle Design code should standardize on `isDisabled`.
|
|
70
|
+
*
|
|
71
|
+
* ```tsx
|
|
72
|
+
* // ✅ Correct
|
|
73
|
+
* <>
|
|
74
|
+
* <Input
|
|
75
|
+
* isTrigger
|
|
76
|
+
* triggerIcon="search"
|
|
77
|
+
* triggerAriaLabel="検索"
|
|
78
|
+
* onIconButtonClick={() => {}}
|
|
79
|
+
* />
|
|
80
|
+
* <div className="flex gap-2">
|
|
81
|
+
* <Input placeholder="検索..." />
|
|
82
|
+
* <Button size="md">検索</Button>
|
|
83
|
+
* </div>
|
|
84
|
+
* <div className="flex gap-2">
|
|
85
|
+
* <Input size="sm" placeholder="メッセージ" />
|
|
86
|
+
* <Button size="sm">送信</Button>
|
|
87
|
+
* </div>
|
|
88
|
+
* <Input isDisabled placeholder="無効状態" />
|
|
89
|
+
* </>
|
|
90
|
+
*
|
|
91
|
+
* // ❌ Wrong - 手動配置
|
|
92
|
+
* <div className="flex">
|
|
93
|
+
* <Input />
|
|
94
|
+
* <IconButton icon="search" aria-label="検索" />
|
|
95
|
+
* </div>
|
|
96
|
+
*
|
|
97
|
+
* // ❌ Wrong - サイズ不一致
|
|
98
|
+
* <div className="flex gap-2">
|
|
99
|
+
* <Input placeholder="検索..." />
|
|
100
|
+
* <Button size="sm">検索</Button>
|
|
101
|
+
* </div>
|
|
102
|
+
* ```
|
|
103
|
+
|
|
104
|
+
* **使用例 / Usage Example**
|
|
105
|
+
*
|
|
106
|
+
* ```tsx
|
|
107
|
+
* <Input
|
|
108
|
+
* size="md"
|
|
109
|
+
* placeholder="テキストを入力"
|
|
110
|
+
* isTrigger
|
|
111
|
+
* triggerIcon="search"
|
|
112
|
+
* />
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* @param {InputProps} props
|
|
116
|
+
*/
|
|
117
|
+
declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
|
|
118
|
+
export { Input, inputVariants };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import figma from "@figma/code-connect";
|
|
3
|
+
import { Input } from "./index.js";
|
|
4
|
+
/**
|
|
5
|
+
* -- This file was auto-generated by Code Connect --
|
|
6
|
+
* `props` includes a mapping from your code props to Figma properties.
|
|
7
|
+
* You should check this is correct, and update the `example` function
|
|
8
|
+
* to return the code example you'd like to see in Figma
|
|
9
|
+
*/
|
|
10
|
+
figma.connect(Input, "https://www.figma.com/design/7alBZXZf65YgcII41TWT0r/Sparkle-Design?node-id=1274%3A63045", {
|
|
11
|
+
props: {
|
|
12
|
+
// These props were automatically mapped based on your linked code:
|
|
13
|
+
size: figma.enum("size", {
|
|
14
|
+
sm: "sm",
|
|
15
|
+
md: "md",
|
|
16
|
+
lg: "lg",
|
|
17
|
+
}),
|
|
18
|
+
isDisabled: figma.enum("state", {
|
|
19
|
+
disabled: true,
|
|
20
|
+
}),
|
|
21
|
+
// isFocused: figma.boolean("isFocused"),
|
|
22
|
+
isInvalid: figma.boolean("isInvalid"),
|
|
23
|
+
defaultValue: figma.boolean("isEmpty", {
|
|
24
|
+
true: undefined,
|
|
25
|
+
false: figma.string("value"),
|
|
26
|
+
}),
|
|
27
|
+
placeholder: figma.boolean("isEmpty", {
|
|
28
|
+
true: figma.string("value"),
|
|
29
|
+
false: undefined,
|
|
30
|
+
}),
|
|
31
|
+
// No matching props could be found for these Figma properties:
|
|
32
|
+
isTrigger: figma.boolean("isTrigger"),
|
|
33
|
+
// "isFocused": figma.boolean('isFocused'),
|
|
34
|
+
},
|
|
35
|
+
example: props => (_jsx(Input, { size: props.size, isDisabled: props.isDisabled, isInvalid: props.isInvalid, defaultValue: props.defaultValue, isTrigger: props.isTrigger, placeholder: props.placeholder })),
|
|
36
|
+
});
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
/**
|
|
14
|
+
* Copyright 2026 Goodpatch Inc.
|
|
15
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
16
|
+
*/
|
|
17
|
+
import * as React from "react";
|
|
18
|
+
import { cva } from "class-variance-authority";
|
|
19
|
+
import { cn } from "../../../lib/utils.js";
|
|
20
|
+
import { IconButton } from "../../../components/ui/icon-button/index.js";
|
|
21
|
+
// 入力フィールドのスタイル定義
|
|
22
|
+
const inputVariants = cva("flex gap-0 items-center w-full rounded-action border bg-white transition-colors p-1", {
|
|
23
|
+
variants: {
|
|
24
|
+
size: {
|
|
25
|
+
sm: "h-8 character-2-regular-pro",
|
|
26
|
+
md: "h-10 character-3-regular-pro",
|
|
27
|
+
lg: "h-12 character-4-regular-pro",
|
|
28
|
+
},
|
|
29
|
+
isInvalid: {
|
|
30
|
+
true: "border-negative-500",
|
|
31
|
+
false: "border-neutral-500",
|
|
32
|
+
},
|
|
33
|
+
isDisabled: {
|
|
34
|
+
true: "cursor-not-allowed",
|
|
35
|
+
false: "",
|
|
36
|
+
},
|
|
37
|
+
isFocused: {
|
|
38
|
+
true: "ring-2 ring-[var(--color-ring-normal)] ring-offset-2 outline-hidden",
|
|
39
|
+
false: "",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
compoundVariants: [
|
|
43
|
+
// 通常状態
|
|
44
|
+
{
|
|
45
|
+
isInvalid: false,
|
|
46
|
+
isDisabled: false,
|
|
47
|
+
className: "border-neutral-500 hover:border-neutral-600",
|
|
48
|
+
},
|
|
49
|
+
// エラー状態
|
|
50
|
+
{
|
|
51
|
+
isInvalid: true,
|
|
52
|
+
isDisabled: false,
|
|
53
|
+
className: "border-negative-500 hover:border-negative-600 bg-white",
|
|
54
|
+
},
|
|
55
|
+
// 無効状態
|
|
56
|
+
{
|
|
57
|
+
isInvalid: false,
|
|
58
|
+
isDisabled: true,
|
|
59
|
+
className: "border-neutral-200 bg-neutral-50",
|
|
60
|
+
},
|
|
61
|
+
// エラー+無効状態
|
|
62
|
+
{
|
|
63
|
+
isInvalid: true,
|
|
64
|
+
isDisabled: true,
|
|
65
|
+
className: "border-negative-200 bg-neutral-50",
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
defaultVariants: {
|
|
69
|
+
size: "md",
|
|
70
|
+
isInvalid: false,
|
|
71
|
+
isDisabled: false,
|
|
72
|
+
isFocused: false,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
// 複数のrefをマージするユーティリティ関数
|
|
76
|
+
function useMergeRefs(...refs) {
|
|
77
|
+
return React.useCallback((value) => {
|
|
78
|
+
refs.forEach(ref => {
|
|
79
|
+
if (typeof ref === "function") {
|
|
80
|
+
ref(value);
|
|
81
|
+
}
|
|
82
|
+
else if (ref != null) {
|
|
83
|
+
ref.current = value;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}, [refs]);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* **概要 / Overview**
|
|
90
|
+
*
|
|
91
|
+
* - インプットはテキストフィールドの形式でユーザーからの入力を取得するために使用するコンポーネントです。
|
|
92
|
+
* - en: The Input component is used to capture user input in the form of a text field.
|
|
93
|
+
*
|
|
94
|
+
* **アンチパターン / Anti-patterns**
|
|
95
|
+
*
|
|
96
|
+
* - Input の横にアイコンボタンを手動で配置しないでください。`isTrigger` / `triggerIcon` props を使用してください。
|
|
97
|
+
* en: Do not manually place an IconButton next to Input. Use `isTrigger` / `triggerIcon` props instead.
|
|
98
|
+
* - Input と横並びの Button は原則同じサイズにしてください。デフォルトの Input には `Button size="md"`、省スペース UI では `Input size="sm"` と `Button size="sm"` を使ってください。
|
|
99
|
+
* en: Keep Button size aligned when placing it next to Input. Use `Button size="md"` with the default Input, or pair `Input size="sm"` with `Button size="sm"` in compact UIs.
|
|
100
|
+
* - 無効状態は `isDisabled` を優先して使ってください。HTML 標準の `disabled` も互換のため受け付けますが、Sparkle Design のコードでは `isDisabled` に統一します。
|
|
101
|
+
* en: Prefer `isDisabled` for disabled state. The native `disabled` prop is still supported for compatibility, but Sparkle Design code should standardize on `isDisabled`.
|
|
102
|
+
*
|
|
103
|
+
* ```tsx
|
|
104
|
+
* // ✅ Correct
|
|
105
|
+
* <>
|
|
106
|
+
* <Input
|
|
107
|
+
* isTrigger
|
|
108
|
+
* triggerIcon="search"
|
|
109
|
+
* triggerAriaLabel="検索"
|
|
110
|
+
* onIconButtonClick={() => {}}
|
|
111
|
+
* />
|
|
112
|
+
* <div className="flex gap-2">
|
|
113
|
+
* <Input placeholder="検索..." />
|
|
114
|
+
* <Button size="md">検索</Button>
|
|
115
|
+
* </div>
|
|
116
|
+
* <div className="flex gap-2">
|
|
117
|
+
* <Input size="sm" placeholder="メッセージ" />
|
|
118
|
+
* <Button size="sm">送信</Button>
|
|
119
|
+
* </div>
|
|
120
|
+
* <Input isDisabled placeholder="無効状態" />
|
|
121
|
+
* </>
|
|
122
|
+
*
|
|
123
|
+
* // ❌ Wrong - 手動配置
|
|
124
|
+
* <div className="flex">
|
|
125
|
+
* <Input />
|
|
126
|
+
* <IconButton icon="search" aria-label="検索" />
|
|
127
|
+
* </div>
|
|
128
|
+
*
|
|
129
|
+
* // ❌ Wrong - サイズ不一致
|
|
130
|
+
* <div className="flex gap-2">
|
|
131
|
+
* <Input placeholder="検索..." />
|
|
132
|
+
* <Button size="sm">検索</Button>
|
|
133
|
+
* </div>
|
|
134
|
+
* ```
|
|
135
|
+
|
|
136
|
+
* **使用例 / Usage Example**
|
|
137
|
+
*
|
|
138
|
+
* ```tsx
|
|
139
|
+
* <Input
|
|
140
|
+
* size="md"
|
|
141
|
+
* placeholder="テキストを入力"
|
|
142
|
+
* isTrigger
|
|
143
|
+
* triggerIcon="search"
|
|
144
|
+
* />
|
|
145
|
+
* ```
|
|
146
|
+
*
|
|
147
|
+
* @param {InputProps} props
|
|
148
|
+
*/
|
|
149
|
+
const Input = React.forwardRef((_a, ref) => {
|
|
150
|
+
var { className, size, isInvalid = false, isDisabled = false, isTrigger = false, triggerIcon = "edit", triggerAriaLabel, onIconButtonClick, disabled, defaultValue, value, onChange, onBlur, onFocus } = _a, props = __rest(_a, ["className", "size", "isInvalid", "isDisabled", "isTrigger", "triggerIcon", "triggerAriaLabel", "onIconButtonClick", "disabled", "defaultValue", "value", "onChange", "onBlur", "onFocus"]);
|
|
151
|
+
// Refs
|
|
152
|
+
const containerRef = React.useRef(null);
|
|
153
|
+
const buttonRef = React.useRef(null);
|
|
154
|
+
const inputRef = React.useRef(null);
|
|
155
|
+
const mergedInputRef = useMergeRefs(inputRef, ref);
|
|
156
|
+
// 状態管理
|
|
157
|
+
const [isInputFocused, setIsInputFocused] = React.useState(false);
|
|
158
|
+
const [isIconButtonFocused, setIsIconButtonFocused] = React.useState(false);
|
|
159
|
+
// HTML標準のdisabled属性とisDisabledプロパティを組み合わせた実際の無効状態
|
|
160
|
+
const isInputDisabled = Boolean(isDisabled || disabled);
|
|
161
|
+
// 入力値が変更されたときの処理
|
|
162
|
+
const handleChange = React.useCallback((e) => {
|
|
163
|
+
if (isInputDisabled)
|
|
164
|
+
return;
|
|
165
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e);
|
|
166
|
+
}, [onChange, isInputDisabled]);
|
|
167
|
+
// Input要素のフォーカス処理
|
|
168
|
+
const handleInputFocus = React.useCallback((e) => {
|
|
169
|
+
if (isInputDisabled)
|
|
170
|
+
return;
|
|
171
|
+
setIsInputFocused(true);
|
|
172
|
+
onFocus === null || onFocus === void 0 ? void 0 : onFocus(e);
|
|
173
|
+
}, [isInputDisabled, onFocus]);
|
|
174
|
+
const handleInputBlur = React.useCallback((e) => {
|
|
175
|
+
// ボタンにフォーカスが移った場合は、入力欄のフォーカスは解除しない
|
|
176
|
+
if (buttonRef.current !== e.relatedTarget) {
|
|
177
|
+
setIsInputFocused(false);
|
|
178
|
+
}
|
|
179
|
+
onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
|
|
180
|
+
}, [onBlur]);
|
|
181
|
+
// ボタンのフォーカス処理
|
|
182
|
+
const handleIconButtonFocus = React.useCallback(() => {
|
|
183
|
+
if (isInputDisabled)
|
|
184
|
+
return;
|
|
185
|
+
setIsIconButtonFocused(true);
|
|
186
|
+
setIsInputFocused(false);
|
|
187
|
+
}, [isInputDisabled]);
|
|
188
|
+
const handleIconButtonBlur = React.useCallback((e) => {
|
|
189
|
+
// 入力欄にフォーカスが移った場合は、ボタンのフォーカスは解除しない
|
|
190
|
+
if (inputRef.current !== e.relatedTarget) {
|
|
191
|
+
setIsIconButtonFocused(false);
|
|
192
|
+
}
|
|
193
|
+
}, []);
|
|
194
|
+
// コンテナクリック時にインプットにフォーカスを当てる
|
|
195
|
+
const handleContainerClick = React.useCallback((e) => {
|
|
196
|
+
if (isInputDisabled)
|
|
197
|
+
return;
|
|
198
|
+
// アイコンボタン上でのクリックを除外
|
|
199
|
+
if (buttonRef.current &&
|
|
200
|
+
(buttonRef.current === e.target ||
|
|
201
|
+
buttonRef.current.contains(e.target))) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
// 入力要素が無効でなければフォーカスを当てる
|
|
205
|
+
if (inputRef.current) {
|
|
206
|
+
inputRef.current.focus();
|
|
207
|
+
}
|
|
208
|
+
}, [isInputDisabled]);
|
|
209
|
+
// 外部クリックでフォーカスを解除するためのハンドラ
|
|
210
|
+
React.useEffect(() => {
|
|
211
|
+
// 無効状態の場合はイベントリスナーを追加しない
|
|
212
|
+
if (isInputDisabled)
|
|
213
|
+
return;
|
|
214
|
+
const handleOutsideClick = (e) => {
|
|
215
|
+
// コンポーネント外のクリックを検出
|
|
216
|
+
if (containerRef.current &&
|
|
217
|
+
!containerRef.current.contains(e.target)) {
|
|
218
|
+
setIsInputFocused(false);
|
|
219
|
+
setIsIconButtonFocused(false);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
// イベントリスナーを追加
|
|
223
|
+
document.addEventListener("mousedown", handleOutsideClick);
|
|
224
|
+
// クリーンアップ関数
|
|
225
|
+
return () => {
|
|
226
|
+
document.removeEventListener("mousedown", handleOutsideClick);
|
|
227
|
+
};
|
|
228
|
+
}, [isInputDisabled]);
|
|
229
|
+
// ボタンサイズの計算
|
|
230
|
+
const iconButtonSize = React.useMemo(() => {
|
|
231
|
+
switch (size) {
|
|
232
|
+
case "sm":
|
|
233
|
+
return "xs";
|
|
234
|
+
case "lg":
|
|
235
|
+
return "md";
|
|
236
|
+
default:
|
|
237
|
+
return "sm";
|
|
238
|
+
}
|
|
239
|
+
}, [size]);
|
|
240
|
+
return (_jsxs("div", { ref: containerRef, className: cn(inputVariants({
|
|
241
|
+
size,
|
|
242
|
+
isInvalid,
|
|
243
|
+
isDisabled: isInputDisabled,
|
|
244
|
+
isFocused: isInputFocused && !isIconButtonFocused,
|
|
245
|
+
className,
|
|
246
|
+
}), !isInputDisabled && "cursor-text" // 入力可能な場合はテキストカーソルを表示
|
|
247
|
+
),
|
|
248
|
+
// NOTE: not supportエラーがLintで出るためコメントアウト
|
|
249
|
+
// aria-disabled={isInputDisabled}
|
|
250
|
+
// aria-invalid={isInvalid === null ? undefined : isInvalid}
|
|
251
|
+
onClick: handleContainerClick, role: "presentation", tabIndex: -1, children: [_jsx("input", Object.assign({ ref: mergedInputRef, disabled: isInputDisabled, "aria-invalid": isInvalid || undefined, className: cn("w-full h-full bg-transparent border-none outline-hidden focus:outline-hidden", "text-text-high placeholder:text-text-placeholder px-2", isInputDisabled &&
|
|
252
|
+
"cursor-not-allowed text-neutral-400 placeholder:text-text-disabled"), onChange: handleChange, onFocus: handleInputFocus, onBlur: handleInputBlur, defaultValue: defaultValue, value: value, "aria-disabled": isInputDisabled }, props)), isTrigger && (_jsx(IconButton, { ref: buttonRef, icon: triggerIcon, theme: "neutral", variant: "ghost", size: iconButtonSize, onClick: onIconButtonClick, isDisabled: isInputDisabled, disabled: isInputDisabled, type: "button" // フォーム内でデフォルトのsubmit動作を防ぐ
|
|
253
|
+
, "aria-label": triggerAriaLabel, onFocus: handleIconButtonFocus, onBlur: handleIconButtonBlur }))] }));
|
|
254
|
+
});
|
|
255
|
+
Input.displayName = "Input";
|
|
256
|
+
export { Input, inputVariants };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 Goodpatch Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { type InputProps } from "@/components/ui/input";
|
|
7
|
+
export interface InputPasswordProps extends Omit<InputProps, "type" | "triggerIcon" | "triggerAriaLabel" | "isTrigger" | "onIconButtonClick"> {
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* **概要 / Overview**
|
|
11
|
+
*
|
|
12
|
+
* - インプットパスワードはパスワードの情報をユーザーから取得するために使用するコンポーネントです。
|
|
13
|
+
* - en: The InputPassword component is used to capture password information from users.
|
|
14
|
+
*
|
|
15
|
+
* **使用例 / Usage Example**
|
|
16
|
+
*
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <InputPassword
|
|
19
|
+
* size="md"
|
|
20
|
+
* placeholder="パスワードを入力してください"
|
|
21
|
+
* />
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @param {InputPasswordProps} props
|
|
25
|
+
*/
|
|
26
|
+
declare const InputPassword: React.ForwardRefExoticComponent<InputPasswordProps & React.RefAttributes<HTMLInputElement>>;
|
|
27
|
+
export { InputPassword };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import figma from "@figma/code-connect";
|
|
3
|
+
import { InputPassword } from "./index.js";
|
|
4
|
+
/**
|
|
5
|
+
* -- This file was auto-generated by Code Connect --
|
|
6
|
+
* `props` includes a mapping from your code props to Figma properties.
|
|
7
|
+
* You should check this is correct, and update the `example` function
|
|
8
|
+
* to return the code example you'd like to see in Figma
|
|
9
|
+
*/
|
|
10
|
+
figma.connect(InputPassword, "https://www.figma.com/design/7alBZXZf65YgcII41TWT0r/Sparkle-Design?node-id=174%3A3296", {
|
|
11
|
+
props: {
|
|
12
|
+
// These props were automatically mapped based on your linked code:
|
|
13
|
+
size: figma.enum("size", {
|
|
14
|
+
sm: "sm",
|
|
15
|
+
md: "md",
|
|
16
|
+
lg: "lg",
|
|
17
|
+
}),
|
|
18
|
+
disabled: figma.enum("state", {
|
|
19
|
+
disable: true,
|
|
20
|
+
}),
|
|
21
|
+
isFocused: figma.boolean("isFocused"),
|
|
22
|
+
isInvalid: figma.boolean("isInvaild"),
|
|
23
|
+
defaultValue: figma.boolean("isEmpty", {
|
|
24
|
+
true: undefined,
|
|
25
|
+
false: figma.string("value"),
|
|
26
|
+
}),
|
|
27
|
+
placeholder: figma.boolean("isEmpty", {
|
|
28
|
+
true: figma.string("value"),
|
|
29
|
+
false: undefined,
|
|
30
|
+
}),
|
|
31
|
+
// No matching props could be found for these Figma properties:
|
|
32
|
+
// "isFocused": figma.boolean('isFocused'),
|
|
33
|
+
// "value": figma.string('value')
|
|
34
|
+
},
|
|
35
|
+
example: props => (_jsx(InputPassword, { size: props.size, disabled: props.disabled, isFocused: props.isFocused, isInvalid: props.isInvalid, defaultValue: props.defaultValue, placeholder: props.placeholder })),
|
|
36
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright 2026 Goodpatch Inc.
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { Input } from "../../../components/ui/input/index.js";
|
|
8
|
+
/**
|
|
9
|
+
* **概要 / Overview**
|
|
10
|
+
*
|
|
11
|
+
* - インプットパスワードはパスワードの情報をユーザーから取得するために使用するコンポーネントです。
|
|
12
|
+
* - en: The InputPassword component is used to capture password information from users.
|
|
13
|
+
*
|
|
14
|
+
* **使用例 / Usage Example**
|
|
15
|
+
*
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <InputPassword
|
|
18
|
+
* size="md"
|
|
19
|
+
* placeholder="パスワードを入力してください"
|
|
20
|
+
* />
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @param {InputPasswordProps} props
|
|
24
|
+
*/
|
|
25
|
+
const InputPassword = React.forwardRef((props, ref) => {
|
|
26
|
+
// パスワードの表示状態を管理
|
|
27
|
+
const [isVisibility, setIsVisibility] = React.useState(false);
|
|
28
|
+
// パスワード表示切り替えハンドラー
|
|
29
|
+
const handleTogglePasswordVisibility = React.useCallback(() => {
|
|
30
|
+
setIsVisibility(prev => !prev);
|
|
31
|
+
}, []);
|
|
32
|
+
return (_jsx(Input, Object.assign({ ref: ref, type: isVisibility ? "text" : "password", triggerIcon: isVisibility ? "visibility_off" : "visibility", triggerAriaLabel: isVisibility ? "パスワードを隠す" : "パスワードを表示する", isTrigger: true, onIconButtonClick: handleTogglePasswordVisibility }, props)));
|
|
33
|
+
});
|
|
34
|
+
InputPassword.displayName = "InputPassword";
|
|
35
|
+
export { InputPassword };
|