@douglasneuroinformatics/libui 2.7.0 → 2.8.1
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/dist/components/Form/StringField/StringFieldPassword.d.ts +5 -2
- package/dist/components/Form/StringField/StringFieldPassword.d.ts.map +1 -1
- package/dist/components/Form/StringField/StringFieldPassword.js +11 -2
- package/dist/douglasneuroinformatics-libui-2.8.1.tgz +0 -0
- package/package.json +2 -2
- package/src/components/Form/Form.stories.tsx +5 -1
- package/src/components/Form/StringField/StringField.stories.tsx +25 -0
- package/src/components/Form/StringField/StringFieldPassword.tsx +31 -2
- package/dist/douglasneuroinformatics-libui-2.7.0.tgz +0 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { StringFormField } from '@douglasneuroinformatics/libui-form-types';
|
|
3
3
|
import type { BaseFieldComponentProps } from '../types.js';
|
|
4
|
-
export type
|
|
5
|
-
export
|
|
4
|
+
export type PasswordStrengthValue = 0 | 1 | 2 | 3 | 4;
|
|
5
|
+
export type StringFieldPasswordProps = BaseFieldComponentProps<string> & Extract<StringFormField, {
|
|
6
|
+
variant: 'password';
|
|
7
|
+
}>;
|
|
8
|
+
export declare const StringFieldPassword: ({ calculateStrength, description, error, label, name, readOnly, setValue, value }: StringFieldPasswordProps) => React.JSX.Element;
|
|
6
9
|
//# sourceMappingURL=StringFieldPassword.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StringFieldPassword.d.ts","sourceRoot":"","sources":["../../../../src/components/Form/StringField/StringFieldPassword.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"StringFieldPassword.d.ts","sourceRoot":"","sources":["../../../../src/components/Form/StringField/StringFieldPassword.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AASjF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,MAAM,qBAAqB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEtD,MAAM,MAAM,wBAAwB,GAAG,uBAAuB,CAAC,MAAM,CAAC,GACpE,OAAO,CAAC,eAAe,EAAE;IAAE,OAAO,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAEpD,eAAO,MAAM,mBAAmB,sFAS7B,wBAAwB,sBAuD1B,CAAC"}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { motion } from 'framer-motion';
|
|
2
3
|
import { EyeIcon, EyeOffIcon } from 'lucide-react';
|
|
3
4
|
import { cn } from '../../../utils.js';
|
|
4
5
|
import { Input } from '../../Input/Input.js';
|
|
5
6
|
import { Label } from '../../Label/Label.js';
|
|
6
7
|
import { FieldGroup } from '../FieldGroup/FieldGroup.js';
|
|
7
|
-
export const StringFieldPassword = ({ description, error, label, name, readOnly, setValue, value }) => {
|
|
8
|
+
export const StringFieldPassword = ({ calculateStrength, description, error, label, name, readOnly, setValue, value }) => {
|
|
9
|
+
const [strength, setStrength] = useState(calculateStrength ? 0 : null);
|
|
8
10
|
const [show, setShow] = useState(false);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (calculateStrength) {
|
|
13
|
+
setStrength(value ? calculateStrength(value) : 0);
|
|
14
|
+
}
|
|
15
|
+
}, [value]);
|
|
9
16
|
return (React.createElement(FieldGroup, null,
|
|
10
17
|
React.createElement(FieldGroup.Row, null,
|
|
11
18
|
React.createElement(Label, { htmlFor: name }, label),
|
|
@@ -15,5 +22,7 @@ export const StringFieldPassword = ({ description, error, label, name, readOnly,
|
|
|
15
22
|
React.createElement("button", { className: "absolute right-0 flex h-full w-8 items-center justify-center text-muted-foreground", disabled: readOnly, tabIndex: -1, type: "button", onClick: () => setShow(!show) },
|
|
16
23
|
React.createElement(EyeIcon, { className: cn('absolute transition-all', show ? '-rotate-90 scale-0' : 'rotate-0 scale-100') }),
|
|
17
24
|
React.createElement(EyeOffIcon, { className: cn('absolute transition-all', !show ? 'rotate-90 scale-0' : 'rotate-0 scale-100') }))),
|
|
25
|
+
strength !== null && (React.createElement(motion.div, { animate: { width: `${Math.max(strength, value?.length ? 1 : 0) * 25}%` }, className: "h-1 w-full overflow-hidden rounded", initial: { width: '0%' }, transition: { duration: 0.5 } },
|
|
26
|
+
React.createElement("div", { className: cn('h-full w-full bg-destructive transition-colors duration-500', strength === 2 && 'bg-yellow-500 dark:bg-yellow-700', strength === 3 && 'bg-sky-500 dark:bg-sky-700', strength === 4 && 'bg-green-500 dark:bg-green-700') }))),
|
|
18
27
|
React.createElement(FieldGroup.Error, { error: error })));
|
|
19
28
|
};
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@douglasneuroinformatics/libui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.8.1",
|
|
5
5
|
"packageManager": "pnpm@9.3.0",
|
|
6
6
|
"description": "Generic UI components for DNP projects, built using React and TailwindCSS",
|
|
7
7
|
"author": {
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@douglasneuroinformatics/libjs": "^0.3.1",
|
|
70
|
-
"@douglasneuroinformatics/libui-form-types": "^0.
|
|
70
|
+
"@douglasneuroinformatics/libui-form-types": "^0.9.0",
|
|
71
71
|
"@headlessui/tailwindcss": "^0.2.1",
|
|
72
72
|
"@heroicons/react": "^2.1.3",
|
|
73
73
|
"@radix-ui/react-accordion": "^1.1.2",
|
|
@@ -4,6 +4,7 @@ import React from 'react';
|
|
|
4
4
|
|
|
5
5
|
import type { FormFields } from '@douglasneuroinformatics/libui-form-types';
|
|
6
6
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
7
|
+
import type { IntRange } from 'type-fest';
|
|
7
8
|
import { z } from 'zod';
|
|
8
9
|
|
|
9
10
|
import { Heading } from '../Heading/Heading.js';
|
|
@@ -165,7 +166,10 @@ const stringFields: FormFields<
|
|
|
165
166
|
stringPassword: {
|
|
166
167
|
kind: 'string',
|
|
167
168
|
label: 'Password',
|
|
168
|
-
variant: 'password'
|
|
169
|
+
variant: 'password',
|
|
170
|
+
calculateStrength: (password: string) => {
|
|
171
|
+
return Math.min(password.length, 4) as IntRange<0, 5>;
|
|
172
|
+
}
|
|
169
173
|
},
|
|
170
174
|
stringInput: {
|
|
171
175
|
description: 'This is a string field',
|
|
@@ -4,6 +4,8 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|
|
4
4
|
|
|
5
5
|
import { StringField } from './StringField.js';
|
|
6
6
|
|
|
7
|
+
import type { PasswordStrengthValue } from './StringFieldPassword.js';
|
|
8
|
+
|
|
7
9
|
type Story = StoryObj<typeof StringField>;
|
|
8
10
|
|
|
9
11
|
export default { component: StringField } as Meta<typeof StringField>;
|
|
@@ -68,6 +70,29 @@ export const Password: Story = {
|
|
|
68
70
|
]
|
|
69
71
|
};
|
|
70
72
|
|
|
73
|
+
export const PasswordWithStrength: Story = {
|
|
74
|
+
decorators: [
|
|
75
|
+
(Story) => {
|
|
76
|
+
const [value, setValue] = useState<string | undefined>();
|
|
77
|
+
return (
|
|
78
|
+
<Story
|
|
79
|
+
args={{
|
|
80
|
+
calculateStrength: (password: string) => {
|
|
81
|
+
return Math.min(password.length, 4) as PasswordStrengthValue;
|
|
82
|
+
},
|
|
83
|
+
description: 'Lorem ipsum dolor sit amet consectetur adipisicing elit.',
|
|
84
|
+
label: 'Password',
|
|
85
|
+
name: 'text',
|
|
86
|
+
setValue,
|
|
87
|
+
value,
|
|
88
|
+
variant: 'password'
|
|
89
|
+
}}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
};
|
|
95
|
+
|
|
71
96
|
export const Select: Story = {
|
|
72
97
|
decorators: [
|
|
73
98
|
(Story) => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import type { StringFormField } from '@douglasneuroinformatics/libui-form-types';
|
|
4
|
+
import { motion } from 'framer-motion';
|
|
4
5
|
import { EyeIcon, EyeOffIcon } from 'lucide-react';
|
|
5
6
|
|
|
6
7
|
import { cn } from '../../../utils.js';
|
|
@@ -10,9 +11,13 @@ import { FieldGroup } from '../FieldGroup/FieldGroup.js';
|
|
|
10
11
|
|
|
11
12
|
import type { BaseFieldComponentProps } from '../types.js';
|
|
12
13
|
|
|
13
|
-
export type
|
|
14
|
+
export type PasswordStrengthValue = 0 | 1 | 2 | 3 | 4;
|
|
15
|
+
|
|
16
|
+
export type StringFieldPasswordProps = BaseFieldComponentProps<string> &
|
|
17
|
+
Extract<StringFormField, { variant: 'password' }>;
|
|
14
18
|
|
|
15
19
|
export const StringFieldPassword = ({
|
|
20
|
+
calculateStrength,
|
|
16
21
|
description,
|
|
17
22
|
error,
|
|
18
23
|
label,
|
|
@@ -21,7 +26,14 @@ export const StringFieldPassword = ({
|
|
|
21
26
|
setValue,
|
|
22
27
|
value
|
|
23
28
|
}: StringFieldPasswordProps) => {
|
|
29
|
+
const [strength, setStrength] = useState<PasswordStrengthValue | null>(calculateStrength ? 0 : null);
|
|
24
30
|
const [show, setShow] = useState(false);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (calculateStrength) {
|
|
33
|
+
setStrength(value ? calculateStrength(value) : 0);
|
|
34
|
+
}
|
|
35
|
+
}, [value]);
|
|
36
|
+
|
|
25
37
|
return (
|
|
26
38
|
<FieldGroup>
|
|
27
39
|
<FieldGroup.Row>
|
|
@@ -48,6 +60,23 @@ export const StringFieldPassword = ({
|
|
|
48
60
|
<EyeOffIcon className={cn('absolute transition-all', !show ? 'rotate-90 scale-0' : 'rotate-0 scale-100')} />
|
|
49
61
|
</button>
|
|
50
62
|
</FieldGroup.Row>
|
|
63
|
+
{strength !== null && (
|
|
64
|
+
<motion.div
|
|
65
|
+
animate={{ width: `${Math.max(strength, value?.length ? 1 : 0) * 25}%` }}
|
|
66
|
+
className="h-1 w-full overflow-hidden rounded"
|
|
67
|
+
initial={{ width: '0%' }}
|
|
68
|
+
transition={{ duration: 0.5 }}
|
|
69
|
+
>
|
|
70
|
+
<div
|
|
71
|
+
className={cn(
|
|
72
|
+
'h-full w-full bg-destructive transition-colors duration-500',
|
|
73
|
+
strength === 2 && 'bg-yellow-500 dark:bg-yellow-700',
|
|
74
|
+
strength === 3 && 'bg-sky-500 dark:bg-sky-700',
|
|
75
|
+
strength === 4 && 'bg-green-500 dark:bg-green-700'
|
|
76
|
+
)}
|
|
77
|
+
/>
|
|
78
|
+
</motion.div>
|
|
79
|
+
)}
|
|
51
80
|
<FieldGroup.Error error={error} />
|
|
52
81
|
</FieldGroup>
|
|
53
82
|
);
|
|
Binary file
|