@khoangd/ui 0.1.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/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # @khoangd/ui
2
+
3
+ Accessible, props-driven React primitives. Import `@khoangd/ui/styles.css` once in the consumer entry point.
4
+
5
+ ```tsx
6
+ import { Button, Grid, Input, InputSelect, Stack, Switch, Textarea } from '@khoangd/ui';
7
+ import '@khoangd/ui/styles.css';
8
+ ```
9
+
10
+ The package has no WTS store, API, socket, router or trading workflow dependency.
11
+
12
+ Stable subpaths are available when a resource only needs one layer:
13
+
14
+ ```tsx
15
+ import { Button, Input } from '@khoangd/ui/primitives';
16
+ import {
17
+ Checkbox,
18
+ DatePicker,
19
+ FormField,
20
+ InputSelect,
21
+ RadioGroup,
22
+ SegmentedControl,
23
+ Switch,
24
+ Textarea,
25
+ } from '@khoangd/ui/forms';
26
+ import { Grid, Stack, Wrapper } from '@khoangd/ui/layout';
27
+ import { ErrorBoundary, Loading, Overlay, Skeleton } from '@khoangd/ui/feedback';
28
+ import { Badge } from '@khoangd/ui/data-display';
29
+ ```
30
+
31
+ `InputSelect` is a native select intended for local option lists. Search, remote data,
32
+ translations and Ant Design adapters remain in the consuming application.
33
+
34
+ `IconButton` requires an accessible `label`. `Badge` and `EmptyState` accept icon nodes so
35
+ consumers can use `@khoangd/icons` without coupling the UI package to a specific icon set.
36
+
37
+ ## Shared display components
38
+
39
+ `ImageWithFallback` and `ErrorView` now live here; `@khoangd/trading-ui` keeps compatibility re-exports. Image fallback recovers when `src` changes. Load the stylesheet for the image's default aspect ratio/fit/radius without Tailwind.
40
+
41
+ ```tsx
42
+ import { VirtualizedList } from '@khoangd/ui/virtualized-list';
43
+
44
+ <VirtualizedList
45
+ items={rows}
46
+ itemHeight={32}
47
+ containerHeight={320}
48
+ getItemKey={(row) => row.id}
49
+ renderItem={(row) => <div style={{ height: 32 }}>{row.label}</div>}
50
+ emptyContent={<span>No data</span>}
51
+ />
52
+ ```
53
+
54
+ `VirtualizedList` supports either `renderItem(item, absoluteIndex)` or `renderContent(visibleItems)`. Rows must have the specified fixed height; viewport height is controlled by `containerHeight`. `onScroll` receives the native scroll event. Routing, data fetching, translations and empty-state assets belong to the consumer. Stable `getItemKey` values preserve row identity when sorting/filtering.
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ export interface ErrorViewProps {
3
+ /** Optional icon (e.g. SVG component) */
4
+ icon?: React.ReactNode;
5
+ /** Message or description */
6
+ description?: React.ReactNode;
7
+ /** Retry / refresh handler */
8
+ onRefresh?: () => void;
9
+ /** Additional class name */
10
+ className?: string;
11
+ }
12
+ /**
13
+ * Generic error state with optional icon, description, and retry.
14
+ * App can wrap with antd Empty, i18n, and custom icon.
15
+ */
16
+ export declare function ErrorView({ icon, description, onRefresh, className, }: ErrorViewProps): React.ReactElement;
17
+ export default ErrorView;
18
+ //# sourceMappingURL=ErrorView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorView.d.ts","sourceRoot":"","sources":["../src/ErrorView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,6BAA6B;IAC7B,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,WAAW,EACX,SAAS,EACT,SAAc,GACf,EAAE,cAAc,GAAG,KAAK,CAAC,YAAY,CA2BrC;AAED,eAAe,SAAS,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Generic error state with optional icon, description, and retry.
4
+ * App can wrap with antd Empty, i18n, and custom icon.
5
+ */
6
+ export function ErrorView({ icon, description, onRefresh, className = '', }) {
7
+ return (_jsxs("div", { className: className, style: {
8
+ display: 'flex',
9
+ flexDirection: 'column',
10
+ alignItems: 'center',
11
+ justifyContent: 'center',
12
+ padding: 24,
13
+ textAlign: 'center',
14
+ }, children: [icon && (_jsx("div", { onClick: onRefresh, onKeyDown: (e) => e.key === 'Enter' && onRefresh?.(), role: "button", tabIndex: 0, style: { cursor: onRefresh ? 'pointer' : 'default', marginBottom: 8 }, children: icon })), description != null && _jsx("div", { style: { color: 'var(--TEXT__2, #666)' }, children: description })] }));
15
+ }
16
+ export default ErrorView;
17
+ //# sourceMappingURL=ErrorView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorView.js","sourceRoot":"","sources":["../src/ErrorView.tsx"],"names":[],"mappings":";AAaA;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,EACxB,IAAI,EACJ,WAAW,EACX,SAAS,EACT,SAAS,GAAG,EAAE,GACC;IACf,OAAO,CACL,eACE,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE;YACL,OAAO,EAAE,MAAM;YACf,aAAa,EAAE,QAAQ;YACvB,UAAU,EAAE,QAAQ;YACpB,cAAc,EAAE,QAAQ;YACxB,OAAO,EAAE,EAAE;YACX,SAAS,EAAE,QAAQ;SACpB,aAEA,IAAI,IAAI,CACP,cACE,OAAO,EAAE,SAAS,EAClB,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,SAAS,EAAE,EAAE,EACpD,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,CAAC,EACX,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,CAAC,EAAE,YAEpE,IAAI,GACD,CACP,EACA,WAAW,IAAI,IAAI,IAAI,cAAK,KAAK,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE,YAAG,WAAW,GAAO,IACtF,CACP,CAAC;AACJ,CAAC;AAED,eAAe,SAAS,CAAC"}
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ export interface ImageWithFallbackProps {
3
+ src?: string;
4
+ alt?: string;
5
+ className?: string;
6
+ style?: React.CSSProperties;
7
+ /** Fallback image URL when src fails to load. If not provided, a placeholder is used. */
8
+ fallbackSrc?: string;
9
+ }
10
+ export declare function ImageWithFallback({ src, alt, className, style, fallbackSrc, }: ImageWithFallbackProps): React.ReactElement | null;
11
+ export default ImageWithFallback;
12
+ //# sourceMappingURL=ImageWithFallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageWithFallback.d.ts","sourceRoot":"","sources":["../src/ImageWithFallback.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,MAAM,WAAW,sBAAsB;IACrC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,yFAAyF;IACzF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAKD,wBAAgB,iBAAiB,CAAC,EAChC,GAAQ,EACR,GAAQ,EACR,SAAc,EACd,KAAsB,EACtB,WAAyB,GAC1B,EAAE,sBAAsB,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAgBpD;AAED,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ const PLACEHOLDER = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"%3E%3Crect fill="%23f0f0f0" width="100" height="100"/%3E%3C/svg%3E';
4
+ export function ImageWithFallback({ src = '', alt = '', className = '', style = { width: 100 }, fallbackSrc = PLACEHOLDER, }) {
5
+ const [failedSrc, setFailedSrc] = useState(null);
6
+ const imgSrc = failedSrc === src ? fallbackSrc : src;
7
+ if (!src)
8
+ return null;
9
+ return (_jsx("img", { style: style, className: `shinhan-image aspect-video object-contain rounded-[4px] ${className}`.trim(), src: imgSrc, alt: alt, onError: () => { if (imgSrc !== fallbackSrc)
10
+ setFailedSrc(src); }, loading: "lazy" }));
11
+ }
12
+ export default ImageWithFallback;
13
+ //# sourceMappingURL=ImageWithFallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageWithFallback.js","sourceRoot":"","sources":["../src/ImageWithFallback.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAWxC,MAAM,WAAW,GACf,+KAA+K,CAAC;AAElL,MAAM,UAAU,iBAAiB,CAAC,EAChC,GAAG,GAAG,EAAE,EACR,GAAG,GAAG,EAAE,EACR,SAAS,GAAG,EAAE,EACd,KAAK,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EACtB,WAAW,GAAG,WAAW,GACF;IACvB,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,SAAS,KAAK,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC;IAErD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAEtB,OAAO,CACL,cACE,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,2DAA2D,SAAS,EAAE,CAAC,IAAI,EAAE,EACxF,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,MAAM,KAAK,WAAW;YAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EACjE,OAAO,EAAC,MAAM,GACd,CACH,CAAC;AACJ,CAAC;AAED,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type CSSProperties, type Key, type ReactNode } from 'react';
2
+ export interface VirtualizedListProps<T> {
3
+ items?: readonly T[];
4
+ itemHeight?: number;
5
+ containerHeight?: number;
6
+ className?: string;
7
+ classNameChild?: string;
8
+ style?: CSSProperties;
9
+ tabIndex?: number;
10
+ renderItem?: (item: T, index: number) => ReactNode;
11
+ renderContent?: (items: T[]) => ReactNode;
12
+ getItemKey?: (item: T, index: number) => Key;
13
+ emptyContent?: ReactNode;
14
+ onScroll?: (event: Event) => void;
15
+ }
16
+ /** Fixed-height list with no routing, app state, stylesheet or translation dependency. */
17
+ export declare function VirtualizedList<T>({ items, itemHeight, containerHeight, className, classNameChild, style, tabIndex, renderItem, renderContent, getItemKey, emptyContent, onScroll, }: VirtualizedListProps<T>): import("react/jsx-runtime").JSX.Element;
18
+ //# sourceMappingURL=VirtualizedList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VirtualizedList.d.ts","sourceRoot":"","sources":["../src/VirtualizedList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,aAAa,EAAE,KAAK,GAAG,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGlG,MAAM,WAAW,oBAAoB,CAAC,CAAC;IACrC,KAAK,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IACnD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,SAAS,CAAC;IAC1C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG,CAAC;IAC7C,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACnC;AAED,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,CAAC,EAAE,EACjC,KAAU,EAAE,UAAe,EAAE,eAAqB,EAAE,SAAc,EAAE,cAAmB,EACvF,KAAK,EAAE,QAAY,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,YAAmB,EAAE,QAAQ,GAC1F,EAAE,oBAAoB,CAAC,CAAC,CAAC,2CAqCzB"}
@@ -0,0 +1,36 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Fragment, useEffect, useRef } from 'react';
3
+ import { useVirtualScroll } from '@khoangd/react-utils';
4
+ /** Fixed-height list with no routing, app state, stylesheet or translation dependency. */
5
+ export function VirtualizedList({ items = [], itemHeight = 30, containerHeight = 100, className = '', classNameChild = '', style, tabIndex = 0, renderItem, renderContent, getItemKey, emptyContent = null, onScroll, }) {
6
+ const container = useRef(null);
7
+ const { range, updateRange } = useVirtualScroll(items.length, itemHeight, containerHeight);
8
+ const hasItems = items.length > 0;
9
+ useEffect(() => {
10
+ const element = container.current;
11
+ if (!element)
12
+ return;
13
+ let frame;
14
+ const handleScroll = (event) => {
15
+ onScroll?.(event);
16
+ if (frame !== undefined)
17
+ cancelAnimationFrame(frame);
18
+ frame = requestAnimationFrame(() => updateRange(element.scrollTop));
19
+ };
20
+ updateRange(element.scrollTop);
21
+ element.addEventListener('scroll', handleScroll, { passive: true });
22
+ return () => {
23
+ element.removeEventListener('scroll', handleScroll);
24
+ if (frame !== undefined)
25
+ cancelAnimationFrame(frame);
26
+ };
27
+ }, [hasItems, onScroll, updateRange]);
28
+ if (!hasItems)
29
+ return _jsx(_Fragment, { children: emptyContent });
30
+ const visibleItems = items.slice(range.start, range.end);
31
+ return (_jsx("div", { ref: container, className: className, tabIndex: tabIndex, style: { position: 'relative', overflowY: 'auto', ...style, height: containerHeight }, children: _jsx("div", { style: { height: items.length * itemHeight, position: 'relative' }, children: _jsx("div", { className: classNameChild, style: { transform: `translateY(${range.start * itemHeight}px)` }, children: renderItem ? visibleItems.map((item, index) => {
32
+ const absoluteIndex = range.start + index;
33
+ return _jsx(Fragment, { children: renderItem(item, absoluteIndex) }, getItemKey?.(item, absoluteIndex) ?? absoluteIndex);
34
+ }) : renderContent?.(visibleItems) }) }) }));
35
+ }
36
+ //# sourceMappingURL=VirtualizedList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VirtualizedList.js","sourceRoot":"","sources":["../src/VirtualizedList.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { HTMLAttributes, ReactNode } from 'react';
2
+ export interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
3
+ variant?: 'neutral' | 'success' | 'warning' | 'danger' | 'info';
4
+ icon?: ReactNode;
5
+ }
6
+ /** Semantic status label. Product icons and tooltip behavior are supplied by consumers. */
7
+ export declare function Badge({ variant, icon, className, children, ...props }: BadgeProps): import("react/jsx-runtime").JSX.Element;
8
+ //# sourceMappingURL=dataDisplay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataDisplay.d.ts","sourceRoot":"","sources":["../src/dataDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvD,MAAM,WAAW,UAAW,SAAQ,cAAc,CAAC,eAAe,CAAC;IACjE,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;IAChE,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,2FAA2F;AAC3F,wBAAgB,KAAK,CAAC,EAAE,OAAmB,EAAE,IAAI,EAAE,SAAc,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,UAAU,2CAOlG"}
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /** Semantic status label. Product icons and tooltip behavior are supplied by consumers. */
3
+ export function Badge({ variant = 'neutral', icon, className = '', children, ...props }) {
4
+ return (_jsxs("span", { className: `shinhan-badge shinhan-badge--${variant} ${className}`.trim(), ...props, children: [icon == null ? null : _jsx("span", { className: "shinhan-badge__icon", "aria-hidden": "true", children: icon }), _jsx("span", { children: children })] }));
5
+ }
6
+ //# sourceMappingURL=dataDisplay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataDisplay.js","sourceRoot":"","sources":["../src/dataDisplay.tsx"],"names":[],"mappings":";AAOA,2FAA2F;AAC3F,MAAM,UAAU,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,EAAE,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAc;IACjG,OAAO,CACL,gBAAM,SAAS,EAAE,gCAAgC,OAAO,IAAI,SAAS,EAAE,CAAC,IAAI,EAAE,KAAM,KAAK,aACtF,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,qBAAqB,iBAAa,MAAM,YAAE,IAAI,GAAQ,EAC7F,yBAAO,QAAQ,GAAQ,IAClB,CACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { Component, type ErrorInfo, type HTMLAttributes, type ReactNode } from 'react';
2
+ export declare function Loading({ label, ...props }: HTMLAttributes<HTMLDivElement> & {
3
+ label?: string;
4
+ }): import("react/jsx-runtime").JSX.Element;
5
+ export declare function Skeleton(props: HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
6
+ export declare function Overlay(props: HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
7
+ export interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
8
+ title: ReactNode;
9
+ description?: ReactNode;
10
+ icon?: ReactNode;
11
+ action?: ReactNode;
12
+ }
13
+ export declare function EmptyState({ title, description, icon, action, className, ...props }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
14
+ export type ErrorBoundaryProps = {
15
+ children: ReactNode;
16
+ fallback?: ReactNode;
17
+ onError?: (error: Error, info: ErrorInfo) => void;
18
+ };
19
+ type ErrorBoundaryState = {
20
+ error: Error | null;
21
+ };
22
+ export declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
23
+ state: ErrorBoundaryState;
24
+ static getDerivedStateFromError(error: Error): {
25
+ error: Error;
26
+ };
27
+ componentDidCatch(error: Error, info: ErrorInfo): void;
28
+ render(): string | number | boolean | Iterable<ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
29
+ }
30
+ export {};
31
+ //# sourceMappingURL=feedback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback.d.ts","sourceRoot":"","sources":["../src/feedback.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvF,wBAAgB,OAAO,CAAC,EAAE,KAAiB,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,cAAc,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,2CAE3G;AACD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC,cAAc,CAAC,2CAAkH;AAChL,wBAAgB,OAAO,CAAC,KAAK,EAAE,cAAc,CAAC,cAAc,CAAC,2CAA8F;AAE3J,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IACpF,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,SAAc,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,2CASzG;AAED,MAAM,MAAM,kBAAkB,GAAG;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;CAAE,CAAC;AAClI,KAAK,kBAAkB,GAAG;IAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CAAE,CAAC;AAClD,qBAAa,aAAc,SAAQ,SAAS,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IAClF,KAAK,EAAE,kBAAkB,CAAmB;IAC5C,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK;;;IAC5C,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS;IAC/C,MAAM;CACP"}
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Component } from 'react';
3
+ export function Loading({ label = 'Loading', ...props }) {
4
+ return _jsx("div", { role: "status", "aria-label": label, ...props, children: _jsx("span", { className: "shinhan-spinner", "aria-hidden": "true" }) });
5
+ }
6
+ export function Skeleton(props) { return _jsx("div", { "aria-hidden": "true", ...props, className: `shinhan-skeleton ${props.className ?? ''}`.trim() }); }
7
+ export function Overlay(props) { return _jsx("div", { ...props, className: `shinhan-overlay ${props.className ?? ''}`.trim() }); }
8
+ export function EmptyState({ title, description, icon, action, className = '', ...props }) {
9
+ return (_jsxs("div", { className: `shinhan-empty-state ${className}`.trim(), ...props, children: [icon == null ? null : _jsx("div", { className: "shinhan-empty-state__icon", "aria-hidden": "true", children: icon }), _jsx("strong", { className: "shinhan-empty-state__title", children: title }), description == null ? null : _jsx("div", { className: "shinhan-empty-state__description", children: description }), action == null ? null : _jsx("div", { className: "shinhan-empty-state__action", children: action })] }));
10
+ }
11
+ export class ErrorBoundary extends Component {
12
+ constructor() {
13
+ super(...arguments);
14
+ this.state = { error: null };
15
+ }
16
+ static getDerivedStateFromError(error) { return { error }; }
17
+ componentDidCatch(error, info) { this.props.onError?.(error, info); }
18
+ render() { return this.state.error ? (this.props.fallback ?? _jsx("div", { role: "alert", children: "Something went wrong." })) : this.props.children; }
19
+ }
20
+ //# sourceMappingURL=feedback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback.js","sourceRoot":"","sources":["../src/feedback.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAuD,MAAM,OAAO,CAAC;AAEvF,MAAM,UAAU,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,EAAE,GAAG,KAAK,EAAuD;IAC1G,OAAO,cAAK,IAAI,EAAC,QAAQ,gBAAa,KAAK,KAAM,KAAK,YAAE,eAAM,SAAS,EAAC,iBAAiB,iBAAa,MAAM,GAAG,GAAM,CAAC;AACxH,CAAC;AACD,MAAM,UAAU,QAAQ,CAAC,KAAqC,IAAI,OAAO,6BAAiB,MAAM,KAAK,KAAK,EAAE,SAAS,EAAE,oBAAoB,KAAK,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,GAAI,CAAC,CAAC,CAAC;AAChL,MAAM,UAAU,OAAO,CAAC,KAAqC,IAAI,OAAO,iBAAS,KAAK,EAAE,SAAS,EAAE,mBAAmB,KAAK,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,GAAI,CAAC,CAAC,CAAC;AAS3J,MAAM,UAAU,UAAU,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,GAAG,EAAE,EAAE,GAAG,KAAK,EAAmB;IACxG,OAAO,CACL,eAAK,SAAS,EAAE,uBAAuB,SAAS,EAAE,CAAC,IAAI,EAAE,KAAM,KAAK,aACjE,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAK,SAAS,EAAC,2BAA2B,iBAAa,MAAM,YAAE,IAAI,GAAO,EACjG,iBAAQ,SAAS,EAAC,4BAA4B,YAAE,KAAK,GAAU,EAC9D,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAK,SAAS,EAAC,kCAAkC,YAAE,WAAW,GAAO,EAClG,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAK,SAAS,EAAC,6BAA6B,YAAE,MAAM,GAAO,IAChF,CACP,CAAC;AACJ,CAAC;AAID,MAAM,OAAO,aAAc,SAAQ,SAAiD;IAApF;;QACE,UAAK,GAAuB,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAI9C,CAAC;IAHC,MAAM,CAAC,wBAAwB,CAAC,KAAY,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IACnE,iBAAiB,CAAC,KAAY,EAAE,IAAe,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACvF,MAAM,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,cAAK,IAAI,EAAC,OAAO,sCAA4B,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;CACrI"}
@@ -0,0 +1,82 @@
1
+ import { type InputHTMLAttributes, type LabelHTMLAttributes, type ReactNode, type SelectHTMLAttributes, type TextareaHTMLAttributes } from 'react';
2
+ export interface SwitchProps {
3
+ checked?: boolean;
4
+ onChange?: (checked: boolean) => void;
5
+ checkedChildren?: ReactNode;
6
+ unCheckedChildren?: ReactNode;
7
+ disabled?: boolean;
8
+ className?: string;
9
+ 'aria-label'?: string;
10
+ }
11
+ /** Controlled, keyboard-accessible switch with no application state dependency. */
12
+ export declare const Switch: import("react").ForwardRefExoticComponent<SwitchProps & import("react").RefAttributes<HTMLButtonElement>>;
13
+ export interface InputSelectOption {
14
+ value: string;
15
+ label: ReactNode;
16
+ disabled?: boolean;
17
+ }
18
+ export interface InputSelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'onChange' | 'value'> {
19
+ value?: string;
20
+ onChange?: (value: string, option: InputSelectOption) => void;
21
+ options?: readonly InputSelectOption[];
22
+ placeholder?: string;
23
+ allowClear?: boolean;
24
+ }
25
+ /** Native select primitive. Searchable or remote-data selects stay in app adapters. */
26
+ export declare const InputSelect: import("react").ForwardRefExoticComponent<InputSelectProps & import("react").RefAttributes<HTMLSelectElement>>;
27
+ /** Preferred product-neutral name. InputSelect remains for compatibility. */
28
+ export declare const Select: import("react").ForwardRefExoticComponent<InputSelectProps & import("react").RefAttributes<HTMLSelectElement>>;
29
+ export type SelectOption = InputSelectOption;
30
+ export type SelectProps = InputSelectProps;
31
+ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
32
+ minRows?: number;
33
+ maxRows?: number;
34
+ }
35
+ export declare const Textarea: import("react").ForwardRefExoticComponent<TextareaProps & import("react").RefAttributes<HTMLTextAreaElement>>;
36
+ export interface DatePickerProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'value' | 'onChange'> {
37
+ value?: string;
38
+ onChange?: (value: string) => void;
39
+ }
40
+ /** Native date input using an explicit YYYY-MM-DD value contract. */
41
+ export declare const DatePicker: import("react").ForwardRefExoticComponent<DatePickerProps & import("react").RefAttributes<HTMLInputElement>>;
42
+ export interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'checked' | 'onChange'> {
43
+ checked?: boolean;
44
+ onCheckedChange?: (checked: boolean) => void;
45
+ label?: ReactNode;
46
+ }
47
+ export declare const Checkbox: import("react").ForwardRefExoticComponent<CheckboxProps & import("react").RefAttributes<HTMLInputElement>>;
48
+ export interface RadioOption {
49
+ value: string;
50
+ label: ReactNode;
51
+ disabled?: boolean;
52
+ }
53
+ export interface RadioGroupProps {
54
+ name: string;
55
+ value?: string;
56
+ options: readonly RadioOption[];
57
+ onChange?: (value: string, option: RadioOption) => void;
58
+ disabled?: boolean;
59
+ direction?: 'row' | 'column';
60
+ className?: string;
61
+ 'aria-label'?: string;
62
+ }
63
+ export declare function RadioGroup({ name, value, options, onChange, disabled, direction, className, 'aria-label': ariaLabel, }: RadioGroupProps): import("react/jsx-runtime").JSX.Element;
64
+ export interface SegmentedControlProps {
65
+ value?: string;
66
+ options: readonly RadioOption[];
67
+ onChange?: (value: string, option: RadioOption) => void;
68
+ disabled?: boolean;
69
+ className?: string;
70
+ 'aria-label'?: string;
71
+ }
72
+ export declare function SegmentedControl({ value, options, onChange, disabled, className, 'aria-label': ariaLabel, }: SegmentedControlProps): import("react/jsx-runtime").JSX.Element;
73
+ export interface FormFieldProps extends LabelHTMLAttributes<HTMLLabelElement> {
74
+ label: ReactNode;
75
+ htmlFor?: string;
76
+ hint?: ReactNode;
77
+ error?: ReactNode;
78
+ required?: boolean;
79
+ children: ReactNode;
80
+ }
81
+ export declare function FormField({ label, htmlFor, hint, error, required, children, className, ...props }: FormFieldProps): import("react/jsx-runtime").JSX.Element;
82
+ //# sourceMappingURL=forms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms.d.ts","sourceRoot":"","sources":["../src/forms.tsx"],"names":[],"mappings":"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"}
package/dist/forms.js ADDED
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, } from 'react';
3
+ /** Controlled, keyboard-accessible switch with no application state dependency. */
4
+ export const Switch = forwardRef(function Switch({ checked = false, onChange, checkedChildren, unCheckedChildren, disabled = false, className = '', 'aria-label': ariaLabel, }, ref) {
5
+ return (_jsxs("button", { ref: ref, type: "button", role: "switch", "aria-checked": checked, "aria-label": ariaLabel, disabled: disabled, className: `shinhan-switch ${className}`.trim(), onClick: () => onChange?.(!checked), children: [_jsx("span", { className: "shinhan-switch__content", children: checked ? checkedChildren : unCheckedChildren }), _jsx("span", { className: "shinhan-switch__thumb", "aria-hidden": "true" })] }));
6
+ });
7
+ /** Native select primitive. Searchable or remote-data selects stay in app adapters. */
8
+ export const InputSelect = forwardRef(function InputSelect({ value = '', onChange, options = [], placeholder = '', allowClear = false, className = '', ...props }, ref) {
9
+ const handleChange = (event) => {
10
+ const nextValue = event.target.value;
11
+ const option = options.find((item) => item.value === nextValue);
12
+ onChange?.(nextValue, option ?? { value: nextValue, label: nextValue });
13
+ };
14
+ return (_jsxs("select", { ref: ref, value: value, onChange: handleChange, className: `shinhan-select ${className}`.trim(), ...props, children: [placeholder || allowClear ? _jsx("option", { value: "", children: placeholder || '—' }) : null, options.map((option) => (_jsx("option", { value: option.value, disabled: option.disabled, children: typeof option.label === 'string' || typeof option.label === 'number'
15
+ ? option.label
16
+ : option.value }, option.value)))] }));
17
+ });
18
+ /** Preferred product-neutral name. InputSelect remains for compatibility. */
19
+ export const Select = InputSelect;
20
+ export const Textarea = forwardRef(function Textarea({ value, defaultValue, minRows = 2, maxRows = 6, className = '', ...props }, ref) {
21
+ const content = value ?? defaultValue ?? '';
22
+ const rows = Math.min(Math.max(minRows, String(content).split('\n').length), maxRows);
23
+ return (_jsx("textarea", { ref: ref, value: value, defaultValue: defaultValue, rows: rows, className: `shinhan-textarea ${className}`.trim(), ...props }));
24
+ });
25
+ /** Native date input using an explicit YYYY-MM-DD value contract. */
26
+ export const DatePicker = forwardRef(function DatePicker({ value = '', onChange, className = '', ...props }, ref) {
27
+ return (_jsx("input", { ref: ref, type: "date", value: value, onChange: (event) => onChange?.(event.target.value), className: `shinhan-datepicker ${className}`.trim(), ...props }));
28
+ });
29
+ export const Checkbox = forwardRef(function Checkbox({ checked = false, onCheckedChange, label, className = '', disabled, ...props }, ref) {
30
+ return (_jsxs("label", { className: `shinhan-checkbox ${disabled ? 'shinhan-checkbox--disabled' : ''} ${className}`.trim(), children: [_jsx("input", { ref: ref, type: "checkbox", checked: checked, disabled: disabled, onChange: (event) => onCheckedChange?.(event.target.checked), ...props }), label == null ? null : _jsx("span", { children: label })] }));
31
+ });
32
+ export function RadioGroup({ name, value, options, onChange, disabled = false, direction = 'row', className = '', 'aria-label': ariaLabel, }) {
33
+ return (_jsx("div", { role: "radiogroup", "aria-label": ariaLabel, className: `shinhan-radio-group shinhan-radio-group--${direction} ${className}`.trim(), children: options.map((option) => (_jsxs("label", { className: `shinhan-radio ${disabled || option.disabled ? 'shinhan-radio--disabled' : ''}`, children: [_jsx("input", { type: "radio", name: name, value: option.value, checked: value === option.value, disabled: disabled || option.disabled, onChange: () => onChange?.(option.value, option) }), _jsx("span", { children: option.label })] }, option.value))) }));
34
+ }
35
+ export function SegmentedControl({ value, options, onChange, disabled = false, className = '', 'aria-label': ariaLabel, }) {
36
+ return (_jsx("div", { role: "radiogroup", "aria-label": ariaLabel, className: `shinhan-segmented ${className}`.trim(), children: options.map((option) => {
37
+ const selected = value === option.value;
38
+ return (_jsx("button", { type: "button", role: "radio", "aria-checked": selected, disabled: disabled || option.disabled, className: "shinhan-segmented__item", onClick: () => onChange?.(option.value, option), children: option.label }, option.value));
39
+ }) }));
40
+ }
41
+ export function FormField({ label, htmlFor, hint, error, required = false, children, className = '', ...props }) {
42
+ return (_jsxs("label", { htmlFor: htmlFor, className: `shinhan-field ${className}`.trim(), ...props, children: [_jsxs("span", { className: "shinhan-field__label", children: [label, required ? _jsx("span", { "aria-hidden": "true", children: " *" }) : null] }), children, error == null ? null : _jsx("span", { className: "shinhan-field__error", children: error }), error != null || hint == null ? null : _jsx("span", { className: "shinhan-field__hint", children: hint })] }));
43
+ }
44
+ //# sourceMappingURL=forms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms.js","sourceRoot":"","sources":["../src/forms.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export * from './primitives.js';
2
+ export * from './forms.js';
3
+ export * from './layout.js';
4
+ export * from './feedback.js';
5
+ export * from './dataDisplay.js';
6
+ export { ImageWithFallback } from './ImageWithFallback.js';
7
+ export type { ImageWithFallbackProps } from './ImageWithFallback.js';
8
+ export { ErrorView } from './ErrorView.js';
9
+ export type { ErrorViewProps } from './ErrorView.js';
10
+ export { VirtualizedList } from './VirtualizedList.js';
11
+ export type { VirtualizedListProps } from './VirtualizedList.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export * from './primitives.js';
2
+ export * from './forms.js';
3
+ export * from './layout.js';
4
+ export * from './feedback.js';
5
+ export * from './dataDisplay.js';
6
+ export { ImageWithFallback } from './ImageWithFallback.js';
7
+ export { ErrorView } from './ErrorView.js';
8
+ export { VirtualizedList } from './VirtualizedList.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { CSSProperties, HTMLAttributes, PropsWithChildren } from 'react';
2
+ type StackProps = PropsWithChildren<HTMLAttributes<HTMLDivElement> & {
3
+ direction?: 'row' | 'column';
4
+ gap?: CSSProperties['gap'];
5
+ align?: CSSProperties['alignItems'];
6
+ justify?: CSSProperties['justifyContent'];
7
+ wrap?: boolean;
8
+ }>;
9
+ export declare function Stack({ direction, gap, align, justify, wrap, style, ...props }: StackProps): import("react/jsx-runtime").JSX.Element;
10
+ type GridProps = PropsWithChildren<HTMLAttributes<HTMLDivElement> & {
11
+ columns?: number | string;
12
+ gap?: CSSProperties['gap'];
13
+ }>;
14
+ export declare function Grid({ columns, gap, style, ...props }: GridProps): import("react/jsx-runtime").JSX.Element;
15
+ export interface WrapperProps extends HTMLAttributes<HTMLDivElement> {
16
+ padding?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 8;
17
+ gap?: 0 | 1 | 2 | 3 | 4 | 6 | 8;
18
+ direction?: 'row' | 'column';
19
+ }
20
+ /** Token-aligned container used by products that do not need a layout framework. */
21
+ export declare function Wrapper({ padding, gap, direction, className, style, ...props }: WrapperProps): import("react/jsx-runtime").JSX.Element;
22
+ export {};
23
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../src/layout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE9E,KAAK,UAAU,GAAG,iBAAiB,CAAC,cAAc,CAAC,cAAc,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACnO,wBAAgB,KAAK,CAAC,EAAE,SAAoB,EAAE,GAA8B,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,UAAU,2CAEhI;AAED,KAAK,SAAS,GAAG,iBAAiB,CAAC,cAAc,CAAC,cAAc,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CAAA;CAAE,CAAC,CAAC;AAC/H,wBAAgB,IAAI,CAAC,EAAE,OAAW,EAAE,GAA8B,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,SAAS,2CAE/F;AAED,MAAM,WAAW,YAAa,SAAQ,cAAc,CAAC,cAAc,CAAC;IAClE,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC9B;AAaD,oFAAoF;AACpF,wBAAgB,OAAO,CAAC,EACtB,OAAW,EACX,GAAO,EACP,SAAoB,EACpB,SAAc,EACd,KAAK,EACL,GAAG,KAAK,EACT,EAAE,YAAY,2CAgBd"}
package/dist/layout.js ADDED
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ export function Stack({ direction = 'column', gap = 'var(--shinhan-space-2)', align, justify, wrap, style, ...props }) {
3
+ return _jsx("div", { ...props, style: { display: 'flex', flexDirection: direction, gap, alignItems: align, justifyContent: justify, flexWrap: wrap ? 'wrap' : undefined, ...style } });
4
+ }
5
+ export function Grid({ columns = 1, gap = 'var(--shinhan-space-2)', style, ...props }) {
6
+ return _jsx("div", { ...props, style: { display: 'grid', gridTemplateColumns: typeof columns === 'number' ? `repeat(${columns}, minmax(0, 1fr))` : columns, gap, ...style } });
7
+ }
8
+ const SPACE_PX = {
9
+ 0: 0,
10
+ 1: 4,
11
+ 2: 8,
12
+ 3: 12,
13
+ 4: 16,
14
+ 5: 20,
15
+ 6: 24,
16
+ 8: 32,
17
+ };
18
+ /** Token-aligned container used by products that do not need a layout framework. */
19
+ export function Wrapper({ padding = 2, gap = 0, direction = 'column', className = '', style, ...props }) {
20
+ const gapPx = SPACE_PX[gap] ?? 0;
21
+ return (_jsx("div", { className: `shinhan-wrapper ${className}`.trim(), style: {
22
+ boxSizing: 'border-box',
23
+ padding: SPACE_PX[padding] ?? 8,
24
+ display: gapPx > 0 ? 'flex' : 'block',
25
+ flexDirection: direction,
26
+ gap: gapPx,
27
+ ...style,
28
+ }, ...props }));
29
+ }
30
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../src/layout.tsx"],"names":[],"mappings":";AAGA,MAAM,UAAU,KAAK,CAAC,EAAE,SAAS,GAAG,QAAQ,EAAE,GAAG,GAAG,wBAAwB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAc;IAC/H,OAAO,iBAAS,KAAK,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,GAAG,KAAK,EAAE,GAAI,CAAC;AAClL,CAAC;AAGD,MAAM,UAAU,IAAI,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,GAAG,GAAG,wBAAwB,EAAE,KAAK,EAAE,GAAG,KAAK,EAAa;IAC9F,OAAO,iBAAS,KAAK,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,OAAO,mBAAmB,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,GAAI,CAAC;AAC1K,CAAC;AAQD,MAAM,QAAQ,GAA2B;IACvC,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;CACN,CAAC;AAEF,oFAAoF;AACpF,MAAM,UAAU,OAAO,CAAC,EACtB,OAAO,GAAG,CAAC,EACX,GAAG,GAAG,CAAC,EACP,SAAS,GAAG,QAAQ,EACpB,SAAS,GAAG,EAAE,EACd,KAAK,EACL,GAAG,KAAK,EACK;IACb,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,CACL,cACE,SAAS,EAAE,mBAAmB,SAAS,EAAE,CAAC,IAAI,EAAE,EAChD,KAAK,EAAE;YACL,SAAS,EAAE,YAAY;YACvB,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC;YAC/B,OAAO,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;YACrC,aAAa,EAAE,SAAS;YACxB,GAAG,EAAE,KAAK;YACV,GAAG,KAAK;SACT,KACG,KAAK,GACT,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { type ButtonHTMLAttributes, type InputHTMLAttributes, type ReactNode } from 'react';
2
+ export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
3
+ variant?: 'primary' | 'secondary' | 'ghost';
4
+ loading?: boolean;
5
+ };
6
+ export declare const Button: import("react").ForwardRefExoticComponent<ButtonHTMLAttributes<HTMLButtonElement> & {
7
+ variant?: "primary" | "secondary" | "ghost" | undefined;
8
+ loading?: boolean | undefined;
9
+ } & import("react").RefAttributes<HTMLButtonElement>>;
10
+ export type InputProps = InputHTMLAttributes<HTMLInputElement> & {
11
+ invalid?: boolean;
12
+ };
13
+ export declare const Input: import("react").ForwardRefExoticComponent<InputHTMLAttributes<HTMLInputElement> & {
14
+ invalid?: boolean | undefined;
15
+ } & import("react").RefAttributes<HTMLInputElement>>;
16
+ export interface IconButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'aria-label'> {
17
+ label: string;
18
+ size?: 'small' | 'medium' | 'large';
19
+ variant?: 'ghost' | 'subtle';
20
+ children: ReactNode;
21
+ }
22
+ export declare const IconButton: import("react").ForwardRefExoticComponent<IconButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
23
+ //# sourceMappingURL=primitives.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../src/primitives.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,oBAAoB,EAAE,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAExG,MAAM,MAAM,WAAW,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AACvI,eAAO,MAAM,MAAM;;;qDAIjB,CAAC;AAGH,MAAM,MAAM,UAAU,GAAG,mBAAmB,CAAC,gBAAgB,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AACvF,eAAO,MAAM,KAAK;;oDAEhB,CAAC;AAGH,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,YAAY,CAAC;IAClG,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC7B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,eAAO,MAAM,UAAU,+GAerB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ export const Button = forwardRef(({ variant = 'primary', loading, disabled, className = '', children, ...props }, ref) => (_jsxs("button", { ref: ref, className: `shinhan-button shinhan-button--${variant} ${className}`.trim(), disabled: disabled || loading, "aria-busy": loading || undefined, ...props, children: [loading ? _jsx("span", { className: "shinhan-spinner", "aria-hidden": "true" }) : null, children] })));
4
+ Button.displayName = 'Button';
5
+ export const Input = forwardRef(({ invalid, className = '', ...props }, ref) => (_jsx("input", { ref: ref, className: `shinhan-input ${className}`.trim(), "aria-invalid": invalid || undefined, ...props })));
6
+ Input.displayName = 'Input';
7
+ export const IconButton = forwardRef(function IconButton({ label, size = 'medium', variant = 'ghost', className = '', children, type = 'button', ...props }, ref) {
8
+ return (_jsx("button", { ref: ref, type: type, "aria-label": label, className: `shinhan-icon-button shinhan-icon-button--${size} shinhan-icon-button--${variant} ${className}`.trim(), ...props, children: children }));
9
+ });
10
+ //# sourceMappingURL=primitives.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primitives.js","sourceRoot":"","sources":["../src/primitives.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAuE,MAAM,OAAO,CAAC;AAGxG,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAAiC,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,GAAG,EAAE,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CACxJ,kBAAQ,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,kCAAkC,OAAO,IAAI,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,QAAQ,IAAI,OAAO,eAAa,OAAO,IAAI,SAAS,KAAM,KAAK,aACpK,OAAO,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,iBAAiB,iBAAa,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,IAC5E,CACV,CAAC,CAAC;AACH,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC;AAG9B,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAA+B,CAAC,EAAE,OAAO,EAAE,SAAS,GAAG,EAAE,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAC5G,gBAAO,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,iBAAiB,SAAS,EAAE,CAAC,IAAI,EAAE,kBAAgB,OAAO,IAAI,SAAS,KAAM,KAAK,GAAI,CACnH,CAAC,CAAC;AACH,KAAK,CAAC,WAAW,GAAG,OAAO,CAAC;AAS5B,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAqC,SAAS,UAAU,CAC1F,EAAE,KAAK,EAAE,IAAI,GAAG,QAAQ,EAAE,OAAO,GAAG,OAAO,EAAE,SAAS,GAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,GAAG,QAAQ,EAAE,GAAG,KAAK,EAAE,EAClG,GAAG;IAEH,OAAO,CACL,iBACE,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,IAAI,gBACE,KAAK,EACjB,SAAS,EAAE,4CAA4C,IAAI,yBAAyB,OAAO,IAAI,SAAS,EAAE,CAAC,IAAI,EAAE,KAC7G,KAAK,YAER,QAAQ,GACF,CACV,CAAC;AACJ,CAAC,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@khoangd/ui",
3
+ "version": "0.1.0",
4
+ "description": "Accessible React UI primitives for Shinhan frontend products",
5
+ "type": "module",
6
+ "sideEffects": [
7
+ "**/*.css"
8
+ ],
9
+ "files": [
10
+ "dist",
11
+ "src/styles.css",
12
+ "README.md"
13
+ ],
14
+ "main": "./dist/index.js",
15
+ "module": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js",
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./primitives": {
24
+ "types": "./dist/primitives.d.ts",
25
+ "import": "./dist/primitives.js"
26
+ },
27
+ "./forms": {
28
+ "types": "./dist/forms.d.ts",
29
+ "import": "./dist/forms.js"
30
+ },
31
+ "./layout": {
32
+ "types": "./dist/layout.d.ts",
33
+ "import": "./dist/layout.js"
34
+ },
35
+ "./feedback": {
36
+ "types": "./dist/feedback.d.ts",
37
+ "import": "./dist/feedback.js"
38
+ },
39
+ "./data-display": {
40
+ "types": "./dist/dataDisplay.d.ts",
41
+ "import": "./dist/dataDisplay.js"
42
+ },
43
+ "./styles.css": "./src/styles.css",
44
+ "./package.json": "./package.json",
45
+ "./virtualized-list": {
46
+ "types": "./dist/VirtualizedList.d.ts",
47
+ "import": "./dist/VirtualizedList.js",
48
+ "default": "./dist/VirtualizedList.js"
49
+ }
50
+ },
51
+ "scripts": {
52
+ "build": "tsc -p tsconfig.json",
53
+ "clean": "rm -rf dist",
54
+ "typecheck": "tsc -p tsconfig.json --noEmit"
55
+ },
56
+ "dependencies": {
57
+ "@khoangd/tokens": "workspace:*",
58
+ "@khoangd/react-utils": "workspace:*"
59
+ },
60
+ "peerDependencies": {
61
+ "react": ">=17 <20",
62
+ "react-dom": ">=17 <20"
63
+ },
64
+ "devDependencies": {
65
+ "@types/react": "^18.0.0",
66
+ "@types/react-dom": "^18.0.0",
67
+ "react": "^18.2.0",
68
+ "react-dom": "^18.2.0",
69
+ "typescript": "^4.7.4"
70
+ },
71
+ "publishConfig": {
72
+ "access": "public",
73
+ "registry": "https://registry.npmjs.org/"
74
+ }
75
+ }
package/src/styles.css ADDED
@@ -0,0 +1,60 @@
1
+ @import '@khoangd/tokens/styles.css';
2
+ .shinhan-image { aspect-ratio: 16 / 9; object-fit: contain; border-radius: 4px; }
3
+ .shinhan-button { align-items: center; border: 1px solid transparent; border-radius: var(--shinhan-radius-md); cursor: pointer; display: inline-flex; font: 500 14px/1 var(--shinhan-font-sans); gap: var(--shinhan-space-2); justify-content: center; min-height: 36px; padding: 0 var(--shinhan-space-4); transition: background var(--shinhan-motion-fast); }
4
+ .shinhan-button:disabled { cursor: not-allowed; opacity: .55; }
5
+ .shinhan-button--primary { background: var(--shinhan-color-brand-primary); color: var(--shinhan-color-text-inverse); }
6
+ .shinhan-button--primary:hover:not(:disabled) { background: var(--shinhan-color-brand-primary-hover); }
7
+ .shinhan-button--secondary { background: var(--shinhan-color-surface); border-color: var(--shinhan-color-border); color: var(--shinhan-color-text-primary); }
8
+ .shinhan-button--ghost { background: transparent; color: var(--shinhan-color-text-primary); }
9
+ .shinhan-icon-button { align-items: center; border: 0; border-radius: 50%; color: inherit; cursor: pointer; display: inline-flex; justify-content: center; padding: 0; }
10
+ .shinhan-icon-button--small { height: 24px; width: 24px; }
11
+ .shinhan-icon-button--medium { height: 32px; width: 32px; }
12
+ .shinhan-icon-button--large { height: 40px; width: 40px; }
13
+ .shinhan-icon-button--ghost { background: transparent; }
14
+ .shinhan-icon-button--subtle { background: var(--shinhan-color-surface-subtle); }
15
+ .shinhan-icon-button:hover:not(:disabled) { background: var(--shinhan-color-border); }
16
+ .shinhan-icon-button:disabled { cursor: not-allowed; opacity: .55; }
17
+ .shinhan-input { background: var(--shinhan-color-surface); border: 1px solid var(--shinhan-color-border); border-radius: var(--shinhan-radius-md); box-sizing: border-box; color: var(--shinhan-color-text-primary); font: 400 14px/1.4 var(--shinhan-font-sans); min-height: 36px; padding: 0 var(--shinhan-space-3); }
18
+ .shinhan-input:focus-visible { border-color: var(--shinhan-color-brand-primary); outline: 2px solid rgba(0, 91, 172, .25); }
19
+ .shinhan-input[aria-invalid='true'] { border-color: #d32f2f; }
20
+ .shinhan-select, .shinhan-textarea, .shinhan-datepicker { background: var(--shinhan-color-surface); border: 1px solid var(--shinhan-color-border); border-radius: var(--shinhan-radius-md); box-sizing: border-box; color: var(--shinhan-color-text-primary); font: 400 14px/1.4 var(--shinhan-font-sans); min-height: 36px; padding: var(--shinhan-space-2) var(--shinhan-space-3); width: 100%; }
21
+ .shinhan-textarea { resize: vertical; }
22
+ .shinhan-select:focus-visible, .shinhan-textarea:focus-visible, .shinhan-datepicker:focus-visible { border-color: var(--shinhan-color-brand-primary); outline: 2px solid rgba(0, 91, 172, .25); }
23
+ .shinhan-switch { align-items: center; background: var(--shinhan-color-border); border: 0; border-radius: 9999px; color: var(--shinhan-color-text-primary); cursor: pointer; display: inline-flex; gap: var(--shinhan-space-1); justify-content: space-between; min-height: 24px; min-width: 44px; padding: 2px; position: relative; transition: background var(--shinhan-motion-fast); }
24
+ .shinhan-switch[aria-checked='true'] { background: var(--shinhan-color-brand-primary); color: var(--shinhan-color-text-inverse); }
25
+ .shinhan-switch:disabled { cursor: not-allowed; opacity: .55; }
26
+ .shinhan-switch__content { font: 500 11px/1 var(--shinhan-font-sans); padding: 0 var(--shinhan-space-1); }
27
+ .shinhan-switch__thumb { background: var(--shinhan-color-surface); border-radius: 50%; box-shadow: 0 1px 2px rgba(0, 0, 0, .2); height: 20px; order: -1; transition: transform var(--shinhan-motion-fast); width: 20px; }
28
+ .shinhan-switch[aria-checked='true'] .shinhan-switch__thumb { order: 1; }
29
+ .shinhan-checkbox, .shinhan-radio { align-items: center; color: var(--shinhan-color-text-primary); cursor: pointer; display: inline-flex; font: 400 14px/1.4 var(--shinhan-font-sans); gap: var(--shinhan-space-2); }
30
+ .shinhan-checkbox--disabled, .shinhan-radio--disabled { cursor: not-allowed; opacity: .55; }
31
+ .shinhan-checkbox input, .shinhan-radio input { accent-color: var(--shinhan-color-brand-primary); height: 16px; margin: 0; width: 16px; }
32
+ .shinhan-radio-group { display: flex; gap: var(--shinhan-space-3); }
33
+ .shinhan-radio-group--column { align-items: flex-start; flex-direction: column; }
34
+ .shinhan-segmented { background: var(--shinhan-color-surface-subtle); border-radius: var(--shinhan-radius-md); display: inline-flex; gap: 2px; padding: 2px; }
35
+ .shinhan-segmented__item { background: transparent; border: 0; border-radius: var(--shinhan-radius-sm); color: var(--shinhan-color-text-secondary); cursor: pointer; font: 500 13px/1 var(--shinhan-font-sans); min-height: 28px; padding: 0 var(--shinhan-space-3); }
36
+ .shinhan-segmented__item[aria-checked='true'] { background: var(--shinhan-color-surface); color: var(--shinhan-color-brand-primary); }
37
+ .shinhan-segmented__item:disabled { cursor: not-allowed; opacity: .55; }
38
+ .shinhan-field { color: var(--shinhan-color-text-primary); display: grid; font-family: var(--shinhan-font-sans); gap: var(--shinhan-space-2); }
39
+ .shinhan-field__label { font-size: 13px; font-weight: 600; }
40
+ .shinhan-field__hint, .shinhan-field__error { font-size: 12px; }
41
+ .shinhan-field__hint { color: var(--shinhan-color-text-secondary); }
42
+ .shinhan-field__error { color: #d32f2f; }
43
+ .shinhan-badge { align-items: center; border-radius: 9999px; display: inline-flex; font: 500 12px/1 var(--shinhan-font-sans); gap: var(--shinhan-space-1); min-height: 22px; padding: 0 var(--shinhan-space-2); white-space: nowrap; }
44
+ .shinhan-badge--neutral { background: var(--shinhan-color-surface-subtle); color: var(--shinhan-color-text-secondary); }
45
+ .shinhan-badge--success { background: #def7e7; color: #16803c; }
46
+ .shinhan-badge--warning { background: #fff3d1; color: #8a5700; }
47
+ .shinhan-badge--danger { background: #ffe5e5; color: #b42318; }
48
+ .shinhan-badge--info { background: #e5f1ff; color: #0568c5; }
49
+ .shinhan-badge__icon { display: inline-flex; }
50
+ .shinhan-empty-state { align-items: center; color: var(--shinhan-color-text-primary); display: flex; flex-direction: column; font-family: var(--shinhan-font-sans); gap: var(--shinhan-space-2); justify-content: center; padding: var(--shinhan-space-6); text-align: center; }
51
+ .shinhan-empty-state__icon { color: var(--shinhan-color-text-secondary); display: inline-flex; }
52
+ .shinhan-empty-state__title { font-size: 14px; }
53
+ .shinhan-empty-state__description { color: var(--shinhan-color-text-secondary); font-size: 13px; }
54
+ .shinhan-empty-state__action { margin-top: var(--shinhan-space-2); }
55
+ .shinhan-spinner { animation: shinhan-spin .7s linear infinite; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; display: inline-block; height: 1em; width: 1em; }
56
+ .shinhan-skeleton { animation: shinhan-pulse 1.4s ease-in-out infinite; background: var(--shinhan-color-surface-subtle); border-radius: var(--shinhan-radius-md); min-height: 1em; }
57
+ .shinhan-overlay { background: var(--shinhan-color-overlay); inset: 0; position: fixed; z-index: 1200; }
58
+ @keyframes shinhan-spin { to { transform: rotate(360deg); } }
59
+ @keyframes shinhan-pulse { 50% { opacity: .55; } }
60
+ @media (prefers-reduced-motion: reduce) { .shinhan-spinner, .shinhan-skeleton { animation: none; } }