@usefui/components 1.5.1 → 1.5.3
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/CHANGELOG.md +12 -0
- package/dist/index.d.mts +237 -9
- package/dist/index.d.ts +237 -9
- package/dist/index.js +1095 -311
- package/dist/index.mjs +1049 -278
- package/package.json +6 -6
- package/src/avatar/index.tsx +1 -1
- package/src/badge/index.tsx +3 -3
- package/src/breadcrumb/Breadcrumb.stories.tsx +36 -0
- package/src/breadcrumb/index.tsx +117 -0
- package/src/breadcrumb/styles/index.ts +19 -0
- package/src/button/Button.stories.tsx +80 -8
- package/src/button/index.tsx +67 -5
- package/src/button/styles/index.ts +82 -10
- package/src/card/Card.stories.tsx +57 -0
- package/src/card/index.tsx +55 -0
- package/src/card/styles/index.ts +72 -0
- package/src/copy-button/CopyButton.stories.tsx +29 -0
- package/src/copy-button/index.tsx +101 -0
- package/src/dialog/index.tsx +1 -1
- package/src/dropdown/index.tsx +1 -1
- package/src/field/Field.stories.tsx +39 -8
- package/src/field/index.tsx +5 -0
- package/src/field/styles/index.ts +38 -12
- package/src/index.ts +8 -0
- package/src/page/index.tsx +1 -1
- package/src/privacy-field/PrivacyField.stories.tsx +29 -0
- package/src/privacy-field/index.tsx +56 -0
- package/src/privacy-field/styles/index.ts +17 -0
- package/src/resizable/Resizable.stories.tsx +40 -0
- package/src/resizable/index.tsx +108 -0
- package/src/resizable/styles/index.ts +65 -0
- package/src/skeleton/Skeleton.stories.tsx +32 -0
- package/src/skeleton/index.tsx +43 -0
- package/src/skeleton/styles/index.ts +56 -0
- package/src/spinner/Spinner.stories.tsx +27 -0
- package/src/spinner/index.tsx +19 -0
- package/src/spinner/styles/index.ts +43 -0
- package/src/text-area/Textarea.stories.tsx +32 -0
- package/src/text-area/index.tsx +78 -0
- package/src/text-area/styles/index.ts +84 -0
- package/src/tooltip/index.tsx +4 -3
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { CardContainer, CardWrapper, CardsGrid } from "./styles";
|
|
5
|
+
|
|
6
|
+
import type { IComponentSize, TComponentShape } from "../../../../types";
|
|
7
|
+
|
|
8
|
+
export interface CardComposition {
|
|
9
|
+
Body: typeof CardBody;
|
|
10
|
+
Meta: typeof CardMeta;
|
|
11
|
+
Grid: typeof CardGrid;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface CardGridProps extends React.ComponentProps<"div">, IComponentSize {}
|
|
15
|
+
interface CardProps extends React.ComponentProps<"div"> {
|
|
16
|
+
shape?: TComponentShape;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const CardGrid = (props: CardGridProps) => {
|
|
20
|
+
const { sizing = "medium", children } = props;
|
|
21
|
+
|
|
22
|
+
return <CardsGrid data-size={sizing}>{children}</CardsGrid>;
|
|
23
|
+
};
|
|
24
|
+
CardGrid.displayName = "Card.Grid";
|
|
25
|
+
|
|
26
|
+
const CardMeta = (props: React.ComponentProps<"div">) => {
|
|
27
|
+
const { children } = props;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className="p-y-medium-20 p-x-medium-30" {...props}>
|
|
31
|
+
{children}
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
CardMeta.displayName = "Card.Meta";
|
|
36
|
+
|
|
37
|
+
const CardBody = (props: CardProps) => {
|
|
38
|
+
const { shape = "smooth", children } = props;
|
|
39
|
+
|
|
40
|
+
return <CardWrapper data-shape={shape}>{children}</CardWrapper>;
|
|
41
|
+
};
|
|
42
|
+
CardBody.displayName = "Card.Body";
|
|
43
|
+
|
|
44
|
+
const Card = (props: CardProps) => {
|
|
45
|
+
const { shape = "smooth", children } = props;
|
|
46
|
+
|
|
47
|
+
return <CardContainer data-shape={shape}> {children}</CardContainer>;
|
|
48
|
+
};
|
|
49
|
+
Card.displayName = "Card";
|
|
50
|
+
|
|
51
|
+
Card.Grid = CardGrid;
|
|
52
|
+
Card.Meta = CardMeta;
|
|
53
|
+
Card.Body = CardBody;
|
|
54
|
+
|
|
55
|
+
export { Card, CardGrid, CardBody, CardMeta };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import styled, { css } from "styled-components";
|
|
4
|
+
|
|
5
|
+
const GridDefaultStyles = css`
|
|
6
|
+
display: grid;
|
|
7
|
+
grid-gap: var(--measurement-medium-30) var(--measurement-medium-30);
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
|
|
10
|
+
@media (max-width: 768px) {
|
|
11
|
+
grid-template-columns: repeat(auto-fill, minmax(100%, 1fr));
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
const GridSizeStyles = css`
|
|
16
|
+
&[data-size="small"] {
|
|
17
|
+
grid-template-columns: repeat(
|
|
18
|
+
auto-fill,
|
|
19
|
+
minmax(var(--measurement-large-80), 1fr)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
&[data-size="medium"] {
|
|
23
|
+
grid-template-columns: repeat(
|
|
24
|
+
auto-fill,
|
|
25
|
+
minmax(var(--measurement-large-90), 1fr)
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
&[data-size="large"] {
|
|
29
|
+
grid-template-columns: repeat(
|
|
30
|
+
auto-fill,
|
|
31
|
+
minmax(calc(var(--measurement-large-90) * 1.2), 1fr)
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
const CardShapeStyles = css`
|
|
36
|
+
&[data-shape="square"] {
|
|
37
|
+
border-radius: 0;
|
|
38
|
+
}
|
|
39
|
+
&[data-shape="smooth"] {
|
|
40
|
+
border-radius: var(--measurement-medium-30);
|
|
41
|
+
}
|
|
42
|
+
&[data-shape="round"] {
|
|
43
|
+
border-radius: var(--measurement-medium-60);
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
const CardDefaultStyles = css`
|
|
47
|
+
${CardShapeStyles}
|
|
48
|
+
|
|
49
|
+
box-sizing: border-box;
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
export const CardContainer = styled.div`
|
|
53
|
+
width: 100%;
|
|
54
|
+
background-color: var(--font-color-alpha-10);
|
|
55
|
+
|
|
56
|
+
${CardDefaultStyles}
|
|
57
|
+
`;
|
|
58
|
+
export const CardWrapper = styled.div`
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
gap: var(--measurement-large-10);
|
|
62
|
+
padding: var(--measurement-medium-60);
|
|
63
|
+
background-color: var(--contrast-color);
|
|
64
|
+
border: var(--measurement-small-10) solid var(--font-color-alpha-10);
|
|
65
|
+
|
|
66
|
+
${CardDefaultStyles}
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
export const CardsGrid = styled.div`
|
|
70
|
+
${GridDefaultStyles}
|
|
71
|
+
${GridSizeStyles}
|
|
72
|
+
`;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { Page, CopyButton } from "..";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* CopyButton are used to copy stored values when clicked.
|
|
7
|
+
*/
|
|
8
|
+
const meta = {
|
|
9
|
+
title: "Components/CopyButton",
|
|
10
|
+
component: CopyButton,
|
|
11
|
+
tags: ["autodocs"],
|
|
12
|
+
} satisfies Meta<typeof CopyButton>;
|
|
13
|
+
export default meta;
|
|
14
|
+
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
export const Default: Story = {
|
|
17
|
+
args: {
|
|
18
|
+
value: "Hello, world!",
|
|
19
|
+
},
|
|
20
|
+
render: ({ ...args }) => (
|
|
21
|
+
<Page>
|
|
22
|
+
<Page.Content className="p-large-30">
|
|
23
|
+
<CopyButton variant="border" sizing="medium" {...args}>
|
|
24
|
+
Click to copy
|
|
25
|
+
</CopyButton>
|
|
26
|
+
</Page.Content>
|
|
27
|
+
</Page>
|
|
28
|
+
),
|
|
29
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
|
|
5
|
+
import { Tooltip, Button } from "../";
|
|
6
|
+
import type { IButtonProperties } from "../";
|
|
7
|
+
|
|
8
|
+
type TooltipValueProperties = {
|
|
9
|
+
copyLabel?: string;
|
|
10
|
+
copiedLabel?: string;
|
|
11
|
+
};
|
|
12
|
+
interface CopyButtonProperties extends IButtonProperties {
|
|
13
|
+
delay?: number;
|
|
14
|
+
value: string;
|
|
15
|
+
tooltip?: TooltipValueProperties;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* CopyButton are used to copy stored values when clicked.
|
|
20
|
+
*
|
|
21
|
+
* @param {IButtonProperties} props - The props for the CopyButton component.
|
|
22
|
+
* @param {boolean} props.raw - Define whether the component is styled or not.
|
|
23
|
+
* @param {boolean} props.rawicon - Define whether the component is styles its svg children.
|
|
24
|
+
* @param {ComponentSizeEnum} props.sizing - The size of the component. Defaults to `medium`.
|
|
25
|
+
* @param {string} props.variant - The style definition used by the component.
|
|
26
|
+
* @param {string} props.value - The value copied when the CopyButton is clicked.
|
|
27
|
+
* @param {number} props.delay - The delay used to rendered the state change.
|
|
28
|
+
* @param {TooltipValueProperties} props.tooltip - The values used to convey the copy state.
|
|
29
|
+
* @param {ReactNode} props.children - The content to be rendered inside the CopyButton.
|
|
30
|
+
* @returns {ReactElement} The CopyButton component.
|
|
31
|
+
*/
|
|
32
|
+
export const CopyButton = ({
|
|
33
|
+
value,
|
|
34
|
+
delay,
|
|
35
|
+
tooltip,
|
|
36
|
+
children,
|
|
37
|
+
...restProps
|
|
38
|
+
}: CopyButtonProperties) => {
|
|
39
|
+
const timerRef = React.useRef<number | null>(null);
|
|
40
|
+
const [copied, setCopied] = React.useState(false);
|
|
41
|
+
|
|
42
|
+
const tooltipLabels = React.useMemo(() => {
|
|
43
|
+
return {
|
|
44
|
+
copy: tooltip?.copyLabel ?? "Copy",
|
|
45
|
+
copied: tooltip?.copiedLabel ?? "Copied!",
|
|
46
|
+
};
|
|
47
|
+
}, [tooltip]);
|
|
48
|
+
|
|
49
|
+
const copyToClipboard = async () => {
|
|
50
|
+
if (value == null) return;
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
await navigator.clipboard.writeText(value);
|
|
54
|
+
setCopied(true);
|
|
55
|
+
} catch {
|
|
56
|
+
// Best-effort fallback for older browsers
|
|
57
|
+
const ta = document.createElement("textarea");
|
|
58
|
+
ta.value = value;
|
|
59
|
+
ta.style.position = "fixed";
|
|
60
|
+
ta.style.opacity = "0";
|
|
61
|
+
document.body.appendChild(ta);
|
|
62
|
+
ta.select();
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
document.execCommand("copy");
|
|
66
|
+
setCopied(true);
|
|
67
|
+
} finally {
|
|
68
|
+
document.body.removeChild(ta);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (timerRef.current != null) window.clearTimeout(timerRef.current);
|
|
73
|
+
timerRef.current = window.setTimeout(() => setCopied(false), delay ?? 1000);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
React.useEffect(() => {
|
|
77
|
+
return () => {
|
|
78
|
+
if (timerRef.current != null) {
|
|
79
|
+
window.clearTimeout(timerRef.current);
|
|
80
|
+
timerRef.current = null;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}, []);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<Tooltip content={copied ? tooltipLabels.copied : tooltipLabels.copy}>
|
|
87
|
+
<Button
|
|
88
|
+
data-testId="copy-code"
|
|
89
|
+
aria-label="copy-code"
|
|
90
|
+
disabled={value == null}
|
|
91
|
+
aria-disabled={value == null}
|
|
92
|
+
variant={restProps?.variant ?? "ghost"}
|
|
93
|
+
onClick={copyToClipboard}
|
|
94
|
+
{...restProps}
|
|
95
|
+
>
|
|
96
|
+
{children}
|
|
97
|
+
</Button>
|
|
98
|
+
</Tooltip>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
101
|
+
CopyButton.displayName = "CopyButton";
|
package/src/dialog/index.tsx
CHANGED
|
@@ -38,7 +38,7 @@ export interface IDialogItemProperties
|
|
|
38
38
|
*
|
|
39
39
|
* @param {IDialogItemProperties} props - The props for the Dialog component.
|
|
40
40
|
* @param {boolean} props.raw - Define whether the component is styled or not.
|
|
41
|
-
* @param {ComponentSizeEnum} props.sizing - The size of the component.
|
|
41
|
+
* @param {ComponentSizeEnum} props.sizing - The size of the component. Defaults to "medium".
|
|
42
42
|
* @param {boolean} props.open - Whether the dialog is open or not.
|
|
43
43
|
* @param {ReactNode} props.children - The content to be rendered inside the dialog.
|
|
44
44
|
* @returns {ReactElement} The Dialog component.
|
package/src/dropdown/index.tsx
CHANGED
|
@@ -132,7 +132,7 @@ DropdownMenuTrigger.displayName = "DropdownMenu.Trigger";
|
|
|
132
132
|
*
|
|
133
133
|
* @param {IDropdownContentProperties} props - The props for the DropdownMenu.Content component.
|
|
134
134
|
* @param {boolean} props.raw - Define whether the component is styled or not.
|
|
135
|
-
* @param {ComponentSizeEnum} props.sizing - The size of the component.
|
|
135
|
+
* @param {ComponentSizeEnum} props.sizing - The size of the component. Defaults to "medium".
|
|
136
136
|
* @param {boolean} props.defaultOpen - The initial open state of the dropdown menu. Defaults to false.
|
|
137
137
|
* @param {ReactNode} props.children - The content to be rendered inside the dropdown menu.
|
|
138
138
|
* @returns {ReactElement} The DropdownMenu.Content component.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
3
|
|
|
4
|
-
import { Field } from "..";
|
|
4
|
+
import { Field, Page } from "..";
|
|
5
5
|
import { ComponentVariantEnum, ComponentSizeEnum } from "../../../../types";
|
|
6
6
|
|
|
7
7
|
const meta = {
|
|
@@ -131,16 +131,47 @@ export const Sizes = {
|
|
|
131
131
|
);
|
|
132
132
|
},
|
|
133
133
|
};
|
|
134
|
+
export const Shapes = {
|
|
135
|
+
render: ({ ...args }) => {
|
|
136
|
+
return (
|
|
137
|
+
<Page>
|
|
138
|
+
<Page.Content>
|
|
139
|
+
<div className="flex align-center justify-center flex-wrap h-100 g-medium-30">
|
|
140
|
+
{["square", "smooth", "round"].map((item) => (
|
|
141
|
+
<Field.Root key={item}>
|
|
142
|
+
<Field
|
|
143
|
+
shape={item}
|
|
144
|
+
sizing="medium"
|
|
145
|
+
placeholder={item}
|
|
146
|
+
variant="secondary"
|
|
147
|
+
/>
|
|
148
|
+
</Field.Root>
|
|
149
|
+
))}
|
|
150
|
+
</div>
|
|
151
|
+
</Page.Content>
|
|
152
|
+
</Page>
|
|
153
|
+
);
|
|
154
|
+
},
|
|
155
|
+
};
|
|
134
156
|
export const Variants = {
|
|
135
157
|
render: ({ ...args }) => {
|
|
136
158
|
return (
|
|
137
|
-
<
|
|
138
|
-
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
159
|
+
<Page>
|
|
160
|
+
<Page.Content>
|
|
161
|
+
<div className="flex align-center justify-center flex-wrap h-100 g-medium-30">
|
|
162
|
+
{["primary", "secondary", "ghost"].map((item) => (
|
|
163
|
+
<Field.Root key={item}>
|
|
164
|
+
<Field
|
|
165
|
+
shape="smooth"
|
|
166
|
+
sizing="medium"
|
|
167
|
+
placeholder={item}
|
|
168
|
+
variant={item}
|
|
169
|
+
/>
|
|
170
|
+
</Field.Root>
|
|
171
|
+
))}
|
|
172
|
+
</div>
|
|
173
|
+
</Page.Content>
|
|
174
|
+
</Page>
|
|
144
175
|
);
|
|
145
176
|
},
|
|
146
177
|
};
|
package/src/field/index.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
IComponentSize,
|
|
11
11
|
ComponentVariantEnum,
|
|
12
12
|
IComponentVariant,
|
|
13
|
+
TComponentShape,
|
|
13
14
|
} from "../../../../types";
|
|
14
15
|
|
|
15
16
|
export enum MetaVariantEnum {
|
|
@@ -26,6 +27,7 @@ export interface IField
|
|
|
26
27
|
IComponentSize,
|
|
27
28
|
IComponentVariant,
|
|
28
29
|
IComponentStyling {
|
|
30
|
+
shape?: TComponentShape;
|
|
29
31
|
hint?: string;
|
|
30
32
|
error?: string;
|
|
31
33
|
}
|
|
@@ -58,6 +60,7 @@ export interface IFieldComposition {
|
|
|
58
60
|
* @param {boolean} props.raw - Define whether the component is styled or not.
|
|
59
61
|
* @param {ComponentSizeEnum} props.sizing - The size of the component. Defaults to `medium`.
|
|
60
62
|
* @param {string} props.variant - The style definition used by the component.
|
|
63
|
+
* @param {TComponentShape} props.shape - The size of the component. Defaults to `smooth`.
|
|
61
64
|
* @param {string} props.error - The error message to display.
|
|
62
65
|
* @param {string} props.hint - The hint message to display.
|
|
63
66
|
* @returns {ReactElement} The Field component.
|
|
@@ -67,6 +70,7 @@ const Field = (props: IField) => {
|
|
|
67
70
|
raw,
|
|
68
71
|
sizing = ComponentSizeEnum.Medium,
|
|
69
72
|
variant = ComponentVariantEnum.Primary,
|
|
73
|
+
shape = "smooth",
|
|
70
74
|
error,
|
|
71
75
|
hint,
|
|
72
76
|
...restProps
|
|
@@ -85,6 +89,7 @@ const Field = (props: IField) => {
|
|
|
85
89
|
data-error={Boolean(error)}
|
|
86
90
|
data-variant={variant}
|
|
87
91
|
data-size={sizing}
|
|
92
|
+
data-shape={shape}
|
|
88
93
|
data-raw={Boolean(raw)}
|
|
89
94
|
{...restProps}
|
|
90
95
|
/>
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import styled, { css } from "styled-components";
|
|
2
2
|
|
|
3
|
-
const FieldDefaultStyles = css`
|
|
3
|
+
export const FieldDefaultStyles = css`
|
|
4
4
|
outline: none;
|
|
5
|
-
cursor:
|
|
5
|
+
cursor: text;
|
|
6
6
|
display: flex;
|
|
7
7
|
align-items: center;
|
|
8
8
|
justify-content: center;
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
|
|
11
|
+
font-size: var(--fontsize-medium-20);
|
|
9
12
|
|
|
10
|
-
font-size: var(--fontsize-small-80);
|
|
11
|
-
font-weight: 500;
|
|
12
13
|
line-height: 1.1;
|
|
13
14
|
letter-spacing: calc(
|
|
14
15
|
var(--fontsize-small-10) - ((var(--fontsize-small-10) * 1.066))
|
|
15
16
|
);
|
|
16
17
|
|
|
17
18
|
border: var(--measurement-small-10) solid transparent;
|
|
18
|
-
border-radius: var(--measurement-medium-30);
|
|
19
19
|
backdrop-filter: blur(var(--measurement-small-10));
|
|
20
20
|
color: var(--font-color-alpha-60);
|
|
21
21
|
width: fit-content;
|
|
@@ -33,29 +33,37 @@ const FieldDefaultStyles = css`
|
|
|
33
33
|
&:focus,
|
|
34
34
|
&:active {
|
|
35
35
|
color: var(--font-color);
|
|
36
|
-
|
|
37
36
|
svg,
|
|
38
37
|
span,
|
|
39
38
|
img {
|
|
40
39
|
opacity: 1;
|
|
41
40
|
}
|
|
42
41
|
}
|
|
42
|
+
|
|
43
43
|
&::placeholder {
|
|
44
44
|
color: var(--font-color-alpha-30);
|
|
45
45
|
}
|
|
46
|
+
|
|
46
47
|
&:disabled {
|
|
47
48
|
cursor: not-allowed;
|
|
48
49
|
opacity: 0.6;
|
|
49
50
|
}
|
|
50
51
|
`;
|
|
51
|
-
const FieldVariantsStyles = css`
|
|
52
|
+
export const FieldVariantsStyles = css`
|
|
52
53
|
&[data-variant="primary"] {
|
|
53
54
|
background-color: var(--font-color-alpha-10);
|
|
55
|
+
border-color: var(--font-color-alpha-10);
|
|
56
|
+
|
|
57
|
+
&:focus,
|
|
58
|
+
&:active {
|
|
59
|
+
box-shadow: 0 0 0 var(--measurement-small-30) var(--font-color-alpha-10);
|
|
60
|
+
}
|
|
54
61
|
|
|
55
62
|
&[data-error="true"] {
|
|
56
63
|
color: var(--color-red);
|
|
57
64
|
background-color: var(--alpha-red-10);
|
|
58
65
|
border-color: var(--alpha-red-10);
|
|
66
|
+
box-shadow: 0 0 0 var(--measurement-small-30) var(--alpha-red-10);
|
|
59
67
|
}
|
|
60
68
|
}
|
|
61
69
|
|
|
@@ -66,7 +74,12 @@ const FieldVariantsStyles = css`
|
|
|
66
74
|
&:hover,
|
|
67
75
|
&:focus,
|
|
68
76
|
&:active {
|
|
69
|
-
|
|
77
|
+
border-color: var(--font-color-alpha-20);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
&:focus,
|
|
81
|
+
&:active {
|
|
82
|
+
box-shadow: 0 0 0 var(--measurement-small-30) var(--font-color-alpha-10);
|
|
70
83
|
}
|
|
71
84
|
|
|
72
85
|
&[data-error="true"] {
|
|
@@ -77,6 +90,7 @@ const FieldVariantsStyles = css`
|
|
|
77
90
|
&:focus,
|
|
78
91
|
&:active {
|
|
79
92
|
background-color: var(--alpha-red-10);
|
|
93
|
+
box-shadow: 0 0 0 var(--measurement-small-30) var(--alpha-red-10);
|
|
80
94
|
}
|
|
81
95
|
}
|
|
82
96
|
}
|
|
@@ -100,26 +114,37 @@ const FieldVariantsStyles = css`
|
|
|
100
114
|
}
|
|
101
115
|
}
|
|
102
116
|
`;
|
|
103
|
-
const FieldSizeStyles = css`
|
|
117
|
+
export const FieldSizeStyles = css`
|
|
104
118
|
&[data-size="small"] {
|
|
105
|
-
|
|
119
|
+
font-size: var(--fontsize-small-60);
|
|
120
|
+
|
|
106
121
|
padding: 0 var(--measurement-medium-30);
|
|
107
122
|
min-width: var(--measurement-medium-60);
|
|
108
123
|
min-height: var(--measurement-medium-80);
|
|
109
124
|
}
|
|
110
125
|
&[data-size="medium"] {
|
|
111
|
-
gap: var(--measurement-medium-30);
|
|
112
126
|
padding: 0 var(--measurement-medium-30);
|
|
113
127
|
min-width: var(--measurement-medium-90);
|
|
114
128
|
min-height: var(--measurement-medium-90);
|
|
115
129
|
width: fit-content;
|
|
116
130
|
}
|
|
117
131
|
&[data-size="large"] {
|
|
118
|
-
padding: var(--measurement-medium-
|
|
132
|
+
padding: var(--measurement-medium-50);
|
|
119
133
|
min-width: var(--measurement-medium-90);
|
|
120
134
|
min-height: var(--measurement-medium-90);
|
|
121
135
|
}
|
|
122
136
|
`;
|
|
137
|
+
export const FieldShapeStyles = css`
|
|
138
|
+
&[data-shape="square"] {
|
|
139
|
+
border-radius: 0;
|
|
140
|
+
}
|
|
141
|
+
&[data-shape="smooth"] {
|
|
142
|
+
border-radius: var(--measurement-medium-20);
|
|
143
|
+
}
|
|
144
|
+
&[data-shape="round"] {
|
|
145
|
+
border-radius: var(--measurement-large-90);
|
|
146
|
+
}
|
|
147
|
+
`;
|
|
123
148
|
|
|
124
149
|
export const Fieldset = styled.fieldset<any>`
|
|
125
150
|
all: unset;
|
|
@@ -134,6 +159,7 @@ export const Input = styled.input<any>`
|
|
|
134
159
|
${FieldDefaultStyles}
|
|
135
160
|
${FieldVariantsStyles}
|
|
136
161
|
${FieldSizeStyles}
|
|
162
|
+
${FieldShapeStyles}
|
|
137
163
|
|
|
138
164
|
&[data-error="true"] {
|
|
139
165
|
&::placeholder {
|
package/src/index.ts
CHANGED
|
@@ -2,8 +2,11 @@ export * from "./accordion";
|
|
|
2
2
|
export * from "./avatar";
|
|
3
3
|
export * from "./badge";
|
|
4
4
|
export * from "./button";
|
|
5
|
+
export * from "./breadcrumb";
|
|
6
|
+
export * from "./card";
|
|
5
7
|
export * from "./checkbox";
|
|
6
8
|
export * from "./collapsible";
|
|
9
|
+
export * from "./copy-button";
|
|
7
10
|
export * from "./dialog";
|
|
8
11
|
export * from "./divider";
|
|
9
12
|
export * from "./dropdown";
|
|
@@ -12,11 +15,16 @@ export * from "./otp-field";
|
|
|
12
15
|
export * from "./overlay";
|
|
13
16
|
export * from "./page";
|
|
14
17
|
export * from "./portal";
|
|
18
|
+
export * from "./privacy-field";
|
|
19
|
+
export * from "./resizable";
|
|
15
20
|
export * from "./sheet";
|
|
16
21
|
export * from "./scrollarea";
|
|
22
|
+
export * from "./spinner";
|
|
23
|
+
export * from "./skeleton";
|
|
17
24
|
export * from "./switch";
|
|
18
25
|
export * from "./table";
|
|
19
26
|
export * from "./tabs";
|
|
27
|
+
export * from "./text-area";
|
|
20
28
|
export * from "./toggle";
|
|
21
29
|
export * from "./toolbar";
|
|
22
30
|
export * from "./tooltip";
|
package/src/page/index.tsx
CHANGED
|
@@ -73,7 +73,7 @@ PageNavigation.displayName = "Page.Navigation";
|
|
|
73
73
|
* @param {string} props.shortcut - The key combination used as keyboard shortcuts to trigger the Page.Toolbar.
|
|
74
74
|
* @param {string} props.hotkey - The key to use in the key combination for the keyboard shortcuts.
|
|
75
75
|
* @param {KeyBindingEnum} props.bindkey - The modifier key to use in the key combination.
|
|
76
|
-
* @param {ComponentSizeEnum} props.sizing - The size of the Page.Toolbar.
|
|
76
|
+
* @param {ComponentSizeEnum} props.sizing - The size of the Page.Toolbar. Defaults to "medium".
|
|
77
77
|
* @param {ComponentHeightEnum} props.height - The height definition of the Page.Toolbar.
|
|
78
78
|
* @param {TComponentSide} props.side - The side of the Page.Toolbar.
|
|
79
79
|
* @param {boolean} props.defaultOpen - Whether the Page.Toolbar should be open by default.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { Page, PrivacyField } from "..";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* PrivacyFields are used to hide sensitive values typed by users.
|
|
7
|
+
*/
|
|
8
|
+
const meta = {
|
|
9
|
+
title: "Components/PrivacyField",
|
|
10
|
+
component: PrivacyField,
|
|
11
|
+
tags: ["autodocs"],
|
|
12
|
+
} satisfies Meta<typeof PrivacyField>;
|
|
13
|
+
export default meta;
|
|
14
|
+
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
export const Default: Story = {
|
|
17
|
+
args: {
|
|
18
|
+
variant: "secondary",
|
|
19
|
+
textIcon: <p className="fs-small-60">txt</p>,
|
|
20
|
+
passwordIcon: <p className="fs-small-60">pwd</p>,
|
|
21
|
+
},
|
|
22
|
+
render: ({ ...args }) => (
|
|
23
|
+
<Page>
|
|
24
|
+
<Page.Content className="p-large-30">
|
|
25
|
+
<PrivacyField {...args} />
|
|
26
|
+
</Page.Content>
|
|
27
|
+
</Page>
|
|
28
|
+
),
|
|
29
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
|
|
5
|
+
import { Field } from "../";
|
|
6
|
+
import { Wrapper, Trigger } from "./styles";
|
|
7
|
+
|
|
8
|
+
import type { IField } from "../";
|
|
9
|
+
|
|
10
|
+
type PrivacyType = "password" | "text";
|
|
11
|
+
interface PrivacyFieldProps extends IField {
|
|
12
|
+
defaultType?: PrivacyType;
|
|
13
|
+
textIcon: React.ReactNode;
|
|
14
|
+
passwordIcon: React.ReactNode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* PrivacyFields are used to hide sensitive values typed by users.
|
|
19
|
+
*
|
|
20
|
+
* @param {PrivacyFieldProps} props - The props for the PrivacyField component.
|
|
21
|
+
* @param {boolean} props.raw - Define whether the component is styled or not.
|
|
22
|
+
* @param {ComponentSizeEnum} props.sizing - The size of the component. Defaults to `medium`.
|
|
23
|
+
* @param {string} props.variant - The style definition used by the component.
|
|
24
|
+
* @param {string} props.error - The error message to display.
|
|
25
|
+
* @param {string} props.hint - The hint message to display.
|
|
26
|
+
* @param {string} props.defaultType - The type of the PrivacyField when rendered.
|
|
27
|
+
* @param {ReactElement} props.textIcon - The Icon used to convey the text type information.
|
|
28
|
+
* @param {ReactElement} props.passwordIcon - The Icon used to convey the password type information.
|
|
29
|
+
* @returns {ReactElement} The PrivacyField component.
|
|
30
|
+
*/
|
|
31
|
+
export const PrivacyField = ({
|
|
32
|
+
defaultType,
|
|
33
|
+
textIcon,
|
|
34
|
+
passwordIcon,
|
|
35
|
+
...restProps
|
|
36
|
+
}: PrivacyFieldProps) => {
|
|
37
|
+
const [type, setType] = React.useState<PrivacyType>(
|
|
38
|
+
defaultType ?? "password"
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const handleChangeType = React.useCallback(() => {
|
|
42
|
+
if (type === "text") setType("password");
|
|
43
|
+
if (type === "password") setType("text");
|
|
44
|
+
}, [type, setType]);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Wrapper className="flex">
|
|
48
|
+
<Field autoComplete="off" type={type} {...restProps} />
|
|
49
|
+
<Trigger variant="ghost" sizing="small" onClick={handleChangeType}>
|
|
50
|
+
{type === "text" && textIcon}
|
|
51
|
+
{type === "password" && passwordIcon}
|
|
52
|
+
</Trigger>
|
|
53
|
+
</Wrapper>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
PrivacyField.displayName = "PrivacyField";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import styled from "styled-components";
|
|
4
|
+
import { Button, Field } from "../../";
|
|
5
|
+
|
|
6
|
+
export const Wrapper = styled(Field.Wrapper)`
|
|
7
|
+
position: relative;
|
|
8
|
+
|
|
9
|
+
input {
|
|
10
|
+
width: 100% !important;
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
export const Trigger = styled(Button)`
|
|
14
|
+
position: absolute !important;
|
|
15
|
+
right: var(--measurement-medium-50);
|
|
16
|
+
top: calc(var(--measurement-medium-50));
|
|
17
|
+
`;
|