podo-ui 0.9.0 → 0.9.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/cdn/font/icon.woff +0 -0
- package/cdn/podo-datepicker.css +1 -1
- package/cdn/podo-datepicker.js +1 -1
- package/cdn/podo-datepicker.min.css +1 -1
- package/cdn/podo-datepicker.min.js +1 -1
- package/cdn/podo-ui.css +16 -4
- package/cdn/podo-ui.min.css +2 -2
- package/dist/react/atom/avatar.d.ts.map +1 -1
- package/dist/react/atom/avatar.js +6 -4
- package/dist/react/atom/button.d.ts.map +1 -1
- package/dist/react/atom/button.js +1 -1
- package/dist/react/atom/chip.d.ts.map +1 -1
- package/dist/react/atom/chip.js +4 -2
- package/dist/react/atom/input.d.ts +4 -0
- package/dist/react/atom/input.d.ts.map +1 -1
- package/dist/react/atom/input.js +9 -24
- package/dist/react/atom/textarea.d.ts +4 -0
- package/dist/react/atom/textarea.d.ts.map +1 -1
- package/dist/react/atom/textarea.js +6 -22
- package/dist/react/hooks/useValidation.d.ts +15 -0
- package/dist/react/hooks/useValidation.d.ts.map +1 -0
- package/dist/react/hooks/useValidation.js +33 -0
- package/dist/react/molecule/table.d.ts.map +1 -1
- package/dist/react/molecule/table.js +14 -4
- package/dist/react/molecule/toast-provider.js +2 -2
- package/package.json +15 -4
- package/scss/color/theme.scss +18 -94
- package/scss/icon/font/icon.woff +0 -0
- package/scss/icon/icon-name.scss +3 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatar.d.ts","sourceRoot":"","sources":["../../../react/atom/avatar.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"avatar.d.ts","sourceRoot":"","sources":["../../../react/atom/avatar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAGjD,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAEjC;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IAElD;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAmFhC,CAAC;AAIH,eAAe,MAAM,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { memo, useCallback } from 'react';
|
|
2
3
|
import styles from './avatar.module.scss';
|
|
3
|
-
const Avatar = ({ type = 'icon', src, icon = 'icon-user', text, size = 56, activityRing = false, className = '', alt = 'Avatar', onClick, }) => {
|
|
4
|
+
const Avatar = memo(({ type = 'icon', src, icon = 'icon-user', text, size = 56, activityRing = false, className = '', alt = 'Avatar', onClick, }) => {
|
|
4
5
|
const wrapperClasses = [
|
|
5
6
|
styles.wrapper,
|
|
6
7
|
activityRing && styles.activityRing,
|
|
@@ -20,7 +21,7 @@ const Avatar = ({ type = 'icon', src, icon = 'icon-user', text, size = 56, activ
|
|
|
20
21
|
// Format text to show only first 2 characters
|
|
21
22
|
const displayText = text ? text.slice(0, 2) : '';
|
|
22
23
|
// Calculate font size based on avatar size and type
|
|
23
|
-
const getFontSize = (contentType) => {
|
|
24
|
+
const getFontSize = useCallback((contentType) => {
|
|
24
25
|
if (contentType === 'icon') {
|
|
25
26
|
// 아이콘: size={56}일 때 44px 기준 (약 78.5%)
|
|
26
27
|
const iconRatio = 0.785;
|
|
@@ -31,7 +32,7 @@ const Avatar = ({ type = 'icon', src, icon = 'icon-user', text, size = 56, activ
|
|
|
31
32
|
const textRatio = 0.43;
|
|
32
33
|
return Math.round(size * textRatio);
|
|
33
34
|
}
|
|
34
|
-
};
|
|
35
|
+
}, [size]);
|
|
35
36
|
const renderContent = () => {
|
|
36
37
|
if (type === 'image' && src) {
|
|
37
38
|
return _jsx("img", { src: src, alt: alt, className: styles.image });
|
|
@@ -50,5 +51,6 @@ const Avatar = ({ type = 'icon', src, icon = 'icon-user', text, size = 56, activ
|
|
|
50
51
|
height: size,
|
|
51
52
|
fontSize: type === 'text' ? getFontSize('text') : getFontSize('icon')
|
|
52
53
|
}, children: renderContent() }) }));
|
|
53
|
-
};
|
|
54
|
+
});
|
|
55
|
+
Avatar.displayName = 'Avatar';
|
|
54
56
|
export default Avatar;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../react/atom/button.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GACnB,SAAS,GACT,SAAS,GACT,cAAc,GACd,MAAM,GACN,MAAM,GACN,SAAS,GACT,SAAS,GACT,QAAQ,CAAC;AAEb,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEjE,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3D,MAAM,WAAW,WAAY,SAAQ,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC;IACjE,kBAAkB;IAClB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,oBAAoB;IACpB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,WAAW;IACX,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IACxC,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uBAAuB;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,QAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../react/atom/button.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GACnB,SAAS,GACT,SAAS,GACT,cAAc,GACd,MAAM,GACN,MAAM,GACN,SAAS,GACT,SAAS,GACT,QAAQ,CAAC;AAEb,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEjE,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3D,MAAM,WAAW,WAAY,SAAQ,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC;IACjE,kBAAkB;IAClB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,oBAAoB;IACpB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,WAAW;IACX,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IACxC,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uBAAuB;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,QAAA,MAAM,MAAM,wHA+CX,CAAC;AAIF,eAAe,MAAM,CAAC"}
|
|
@@ -11,7 +11,7 @@ const Button = forwardRef(({ theme = 'default', variant = 'solid', size = 'sm',
|
|
|
11
11
|
]
|
|
12
12
|
.filter(Boolean)
|
|
13
13
|
.join(' ');
|
|
14
|
-
return (_jsxs("button", { ref: ref, className: buttonClass || undefined, disabled: disabled || loading, ...rest, children: [loading ? (_jsx("i", { className: "icon-loading" })) : (icon && _jsx("i", { className: icon })), children, rightIcon && !loading && _jsx("i", { className: rightIcon })] }));
|
|
14
|
+
return (_jsxs("button", { ref: ref, className: buttonClass || undefined, disabled: disabled || loading, "aria-busy": loading ? true : undefined, "aria-disabled": disabled ? true : undefined, ...rest, children: [loading ? (_jsx("i", { className: "icon-loading" })) : (icon && _jsx("i", { className: icon })), children, rightIcon && !loading && _jsx("i", { className: rightIcon })] }));
|
|
15
15
|
});
|
|
16
16
|
Button.displayName = 'Button';
|
|
17
17
|
export default Button;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chip.d.ts","sourceRoot":"","sources":["../../../react/atom/chip.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"chip.d.ts","sourceRoot":"","sources":["../../../react/atom/chip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAe,MAAM,OAAO,CAAC;AAEpC,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnE,IAAI,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;IACrC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,QAAA,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,SAAS,CA4B5B,CAAC;AAIH,eAAe,IAAI,CAAC"}
|
package/dist/react/atom/chip.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
const Chip = memo(({ children, theme = 'default', type = 'default', size = 'md', round = false, icon, onDelete, className = '', }) => {
|
|
3
4
|
const chipClasses = [
|
|
4
5
|
'chip',
|
|
5
6
|
theme !== 'default' && theme,
|
|
@@ -11,5 +12,6 @@ const Chip = ({ children, theme = 'default', type = 'default', size = 'md', roun
|
|
|
11
12
|
.filter(Boolean)
|
|
12
13
|
.join(' ');
|
|
13
14
|
return (_jsxs("div", { className: chipClasses, children: [icon && _jsx("i", { className: `icon ${icon}` }), children, onDelete && _jsx("button", { "aria-label": "\uC0AD\uC81C", onClick: onDelete })] }));
|
|
14
|
-
};
|
|
15
|
+
});
|
|
16
|
+
Chip.displayName = 'Chip';
|
|
15
17
|
export default Chip;
|
|
@@ -6,6 +6,10 @@ export interface InputWrapperProps extends React.ComponentProps<'input'> {
|
|
|
6
6
|
withIcon?: string;
|
|
7
7
|
withRightIcon?: string;
|
|
8
8
|
unit?: string;
|
|
9
|
+
/** Accessible label for screen readers */
|
|
10
|
+
'aria-label'?: string;
|
|
11
|
+
/** ID of element describing the input */
|
|
12
|
+
'aria-describedby'?: string;
|
|
9
13
|
}
|
|
10
14
|
declare const Input: React.FC<InputWrapperProps>;
|
|
11
15
|
export default Input;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../react/atom/input.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../react/atom/input.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,WAAW,iBAAkB,SAAQ,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC;IACtE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,QAAA,MAAM,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA4CtC,CAAC;AAEF,eAAe,KAAK,CAAC"}
|
package/dist/react/atom/input.js
CHANGED
|
@@ -1,29 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { z } from 'zod';
|
|
2
|
+
import { useEffect } from 'react';
|
|
4
3
|
import styles from './input.module.scss';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
setStatusClass('');
|
|
11
|
-
if (validator && value) {
|
|
12
|
-
try {
|
|
13
|
-
validator.parse(value);
|
|
14
|
-
setStatusClass('success');
|
|
15
|
-
}
|
|
16
|
-
catch (e) {
|
|
17
|
-
if (e instanceof z.ZodError) {
|
|
18
|
-
setMessage(e.errors[0].message);
|
|
19
|
-
setStatusClass('danger');
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}, [validator, value]);
|
|
4
|
+
import { useValidation } from '../hooks/useValidation';
|
|
5
|
+
const Input = ({ validator, value, className, withIcon, withRightIcon, unit, type = 'text', id, ...rest }) => {
|
|
6
|
+
const inputId = id || `input-${Math.random().toString(36).slice(2, 9)}`;
|
|
7
|
+
const errorId = `${inputId}-error`;
|
|
8
|
+
const { message, statusClass, validate } = useValidation(validator);
|
|
24
9
|
useEffect(() => {
|
|
25
|
-
|
|
26
|
-
}, [
|
|
27
|
-
return (_jsxs("div", { className: `${styles.style} ${className || ''}`, children: [_jsxs("div", { className: `${className || ''} ${withIcon ? 'with-icon' : ''} ${withRightIcon ? 'with-right-icon' : ''}`, children: [withIcon && _jsx("i", { className: withIcon }), _jsx("input", { type: type, ...rest, value: value ?? '', className: `${statusClass} ${className || ''}
|
|
10
|
+
validate(value);
|
|
11
|
+
}, [validate, value]);
|
|
12
|
+
return (_jsxs("div", { className: `${styles.style} ${className || ''}`, children: [_jsxs("div", { className: `${className || ''} ${withIcon ? 'with-icon' : ''} ${withRightIcon ? 'with-right-icon' : ''}`, children: [withIcon && _jsx("i", { className: withIcon }), _jsx("input", { id: inputId, type: type, ...rest, value: value ?? '', className: `${statusClass} ${className || ''}`, "aria-invalid": statusClass === 'danger' ? true : undefined, "aria-describedby": message ? errorId : rest['aria-describedby'] }), withRightIcon && _jsx("i", { className: withRightIcon }), unit && _jsx("span", { className: "unit", children: unit })] }), validator && message !== '' && (_jsx("div", { id: errorId, className: "validator", role: "alert", children: message }))] }));
|
|
28
13
|
};
|
|
29
14
|
export default Input;
|
|
@@ -3,6 +3,10 @@ export interface TextareaWrapperProps extends React.ComponentProps<'textarea'> {
|
|
|
3
3
|
value: string;
|
|
4
4
|
className?: string;
|
|
5
5
|
validator?: z.ZodType<unknown>;
|
|
6
|
+
/** Accessible label for screen readers */
|
|
7
|
+
'aria-label'?: string;
|
|
8
|
+
/** ID of element describing the textarea */
|
|
9
|
+
'aria-describedby'?: string;
|
|
6
10
|
}
|
|
7
11
|
declare const Textarea: React.FC<TextareaWrapperProps>;
|
|
8
12
|
export default Textarea;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textarea.d.ts","sourceRoot":"","sources":["../../../react/atom/textarea.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"textarea.d.ts","sourceRoot":"","sources":["../../../react/atom/textarea.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,WAAW,oBAAqB,SAAQ,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,QAAA,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA6B5C,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
|
@@ -1,26 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { z } from 'zod';
|
|
4
2
|
import styles from './textarea.module.scss';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (validator && value.length > 0) {
|
|
12
|
-
try {
|
|
13
|
-
validator.parse(value);
|
|
14
|
-
setStatusClass('success');
|
|
15
|
-
}
|
|
16
|
-
catch (e) {
|
|
17
|
-
if (e instanceof z.ZodError) {
|
|
18
|
-
setMessage(e.errors[0].message);
|
|
19
|
-
setStatusClass('danger');
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
return (_jsxs("div", { className: `${styles.style} ${className}`, children: [_jsx("textarea", { ...rest, value: value, className: `${statusClass} ${className}`, onKeyUp: validateHandler }), validator && message !== '' && (_jsx("div", { className: "validator", children: message }))] }));
|
|
3
|
+
import { useValidation } from '../hooks/useValidation';
|
|
4
|
+
const Textarea = ({ validator, value, className, id, ...rest }) => {
|
|
5
|
+
const textareaId = id || `textarea-${Math.random().toString(36).slice(2, 9)}`;
|
|
6
|
+
const errorId = `${textareaId}-error`;
|
|
7
|
+
const { message, statusClass, validate } = useValidation(validator);
|
|
8
|
+
return (_jsxs("div", { className: `${styles.style} ${className}`, children: [_jsx("textarea", { id: textareaId, ...rest, value: value, className: `${statusClass} ${className}`, onKeyUp: () => validate(value), "aria-invalid": statusClass === 'danger' ? true : undefined, "aria-describedby": message ? errorId : rest['aria-describedby'] }), validator && message !== '' && (_jsx("div", { id: errorId, className: "validator", role: "alert", children: message }))] }));
|
|
25
9
|
};
|
|
26
10
|
export default Textarea;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export interface ValidationResult {
|
|
3
|
+
message: string;
|
|
4
|
+
statusClass: string;
|
|
5
|
+
validate: (value: string | number | undefined) => void;
|
|
6
|
+
reset: () => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Custom hook for Zod validation
|
|
10
|
+
* @param validator - Zod schema for validation
|
|
11
|
+
* @returns Validation state and handlers
|
|
12
|
+
*/
|
|
13
|
+
export declare const useValidation: (validator?: z.ZodType<unknown>) => ValidationResult;
|
|
14
|
+
export default useValidation;
|
|
15
|
+
//# sourceMappingURL=useValidation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useValidation.d.ts","sourceRoot":"","sources":["../../../react/hooks/useValidation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACvD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,GACxB,YAAY,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAC7B,gBA+BF,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
/**
|
|
4
|
+
* Custom hook for Zod validation
|
|
5
|
+
* @param validator - Zod schema for validation
|
|
6
|
+
* @returns Validation state and handlers
|
|
7
|
+
*/
|
|
8
|
+
export const useValidation = (validator) => {
|
|
9
|
+
const [message, setMessage] = useState('');
|
|
10
|
+
const [statusClass, setStatusClass] = useState('');
|
|
11
|
+
const reset = useCallback(() => {
|
|
12
|
+
setMessage('');
|
|
13
|
+
setStatusClass('');
|
|
14
|
+
}, []);
|
|
15
|
+
const validate = useCallback((value) => {
|
|
16
|
+
reset();
|
|
17
|
+
if (!validator || !value) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
validator.parse(value);
|
|
22
|
+
setStatusClass('success');
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
if (e instanceof z.ZodError) {
|
|
26
|
+
setMessage(e.errors[0].message);
|
|
27
|
+
setStatusClass('danger');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}, [validator, reset]);
|
|
31
|
+
return { message, statusClass, validate, reset };
|
|
32
|
+
};
|
|
33
|
+
export default useValidation;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../react/molecule/table.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../react/molecule/table.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAE3C,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,iBAAiB;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,kBAAkB;IAClB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;IACvE,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC3D,yBAAyB;IACzB,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1B,kBAAkB;IAClB,UAAU,EAAE,CAAC,EAAE,CAAC;IAChB,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC;IAC1C,oCAAoC;IACpC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iBAAiB;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wBAAwB;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,yBAAyB;IACzB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,iBAAS,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAChD,OAAO,EACP,UAAU,EACV,MAAM,EACN,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,UAAU,EACV,SAAS,GACV,EAAE,UAAU,CAAC,CAAC,CAAC,2CA+Df;kBAxEQ,KAAK;;;AA4Ed,eAAe,KAAK,CAAC"}
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback } from 'react';
|
|
2
3
|
function Table({ columns, dataSource, rowKey, list, border, fill, onRowClick, className, }) {
|
|
3
|
-
const getRowKey = (record, index) => {
|
|
4
|
+
const getRowKey = useCallback((record, index) => {
|
|
4
5
|
if (typeof rowKey === 'function') {
|
|
5
6
|
return rowKey(record);
|
|
6
7
|
}
|
|
7
8
|
return String(record[rowKey] ?? index);
|
|
8
|
-
};
|
|
9
|
+
}, [rowKey]);
|
|
10
|
+
const handleRowClick = useCallback((record, index) => {
|
|
11
|
+
onRowClick?.(record, index);
|
|
12
|
+
}, [onRowClick]);
|
|
13
|
+
const handleKeyDown = useCallback((e, record, index) => {
|
|
14
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
15
|
+
e.preventDefault();
|
|
16
|
+
onRowClick?.(record, index);
|
|
17
|
+
}
|
|
18
|
+
}, [onRowClick]);
|
|
9
19
|
const tableClass = [list && 'list', border && 'border', fill && 'fill', className]
|
|
10
20
|
.filter(Boolean)
|
|
11
21
|
.join(' ');
|
|
12
|
-
return (_jsxs("table", { className: tableClass || undefined, children: [_jsx("thead", { children: _jsx("tr", { children: columns.map((col) => (_jsx("th", { style: {
|
|
22
|
+
return (_jsxs("table", { className: tableClass || undefined, children: [_jsx("thead", { children: _jsx("tr", { children: columns.map((col) => (_jsx("th", { scope: "col", style: {
|
|
13
23
|
width: col.width,
|
|
14
24
|
textAlign: col.align,
|
|
15
|
-
}, children: col.title }, col.key))) }) }), _jsx("tbody", { children: dataSource.map((record, index) => (_jsx("tr", { onClick: () =>
|
|
25
|
+
}, children: col.title }, col.key))) }) }), _jsx("tbody", { children: dataSource.map((record, index) => (_jsx("tr", { onClick: () => handleRowClick(record, index), style: onRowClick ? { cursor: 'pointer' } : undefined, role: onRowClick ? 'button' : undefined, tabIndex: onRowClick ? 0 : undefined, onKeyDown: onRowClick ? (e) => handleKeyDown(e, record, index) : undefined, children: columns.map((col) => (_jsx("td", { style: { textAlign: col.align }, children: col.render
|
|
16
26
|
? col.render(record[col.key], record, index)
|
|
17
27
|
: record[col.key] }, col.key))) }, getRowKey(record, index)))) })] }));
|
|
18
28
|
}
|
|
@@ -40,9 +40,9 @@ export const ToastProvider = ({ children }) => {
|
|
|
40
40
|
const hideToast = useCallback((id) => {
|
|
41
41
|
setToasts((prev) => prev.filter((toast) => toast.id !== id));
|
|
42
42
|
}, []);
|
|
43
|
-
const getToastsByPosition = (position) => {
|
|
43
|
+
const getToastsByPosition = useCallback((position) => {
|
|
44
44
|
return toasts.filter((toast) => toast.position === position);
|
|
45
|
-
};
|
|
45
|
+
}, [toasts]);
|
|
46
46
|
const positions = [
|
|
47
47
|
'top-left',
|
|
48
48
|
'top-center',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "podo-ui",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": "hada0127 <work@tarucy.net>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"homepage": "https://podoui.com",
|
|
15
15
|
"main": "dist/index.js",
|
|
16
|
+
"module": "dist/index.js",
|
|
16
17
|
"types": "dist/index.d.ts",
|
|
17
18
|
"files": [
|
|
18
19
|
"dist",
|
|
@@ -23,12 +24,14 @@
|
|
|
23
24
|
"global.d.ts",
|
|
24
25
|
"mixin.scss",
|
|
25
26
|
"vite-fonts.scss",
|
|
26
|
-
"README.md"
|
|
27
|
+
"README.md",
|
|
28
|
+
"README.ko.md"
|
|
27
29
|
],
|
|
28
30
|
"exports": {
|
|
29
31
|
".": {
|
|
30
32
|
"types": "./dist/index.d.ts",
|
|
31
33
|
"import": "./dist/index.js",
|
|
34
|
+
"require": "./dist/index.js",
|
|
32
35
|
"default": "./dist/index.js"
|
|
33
36
|
},
|
|
34
37
|
"./react/atom/*": {
|
|
@@ -79,7 +82,10 @@
|
|
|
79
82
|
"prepublishOnly": "npm run build:all",
|
|
80
83
|
"lint": "eslint .",
|
|
81
84
|
"icon": "node ./cli/icon-scss.js",
|
|
82
|
-
"deploy": "vite build && wrangler pages deploy dist/client"
|
|
85
|
+
"deploy": "vite build && wrangler pages deploy dist/client",
|
|
86
|
+
"test": "vitest",
|
|
87
|
+
"test:run": "vitest run",
|
|
88
|
+
"test:coverage": "vitest run --coverage"
|
|
83
89
|
},
|
|
84
90
|
"peerDependencies": {
|
|
85
91
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -105,6 +111,9 @@
|
|
|
105
111
|
"devDependencies": {
|
|
106
112
|
"@eslint/js": "^9.8.0",
|
|
107
113
|
"@playwright/test": "^1.56.1",
|
|
114
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
115
|
+
"@testing-library/react": "^16.1.0",
|
|
116
|
+
"@testing-library/user-event": "^14.5.2",
|
|
108
117
|
"@types/node": "22.10.2",
|
|
109
118
|
"@types/react": "^18.3.3",
|
|
110
119
|
"@types/react-dom": "^18.3.0",
|
|
@@ -125,8 +134,10 @@
|
|
|
125
134
|
"scss": "^0.2.4",
|
|
126
135
|
"typescript": "^5.5.3",
|
|
127
136
|
"typescript-eslint": "^8.0.0",
|
|
137
|
+
"jsdom": "^25.0.1",
|
|
128
138
|
"vike": "^0.4.182",
|
|
129
139
|
"vike-react": "^0.4.18",
|
|
130
|
-
"vite": "^5.4.0"
|
|
140
|
+
"vite": "^5.4.0",
|
|
141
|
+
"vitest": "^2.1.8"
|
|
131
142
|
}
|
|
132
143
|
}
|
package/scss/color/theme.scss
CHANGED
|
@@ -87,101 +87,9 @@
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/*
|
|
90
|
-
|
|
90
|
+
Dark mode color variables (shared mixin)
|
|
91
91
|
*/
|
|
92
|
-
@
|
|
93
|
-
:root:not([data-color-mode='light']) {
|
|
94
|
-
--color-primary: #7c3aed;
|
|
95
|
-
--color-primary-hover: #8b5cf6;
|
|
96
|
-
--color-primary-pressed: #7c3aed;
|
|
97
|
-
--color-primary-focus: #8b5cf6;
|
|
98
|
-
--color-primary-fill: #111827;
|
|
99
|
-
--color-primary-reverse: #ffffff;
|
|
100
|
-
--color-primary-outline: rgba(158, 115, 254, 0.3);
|
|
101
|
-
--color-default: #34343a;
|
|
102
|
-
--color-default-hover: #3f3f46;
|
|
103
|
-
--color-default-pressed: #34343a;
|
|
104
|
-
--color-default-focus: #3f3f46;
|
|
105
|
-
--color-default-fill: #34343a;
|
|
106
|
-
--color-default-reverse: #ffffff;
|
|
107
|
-
--color-default-outline: rgba(63, 63, 70, 0.3);
|
|
108
|
-
--color-default-deep: #a1a1aa;
|
|
109
|
-
--color-default-deep-hover: #d1d1d7;
|
|
110
|
-
--color-default-deep-pressed: #a1a1aa;
|
|
111
|
-
--color-default-deep-focus: #d1d1d7;
|
|
112
|
-
--color-default-deep-fill: #52525b;
|
|
113
|
-
--color-default-deep-reverse: #2c2c31;
|
|
114
|
-
--color-default-deep-outline: rgba(209, 209, 215, 0.3);
|
|
115
|
-
--color-info: #0a73eb;
|
|
116
|
-
--color-info-hover: #1890ff;
|
|
117
|
-
--color-info-pressed: #0a73eb;
|
|
118
|
-
--color-info-focus: #1890ff;
|
|
119
|
-
--color-info-fill: #1c1c20;
|
|
120
|
-
--color-info-reverse: #ffffff;
|
|
121
|
-
--color-info-outline: rgba(24, 144, 255, 0.3);
|
|
122
|
-
--color-link: #0284c7;
|
|
123
|
-
--color-link-hover: #0ea5e9;
|
|
124
|
-
--color-link-pressed: #0284c7;
|
|
125
|
-
--color-link-focus: #0ea5e9;
|
|
126
|
-
--color-link-fill: #1c1c20;
|
|
127
|
-
--color-link-reverse: #ffffff;
|
|
128
|
-
--color-link-outline: rgba(14, 165, 233, 0.3);
|
|
129
|
-
--color-success: #0d9488;
|
|
130
|
-
--color-success-hover: #1bb0a2;
|
|
131
|
-
--color-success-pressed: #0d9488;
|
|
132
|
-
--color-success-focus: #1bb0a2;
|
|
133
|
-
--color-success-fill: #1c1c20;
|
|
134
|
-
--color-success-reverse: #ffffff;
|
|
135
|
-
--color-success-outline: rgba(27, 176, 162, 0.3);
|
|
136
|
-
--color-warning: #e8840f;
|
|
137
|
-
--color-warning-hover: #f19b0b;
|
|
138
|
-
--color-warning-pressed: #e8840f;
|
|
139
|
-
--color-warning-focus: #f19b0b;
|
|
140
|
-
--color-warning-fill: #1c1c20;
|
|
141
|
-
--color-warning-reverse: #ffffff;
|
|
142
|
-
--color-warning-outline: rgba(241, 155, 11, 0.3);
|
|
143
|
-
--color-danger: #f04646;
|
|
144
|
-
--color-danger-hover: #f25959;
|
|
145
|
-
--color-danger-pressed: #f04646;
|
|
146
|
-
--color-danger-focus: #f25959;
|
|
147
|
-
--color-danger-fill: #1c1c20;
|
|
148
|
-
--color-danger-reverse: #ffffff;
|
|
149
|
-
--color-danger-outline: rgba(242, 89, 89, 0.3);
|
|
150
|
-
--color-bg-modal: #2c2c31;
|
|
151
|
-
--color-bg-disabled: #2c2c31;
|
|
152
|
-
--color-bg-toggle: #52525b;
|
|
153
|
-
--color-bg-indicator: rgba(255, 255, 255, 0.36);
|
|
154
|
-
--color-bg-block: rgba(0, 0, 0, 0.09);
|
|
155
|
-
--color-bg-reverse-wb: #000000;
|
|
156
|
-
--color-bg-reverse-bw: #ffffff;
|
|
157
|
-
--color-bg-wt: #ffffff;
|
|
158
|
-
--color-bg-bk: #000000;
|
|
159
|
-
--color-bg-elevation: #09090b;
|
|
160
|
-
--color-bg-elevation-1: #18181b;
|
|
161
|
-
--color-bg-elevation-2: #242429;
|
|
162
|
-
--color-bg-elevation-3: #2c2c31;
|
|
163
|
-
--color-border: #52525b;
|
|
164
|
-
--color-border-hover: #71717a;
|
|
165
|
-
--color-border-pressed: #52525b;
|
|
166
|
-
--color-border-focus: #71717a;
|
|
167
|
-
--color-border-disabled: rgba(255, 255, 255, 0.09);
|
|
168
|
-
--color-border-alpha: rgba(255, 255, 255, 0.18);
|
|
169
|
-
--color-text-header: #f4f4f5;
|
|
170
|
-
--color-text-body: #e4e4e7;
|
|
171
|
-
--color-text-sub: #a1a1aa;
|
|
172
|
-
--color-text-action: #d1d1d7;
|
|
173
|
-
--color-text-action-hover: #f4f4f5;
|
|
174
|
-
--color-text-action-pressed: #d1d1d7;
|
|
175
|
-
--color-text-action-focus: #f4f4f5;
|
|
176
|
-
--color-text-action-disabled: #52525b;
|
|
177
|
-
--color-text-action-reverse: #ffffff;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
html[data-color-mode='dark'] {
|
|
182
|
-
/*
|
|
183
|
-
Dark mode colors
|
|
184
|
-
*/
|
|
92
|
+
@mixin dark-mode-vars {
|
|
185
93
|
--color-primary: #7c3aed;
|
|
186
94
|
--color-primary-hover: #8b5cf6;
|
|
187
95
|
--color-primary-pressed: #7c3aed;
|
|
@@ -267,3 +175,19 @@ html[data-color-mode='dark'] {
|
|
|
267
175
|
--color-text-action-disabled: #52525b;
|
|
268
176
|
--color-text-action-reverse: #ffffff;
|
|
269
177
|
}
|
|
178
|
+
|
|
179
|
+
/*
|
|
180
|
+
Auto mode: Follow system preference
|
|
181
|
+
*/
|
|
182
|
+
@media (prefers-color-scheme: dark) {
|
|
183
|
+
:root:not([data-color-mode='light']) {
|
|
184
|
+
@include dark-mode-vars;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/*
|
|
189
|
+
Manual dark mode
|
|
190
|
+
*/
|
|
191
|
+
html[data-color-mode='dark'] {
|
|
192
|
+
@include dark-mode-vars;
|
|
193
|
+
}
|
package/scss/icon/font/icon.woff
CHANGED
|
Binary file
|