@seed-design/react 2.4.2 → 2.5.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/lib/components/Skeleton/Skeleton.cjs +21 -1
- package/lib/components/Skeleton/Skeleton.d.ts +4 -1
- package/lib/components/Skeleton/Skeleton.d.ts.map +1 -1
- package/lib/components/Skeleton/Skeleton.js +22 -2
- package/lib/components/Skeleton/Skeleton.test.d.ts +2 -0
- package/lib/components/Skeleton/Skeleton.test.d.ts.map +1 -0
- package/package.json +5 -5
- package/src/components/AGENTS.md +10 -14
- package/src/components/Skeleton/Skeleton.test.tsx +68 -0
- package/src/components/Skeleton/Skeleton.tsx +36 -4
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
require("../../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_responsive = require("../../types/responsive.cjs");
|
|
3
4
|
const require_styled = require("../../utils/styled.cjs");
|
|
4
5
|
const require_createRecipeContext = require("../../utils/createRecipeContext.cjs");
|
|
6
|
+
let _seed_design_css_vars = require("@seed-design/css/vars");
|
|
5
7
|
let _seed_design_react_primitive = require("@seed-design/react-primitive");
|
|
8
|
+
let react = require("react");
|
|
9
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
6
10
|
let _seed_design_css_recipes_skeleton = require("@seed-design/css/recipes/skeleton");
|
|
7
11
|
//#region src/components/Skeleton/Skeleton.tsx
|
|
8
12
|
var { withContext } = require_createRecipeContext.createRecipeContext(_seed_design_css_recipes_skeleton.skeleton);
|
|
9
|
-
|
|
13
|
+
function handleHeight(height) {
|
|
14
|
+
if (!height?.startsWith("lineHeight.")) return height;
|
|
15
|
+
const value = _seed_design_css_vars.vars.$lineHeight[height.slice(11)];
|
|
16
|
+
return typeof value === "string" ? value : height;
|
|
17
|
+
}
|
|
18
|
+
var Skeleton = withContext((0, react.forwardRef)(function Skeleton({ height, ...props }, ref) {
|
|
19
|
+
const { style, restProps } = require_styled.useStyleProps({
|
|
20
|
+
...props,
|
|
21
|
+
height: require_responsive.isResponsiveObject(height) ? Object.fromEntries(Object.entries(height).map(([key, value]) => [key, handleHeight(value)])) : handleHeight(height)
|
|
22
|
+
});
|
|
23
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_seed_design_react_primitive.Primitive.div, {
|
|
24
|
+
ref,
|
|
25
|
+
style,
|
|
26
|
+
...restProps
|
|
27
|
+
});
|
|
28
|
+
}));
|
|
29
|
+
Skeleton.displayName = "Skeleton";
|
|
10
30
|
//#endregion
|
|
11
31
|
exports.Skeleton = Skeleton;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { PrimitiveProps } from '@seed-design/react-primitive';
|
|
2
2
|
import { SkeletonVariantProps } from '@seed-design/css/recipes/skeleton';
|
|
3
|
+
import { LineHeight } from '@seed-design/css/vars';
|
|
4
|
+
import { ResponsiveValue, UnwrapResponsive } from '../../types/responsive';
|
|
3
5
|
import { StyleProps } from '../../utils/styled';
|
|
4
6
|
import type * as React from "react";
|
|
5
|
-
export interface SkeletonProps extends SkeletonVariantProps, PrimitiveProps, Pick<StyleProps, "
|
|
7
|
+
export interface SkeletonProps extends SkeletonVariantProps, PrimitiveProps, Pick<StyleProps, "width">, Omit<React.HTMLAttributes<HTMLDivElement>, "color"> {
|
|
8
|
+
height?: ResponsiveValue<UnwrapResponsive<StyleProps["height"]> | `lineHeight.${LineHeight}`>;
|
|
6
9
|
}
|
|
7
10
|
export declare const Skeleton: React.ForwardRefExoticComponent<SkeletonProps & React.RefAttributes<HTMLDivElement>>;
|
|
8
11
|
//# sourceMappingURL=Skeleton.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Skeleton.d.ts","sourceRoot":"","sources":["../../../src/components/Skeleton/Skeleton.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAEpC,OAAO,
|
|
1
|
+
{"version":3,"file":"Skeleton.d.ts","sourceRoot":"","sources":["../../../src/components/Skeleton/Skeleton.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAEpC,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAIpE,MAAM,WAAW,aACf,SAAQ,oBAAoB,EAC1B,cAAc,EACd,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EACzB,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IACrD,MAAM,CAAC,EAAE,eAAe,CAAC,gBAAgB,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,GAAG,cAAc,UAAU,EAAE,CAAC,CAAC;CAC/F;AAUD,eAAO,MAAM,QAAQ,sFAapB,CAAC"}
|
|
@@ -1,10 +1,30 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { isResponsiveObject } from "../../types/responsive.js";
|
|
3
|
+
import { useStyleProps } from "../../utils/styled.js";
|
|
3
4
|
import { createRecipeContext } from "../../utils/createRecipeContext.js";
|
|
5
|
+
import { vars } from "@seed-design/css/vars";
|
|
4
6
|
import { Primitive } from "@seed-design/react-primitive";
|
|
7
|
+
import { forwardRef } from "react";
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
9
|
import { skeleton } from "@seed-design/css/recipes/skeleton";
|
|
6
10
|
//#region src/components/Skeleton/Skeleton.tsx
|
|
7
11
|
var { withContext } = createRecipeContext(skeleton);
|
|
8
|
-
|
|
12
|
+
function handleHeight(height) {
|
|
13
|
+
if (!height?.startsWith("lineHeight.")) return height;
|
|
14
|
+
const value = vars.$lineHeight[height.slice(11)];
|
|
15
|
+
return typeof value === "string" ? value : height;
|
|
16
|
+
}
|
|
17
|
+
var Skeleton = withContext(forwardRef(function Skeleton({ height, ...props }, ref) {
|
|
18
|
+
const { style, restProps } = useStyleProps({
|
|
19
|
+
...props,
|
|
20
|
+
height: isResponsiveObject(height) ? Object.fromEntries(Object.entries(height).map(([key, value]) => [key, handleHeight(value)])) : handleHeight(height)
|
|
21
|
+
});
|
|
22
|
+
return /* @__PURE__ */ jsx(Primitive.div, {
|
|
23
|
+
ref,
|
|
24
|
+
style,
|
|
25
|
+
...restProps
|
|
26
|
+
});
|
|
27
|
+
}));
|
|
28
|
+
Skeleton.displayName = "Skeleton";
|
|
9
29
|
//#endregion
|
|
10
30
|
export { Skeleton };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Skeleton.test.d.ts","sourceRoot":"","sources":["../../../src/components/Skeleton/Skeleton.test.tsx"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seed-design/react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/daangn/seed-design.git",
|
|
@@ -50,15 +50,15 @@
|
|
|
50
50
|
"@seed-design/react-collapsible": "^1.0.1",
|
|
51
51
|
"@seed-design/react-dialog": "^2.0.3",
|
|
52
52
|
"@seed-design/react-date-picker": "^1.2.0",
|
|
53
|
-
"@seed-design/react-drawer": "^2.0.
|
|
53
|
+
"@seed-design/react-drawer": "^2.0.7",
|
|
54
54
|
"@seed-design/react-field": "^2.0.1",
|
|
55
55
|
"@seed-design/react-field-button": "^2.0.1",
|
|
56
56
|
"@seed-design/react-fieldset": "^1.0.1",
|
|
57
57
|
"@seed-design/react-file-upload": "^1.0.1",
|
|
58
58
|
"@seed-design/react-image": "^1.1.0",
|
|
59
|
-
"@seed-design/react-menu": "^2.0.
|
|
59
|
+
"@seed-design/react-menu": "^2.0.3",
|
|
60
60
|
"@seed-design/react-middle-truncate": "^1.0.1",
|
|
61
|
-
"@seed-design/react-navigation-menu": "^2.0.
|
|
61
|
+
"@seed-design/react-navigation-menu": "^2.0.2",
|
|
62
62
|
"@seed-design/react-popover": "^2.0.1",
|
|
63
63
|
"@seed-design/react-portal": "^2.0.1",
|
|
64
64
|
"@seed-design/react-primitive": "^2.0.1",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"@seed-design/react-radio-group": "^2.0.1",
|
|
69
69
|
"@seed-design/react-scale-feedback": "^1.0.0",
|
|
70
70
|
"@seed-design/react-segmented-control": "^2.0.2",
|
|
71
|
-
"@seed-design/react-select": "^1.0.
|
|
71
|
+
"@seed-design/react-select": "^1.0.2",
|
|
72
72
|
"@seed-design/react-side-navigation": "^1.0.1",
|
|
73
73
|
"@seed-design/react-slider": "^2.0.1",
|
|
74
74
|
"@seed-design/react-snackbar": "^2.0.1",
|
package/src/components/AGENTS.md
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
# packages/react/src/components
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@seed-design/react` 컴포넌트 구현 디렉터리다. 컴포넌트마다 `PascalCase` 폴더에 `{ComponentName}.tsx`와 `index.ts`를 둔다.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 규칙
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
### Public surface
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
9
|
+
- 구현은 `{ComponentName}.tsx`나 역할별 구현 파일에 두고, 공개 re-export는 `index.ts`와 namespace 파일에서만 한다. `{ComponentName}.namespace.ts`는 compound 컴포넌트에 namespace 단축 이름이 필요할 때만 추가한다.
|
|
10
|
+
- export는 사용자에게 의미가 분명한 slot만 노출한다. animation·layout helper slot은 namespace에서도 숨기고, direct composition이나 styling escape hatch 같은 사용자 시나리오가 있을 때만 공개한다.
|
|
11
|
+
- compound 컴포넌트를 설계·변경함 → React API만 닫지 말고 `docs/registry/react/ui/` snippet이 어떤 convenience layer를 줄지 함께 정한다.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
### Headless 재사용
|
|
14
14
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
- compound component를 설계할 때는 React API만 닫지 말고 `docs/registry/react/ui` snippet surface가 어떤 convenience layer를 제공할지도 함께 생각한다.
|
|
19
|
-
- headless context가 제공하는 `stateProps`/slot props는 styled layer에서 다시 계산하지 않는다. `createWithStateProps([useHeadlessContext])`처럼 기존 context를 직접 재사용하고, 동일한 `data-*` props를 위한 중복 helper export는 만들지 않는다.
|
|
20
|
-
- headless primitive wrapper의 props는 hook props를 재사용한다. `ComponentItemProps extends UseComponentItemProps`처럼 한 source를 확장하고, 같은 prop을 컴포넌트 props에 다시 선언하지 않는다.
|
|
21
|
-
- headless hook prop이 discriminated union이면 styled wrapper도 union contract를 보존한다. mode별로 hook props를 재조합해야 할 때는 branch를 나누어 특정 mode에서 무효한 prop이 섞이지 않게 한다.
|
|
15
|
+
- headless context의 `stateProps`·slot props → styled layer에서 다시 계산하지 않는다. `createWithStateProps([useHeadlessContext])`(`../../utils/createWithStateProps`)로 기존 context를 재사용하고, 같은 `data-*` props용 helper를 따로 export하지 않는다. 예: `NavigationMenu/NavigationMenu.tsx`.
|
|
16
|
+
- headless primitive wrapper의 props → `ComponentItemProps extends UseComponentItemProps`처럼 훅 props를 확장한다. 같은 prop을 다시 선언하지 않는다.
|
|
17
|
+
- headless 훅 prop이 discriminated union → styled wrapper도 union contract를 유지한다. mode별로 훅 props를 다시 조합해야 하면 branch를 나눠 특정 mode에서 무효한 prop이 섞이지 않게 한다.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { vars } from "@seed-design/css/vars";
|
|
2
|
+
import { render } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, it } from "bun:test";
|
|
4
|
+
import { createRef } from "react";
|
|
5
|
+
import { Skeleton } from "./Skeleton";
|
|
6
|
+
|
|
7
|
+
describe("Skeleton", () => {
|
|
8
|
+
it.each(["t4", "t4Static"] as const)("resolves lineHeight.%s", (token) => {
|
|
9
|
+
const { getByTestId } = render(
|
|
10
|
+
<Skeleton data-testid="skeleton" height={`lineHeight.${token}`} />,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
expect(getByTestId("skeleton").style.getPropertyValue("--seed-box-height-base")).toBe(
|
|
14
|
+
vars.$lineHeight[token],
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("supports line-height and dimension tokens in responsive heights", () => {
|
|
19
|
+
const { getByTestId } = render(
|
|
20
|
+
<Skeleton
|
|
21
|
+
data-testid="skeleton"
|
|
22
|
+
height={{ base: "lineHeight.t4", sm: undefined, md: "lineHeight.t5", lg: "x8" }}
|
|
23
|
+
/>,
|
|
24
|
+
);
|
|
25
|
+
const { style } = getByTestId("skeleton");
|
|
26
|
+
|
|
27
|
+
expect(style.getPropertyValue("--seed-box-height-base")).toBe(vars.$lineHeight.t4);
|
|
28
|
+
expect(style.getPropertyValue("--seed-box-height-sm")).toBe("");
|
|
29
|
+
expect(style.getPropertyValue("--seed-box-height-md")).toBe(vars.$lineHeight.t5);
|
|
30
|
+
expect(style.getPropertyValue("--seed-box-height-lg")).toBe(vars.$dimension.x8);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it.each([
|
|
34
|
+
[undefined, ""],
|
|
35
|
+
["x4", vars.$dimension.x4],
|
|
36
|
+
["24px", "24px"],
|
|
37
|
+
["var(--custom-height)", "var(--custom-height)"],
|
|
38
|
+
["lineHeight.invalid", "lineHeight.invalid"],
|
|
39
|
+
["lineHeight.constructor", "lineHeight.constructor"],
|
|
40
|
+
])("preserves existing height %s", (height, expected) => {
|
|
41
|
+
const { getByTestId } = render(<Skeleton data-testid="skeleton" height={height} />);
|
|
42
|
+
|
|
43
|
+
expect(getByTestId("skeleton").style.getPropertyValue("--seed-box-height-base")).toBe(expected);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("preserves styles, className and ref with asChild", () => {
|
|
47
|
+
const ref = createRef<HTMLDivElement>();
|
|
48
|
+
const { getByTestId } = render(
|
|
49
|
+
<Skeleton
|
|
50
|
+
ref={ref}
|
|
51
|
+
asChild
|
|
52
|
+
height="lineHeight.t4"
|
|
53
|
+
width="x8"
|
|
54
|
+
className="custom"
|
|
55
|
+
style={{ height: "32px" }}
|
|
56
|
+
>
|
|
57
|
+
<div data-testid="skeleton" />
|
|
58
|
+
</Skeleton>,
|
|
59
|
+
);
|
|
60
|
+
const element = getByTestId("skeleton");
|
|
61
|
+
|
|
62
|
+
expect<HTMLElement | null>(ref.current).toBe(element);
|
|
63
|
+
expect(element).toHaveClass("custom");
|
|
64
|
+
expect(element.style.height).toBe("32px");
|
|
65
|
+
expect(element.style.getPropertyValue("--seed-box-width-base")).toBe(vars.$dimension.x8);
|
|
66
|
+
expect(element).not.toHaveAttribute("height");
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -1,15 +1,47 @@
|
|
|
1
1
|
import { Primitive, type PrimitiveProps } from "@seed-design/react-primitive";
|
|
2
2
|
import { skeleton, type SkeletonVariantProps } from "@seed-design/css/recipes/skeleton";
|
|
3
|
+
import { vars, type LineHeight } from "@seed-design/css/vars";
|
|
3
4
|
import type * as React from "react";
|
|
5
|
+
import { forwardRef } from "react";
|
|
6
|
+
import {
|
|
7
|
+
isResponsiveObject,
|
|
8
|
+
type ResponsiveValue,
|
|
9
|
+
type UnwrapResponsive,
|
|
10
|
+
} from "../../types/responsive";
|
|
4
11
|
import { createRecipeContext } from "../../utils/createRecipeContext";
|
|
5
|
-
import {
|
|
12
|
+
import { useStyleProps, type StyleProps } from "../../utils/styled";
|
|
6
13
|
|
|
7
14
|
const { withContext } = createRecipeContext(skeleton);
|
|
8
15
|
|
|
9
16
|
export interface SkeletonProps
|
|
10
17
|
extends SkeletonVariantProps,
|
|
11
18
|
PrimitiveProps,
|
|
12
|
-
Pick<StyleProps, "
|
|
13
|
-
Omit<React.HTMLAttributes<HTMLDivElement>, "color"> {
|
|
19
|
+
Pick<StyleProps, "width">,
|
|
20
|
+
Omit<React.HTMLAttributes<HTMLDivElement>, "color"> {
|
|
21
|
+
height?: ResponsiveValue<UnwrapResponsive<StyleProps["height"]> | `lineHeight.${LineHeight}`>;
|
|
22
|
+
}
|
|
14
23
|
|
|
15
|
-
|
|
24
|
+
function handleHeight(height: string | undefined) {
|
|
25
|
+
if (!height?.startsWith("lineHeight.")) return height;
|
|
26
|
+
|
|
27
|
+
const value = vars.$lineHeight[height.slice("lineHeight.".length) as LineHeight];
|
|
28
|
+
|
|
29
|
+
return typeof value === "string" ? value : height;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const Skeleton = withContext<HTMLDivElement, SkeletonProps>(
|
|
33
|
+
forwardRef<HTMLDivElement, SkeletonProps>(function Skeleton({ height, ...props }, ref) {
|
|
34
|
+
const { style, restProps } = useStyleProps({
|
|
35
|
+
...props,
|
|
36
|
+
height: isResponsiveObject(height)
|
|
37
|
+
? Object.fromEntries(
|
|
38
|
+
Object.entries(height).map(([key, value]) => [key, handleHeight(value)]),
|
|
39
|
+
)
|
|
40
|
+
: handleHeight(height),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return <Primitive.div ref={ref} style={style} {...restProps} />;
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
Skeleton.displayName = "Skeleton";
|