@hjmds/design-contracts 1.50.0 โ†’ 2.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.
Files changed (56) hide show
  1. package/dist/base-recipes.d.ts +0 -7
  2. package/dist/base-recipes.d.ts.map +1 -1
  3. package/dist/base-recipes.js +3 -7
  4. package/dist/base-recipes.js.map +1 -1
  5. package/dist/behaviors.d.ts +7 -7
  6. package/dist/catalog.d.ts +66 -9
  7. package/dist/catalog.d.ts.map +1 -1
  8. package/dist/catalog.js +1 -1
  9. package/dist/catalog.js.map +1 -1
  10. package/dist/component-recipes.d.ts +14 -0
  11. package/dist/component-recipes.d.ts.map +1 -1
  12. package/dist/component-recipes.js +4 -0
  13. package/dist/component-recipes.js.map +1 -1
  14. package/dist/date-range.d.ts +56 -8
  15. package/dist/date-range.d.ts.map +1 -1
  16. package/dist/date-range.js +37 -5
  17. package/dist/date-range.js.map +1 -1
  18. package/dist/icon-button-recipe.d.ts +0 -6
  19. package/dist/icon-button-recipe.d.ts.map +1 -1
  20. package/dist/icon-button-recipe.js +8 -11
  21. package/dist/icon-button-recipe.js.map +1 -1
  22. package/dist/image.d.ts +36 -0
  23. package/dist/image.d.ts.map +1 -1
  24. package/dist/image.js +44 -0
  25. package/dist/image.js.map +1 -1
  26. package/dist/screen-patterns.d.ts +8 -0
  27. package/dist/screen-patterns.d.ts.map +1 -1
  28. package/dist/screen-patterns.js +5 -0
  29. package/dist/screen-patterns.js.map +1 -1
  30. package/dist/tag.d.ts +20 -7
  31. package/dist/tag.d.ts.map +1 -1
  32. package/dist/tag.js +20 -9
  33. package/dist/tag.js.map +1 -1
  34. package/dist/version.d.ts +1 -1
  35. package/dist/version.d.ts.map +1 -1
  36. package/dist/version.js +1 -1
  37. package/dist/version.js.map +1 -1
  38. package/docs/generated/component-maturity.md +1 -1
  39. package/docs/generated/renderer-evidence.json +3 -3
  40. package/docs/generated/renderer-evidence.md +1 -1
  41. package/docs/generated/showcase-manifest.json +3 -2
  42. package/docs/tag.md +14 -5
  43. package/docs/theme-palette.md +3 -1
  44. package/docs/usage/components/auth-screen-layout.md +2 -0
  45. package/docs/usage/components/avatar.md +13 -0
  46. package/docs/usage/components/button.md +4 -3
  47. package/docs/usage/components/chart.md +1 -0
  48. package/docs/usage/components/comment-thread-row.md +4 -1
  49. package/docs/usage/components/date-range-picker.md +9 -3
  50. package/docs/usage/components/icon-button.md +6 -6
  51. package/docs/usage/components/image.md +7 -0
  52. package/docs/usage/components/profile-summary.md +27 -2
  53. package/docs/usage/components/radio-group.md +18 -0
  54. package/docs/usage/components/sheet.md +1 -1
  55. package/docs/usage/components/tag.md +8 -5
  56. package/package.json +1 -1
package/dist/tag.js CHANGED
@@ -3,6 +3,8 @@ import { semanticColors } from "./semantic-colors.js";
3
3
  import { fontWeight, spacing, stroke } from "./foundations.js";
4
4
  export const tagDefaults = {
5
5
  tone: "neutral",
6
+ // The approved border-only capsule is the shared default (2026-10-29).
7
+ variant: "outline",
6
8
  };
7
9
  function assertCopy(value, field) {
8
10
  if (value.trim().length === 0) {
@@ -18,6 +20,9 @@ const supportedTones = [
18
20
  ];
19
21
  export function validateTagDescriptor(descriptor) {
20
22
  assertCopy(descriptor.label, "label");
23
+ if (descriptor.variant !== undefined && !["default", "outline"].includes(descriptor.variant)) {
24
+ throw new TypeError(`Unsupported Tag variant: ${String(descriptor.variant)}`);
25
+ }
21
26
  if (descriptor.tone !== undefined &&
22
27
  !supportedTones.includes(descriptor.tone)) {
23
28
  throw new TypeError(`Unsupported Tag tone: ${String(descriptor.tone)}`);
@@ -28,14 +33,10 @@ export function resolveTagDescriptor(descriptor) {
28
33
  return {
29
34
  label: descriptor.label,
30
35
  tone: descriptor.tone ?? tagDefaults.tone,
36
+ variant: descriptor.variant ?? tagDefaults.variant,
31
37
  };
32
38
  }
33
- /**
34
- * Rectangular, not the pill `radius.full` that `chipRecipe`
35
- * use, so a static Tag never reads as a pressable Chip or a status Badge at a
36
- * glance. No `states` and no `focus` contract: nothing here is ever pressed
37
- * or focused.
38
- */
39
+ /** Static metadata has no focus or press contract, including the outline capsule. */
39
40
  export const tagRecipe = {
40
41
  slots: ["root", "label"],
41
42
  defaults: tagDefaults,
@@ -77,15 +78,25 @@ export const tagRecipe = {
77
78
  textVariant: "caption",
78
79
  fontWeight: fontWeight.medium,
79
80
  },
81
+ // Profile fact rails need breathing room without a colored plate (2026-10-29 approved preview).
82
+ outline: {
83
+ minHeight: 32,
84
+ paddingHorizontal: 10,
85
+ paddingVertical: 6,
86
+ radius: "full",
87
+ fontWeight: fontWeight.regular,
88
+ background: "transparent",
89
+ content: semanticColors.content.primary,
90
+ },
80
91
  radius: "sm",
81
92
  borderWidth: stroke.default,
82
93
  };
83
94
  /** Resolve the same Tag recipe for non-CSS renderers. */
84
- export function resolveTagPresentation(tone, palette) {
95
+ export function resolveTagPresentation(tone, palette, variant = tagDefaults.variant) {
85
96
  const contract = tagRecipe.tones[tone];
86
97
  return {
87
- background: resolveColorReference(contract.background, palette),
88
- content: resolveColorReference(contract.content, palette),
98
+ background: variant === "outline" ? "transparent" : resolveColorReference(contract.background, palette),
99
+ content: resolveColorReference(variant === "outline" && tone === "neutral" ? tagRecipe.outline.content : contract.content, palette),
89
100
  border: contract.border === null
90
101
  ? null
91
102
  : resolveColorReference(contract.border, palette),
package/dist/tag.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tag.js","sourceRoot":"","sources":["../src/tag.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveColorReference,\n type ColorReferencePalette,\n} from \"./color-references.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\nimport { fontWeight, spacing, stroke } from \"./foundations.js\";\n\nexport type TagTone = \"neutral\" | \"info\" | \"success\" | \"attention\" | \"brand\";\n\nexport const tagDefaults = {\n tone: \"neutral\",\n} as const satisfies Readonly<{ tone: TagTone }>;\n\n/**\n * A single piece of static metadata (a position, a grade, a season). Tag has\n * no interaction axis at all โ€” no selected, no pressed, no closable. Removing\n * or toggling a tag is deselection, which belongs to `Chip`.\n */\nexport type TagDescriptor = Readonly<{\n label: string;\n tone?: TagTone;\n}>;\n\nexport type ResolvedTagDescriptor = Readonly<{\n label: string;\n tone: TagTone;\n}>;\n\nfunction assertCopy(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Tag ${field} must not be empty`);\n }\n}\n\nconst supportedTones: readonly TagTone[] = [\n \"neutral\",\n \"info\",\n \"success\",\n \"attention\",\n \"brand\",\n];\n\nexport function validateTagDescriptor(descriptor: TagDescriptor): void {\n assertCopy(descriptor.label, \"label\");\n if (\n descriptor.tone !== undefined &&\n !supportedTones.includes(descriptor.tone)\n ) {\n throw new TypeError(`Unsupported Tag tone: ${String(descriptor.tone)}`);\n }\n}\n\nexport function resolveTagDescriptor(\n descriptor: TagDescriptor,\n): ResolvedTagDescriptor {\n validateTagDescriptor(descriptor);\n return {\n label: descriptor.label,\n tone: descriptor.tone ?? tagDefaults.tone,\n };\n}\n\n/**\n * Rectangular, not the pill `radius.full` that `chipRecipe`\n * use, so a static Tag never reads as a pressable Chip or a status Badge at a\n * glance. No `states` and no `focus` contract: nothing here is ever pressed\n * or focused.\n */\nexport const tagRecipe = {\n slots: [\"root\", \"label\"] as const,\n defaults: tagDefaults,\n tones: {\n // Same as badgeRecipe: a canvas plate without a border vanished on the canvas (2026-10-17 review E3).\n neutral: {\n background: semanticColors.canvas,\n content: semanticColors.content.secondary,\n border: semanticColors.border.default,\n },\n brand: {\n background: semanticColors.canvas,\n content: semanticColors.content.brand,\n border: semanticColors.border.default,\n },\n info: {\n background: semanticColors.canvas,\n content: semanticColors.feedback.info.foreground,\n border: semanticColors.feedback.info.border,\n },\n success: {\n background: semanticColors.feedback.success.badgeBackground,\n content: semanticColors.feedback.success.foreground,\n border: semanticColors.feedback.success.border,\n },\n attention: {\n background: semanticColors.feedback.attention.badgeBackground,\n content: semanticColors.feedback.attention.foreground,\n border: semanticColors.feedback.attention.border,\n },\n },\n size: {\n // Static metadata still participates in Dynamic Type. A fixed 20px frame\n // clips the label at accessibility sizes; minHeight preserves the compact\n // base shape while allowing the intrinsic line box to grow.\n minHeight: 20,\n paddingHorizontal: spacing.xxs,\n gap: spacing.xxs,\n textVariant: \"caption\",\n fontWeight: fontWeight.medium,\n },\n radius: \"sm\",\n borderWidth: stroke.default,\n} as const;\n\nexport type ResolvedTagPresentation = Readonly<{\n background: string;\n content: string;\n border: string | null;\n}>;\n\n/** Resolve the same Tag recipe for non-CSS renderers. */\nexport function resolveTagPresentation(\n tone: TagTone,\n palette: ColorReferencePalette,\n): ResolvedTagPresentation {\n const contract = tagRecipe.tones[tone];\n return {\n background: resolveColorReference(contract.background, palette),\n content: resolveColorReference(contract.content, palette),\n border:\n contract.border === null\n ? null\n : resolveColorReference(contract.border, palette),\n };\n}\n"]}
1
+ {"version":3,"file":"tag.js","sourceRoot":"","sources":["../src/tag.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveColorReference,\n type ColorReferencePalette,\n} from \"./color-references.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\nimport { fontWeight, spacing, stroke } from \"./foundations.js\";\n\nexport type TagTone = \"neutral\" | \"info\" | \"success\" | \"attention\" | \"brand\";\n\nexport type TagVariant = \"default\" | \"outline\";\n\nexport const tagDefaults = {\n tone: \"neutral\",\n // The approved border-only capsule is the shared default (2026-10-29).\n variant: \"outline\",\n} as const satisfies Readonly<{ tone: TagTone; variant: TagVariant }>;\n\n/**\n * A single piece of static metadata (a position, a grade, a season). Tag has\n * no interaction axis at all โ€” no selected, no pressed, no closable. Removing\n * or toggling a tag is deselection, which belongs to `Chip`.\n */\nexport type TagDescriptor = Readonly<{\n label: string;\n tone?: TagTone;\n variant?: TagVariant;\n}>;\n\nexport type ResolvedTagDescriptor = Readonly<{\n label: string;\n tone: TagTone;\n variant: TagVariant;\n}>;\n\nfunction assertCopy(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Tag ${field} must not be empty`);\n }\n}\n\nconst supportedTones: readonly TagTone[] = [\n \"neutral\",\n \"info\",\n \"success\",\n \"attention\",\n \"brand\",\n];\n\nexport function validateTagDescriptor(descriptor: TagDescriptor): void {\n assertCopy(descriptor.label, \"label\");\n if (descriptor.variant !== undefined && ![\"default\", \"outline\"].includes(descriptor.variant)) {\n throw new TypeError(`Unsupported Tag variant: ${String(descriptor.variant)}`);\n }\n if (\n descriptor.tone !== undefined &&\n !supportedTones.includes(descriptor.tone)\n ) {\n throw new TypeError(`Unsupported Tag tone: ${String(descriptor.tone)}`);\n }\n}\n\nexport function resolveTagDescriptor(\n descriptor: TagDescriptor,\n): ResolvedTagDescriptor {\n validateTagDescriptor(descriptor);\n return {\n label: descriptor.label,\n tone: descriptor.tone ?? tagDefaults.tone,\n variant: descriptor.variant ?? tagDefaults.variant,\n };\n}\n\n/** Static metadata has no focus or press contract, including the outline capsule. */\nexport const tagRecipe = {\n slots: [\"root\", \"label\"] as const,\n defaults: tagDefaults,\n tones: {\n // Same as badgeRecipe: a canvas plate without a border vanished on the canvas (2026-10-17 review E3).\n neutral: {\n background: semanticColors.canvas,\n content: semanticColors.content.secondary,\n border: semanticColors.border.default,\n },\n brand: {\n background: semanticColors.canvas,\n content: semanticColors.content.brand,\n border: semanticColors.border.default,\n },\n info: {\n background: semanticColors.canvas,\n content: semanticColors.feedback.info.foreground,\n border: semanticColors.feedback.info.border,\n },\n success: {\n background: semanticColors.feedback.success.badgeBackground,\n content: semanticColors.feedback.success.foreground,\n border: semanticColors.feedback.success.border,\n },\n attention: {\n background: semanticColors.feedback.attention.badgeBackground,\n content: semanticColors.feedback.attention.foreground,\n border: semanticColors.feedback.attention.border,\n },\n },\n size: {\n // Static metadata still participates in Dynamic Type. A fixed 20px frame\n // clips the label at accessibility sizes; minHeight preserves the compact\n // base shape while allowing the intrinsic line box to grow.\n minHeight: 20,\n paddingHorizontal: spacing.xxs,\n gap: spacing.xxs,\n textVariant: \"caption\",\n fontWeight: fontWeight.medium,\n },\n // Profile fact rails need breathing room without a colored plate (2026-10-29 approved preview).\n outline: {\n minHeight: 32,\n paddingHorizontal: 10,\n paddingVertical: 6,\n radius: \"full\",\n fontWeight: fontWeight.regular,\n background: \"transparent\",\n content: semanticColors.content.primary,\n },\n radius: \"sm\",\n borderWidth: stroke.default,\n} as const;\n\nexport type ResolvedTagPresentation = Readonly<{\n background: string;\n content: string;\n border: string | null;\n}>;\n\n/** Resolve the same Tag recipe for non-CSS renderers. */\nexport function resolveTagPresentation(\n tone: TagTone,\n palette: ColorReferencePalette,\n variant: TagVariant = tagDefaults.variant,\n): ResolvedTagPresentation {\n const contract = tagRecipe.tones[tone];\n return {\n background: variant === \"outline\" ? \"transparent\" : resolveColorReference(contract.background, palette),\n content: resolveColorReference(variant === \"outline\" && tone === \"neutral\" ? tagRecipe.outline.content : contract.content, palette),\n border:\n contract.border === null\n ? null\n : resolveColorReference(contract.border, palette),\n };\n}\n"]}
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Package release shown by documentation surfaces. Kept in sync by a test. */
2
- export declare const designSystemVersion: "1.50.0";
2
+ export declare const designSystemVersion: "2.1.0";
3
3
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,eAAO,MAAM,mBAAmB,EAAG,QAAiB,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,eAAO,MAAM,mBAAmB,EAAG,OAAgB,CAAC"}
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Package release shown by documentation surfaces. Kept in sync by a test. */
2
- export const designSystemVersion = "1.50.0";
2
+ export const designSystemVersion = "2.1.0";
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.50.0\" as const;\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,OAAgB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"2.1.0\" as const;\n"]}
@@ -1,6 +1,6 @@
1
1
  # Component maturity and evidence manifest
2
2
 
3
- > Generated from `@hjmds/design-contracts` 1.50.0. Do not edit this file directly; run `pnpm contracts:sync`.
3
+ > Generated from `@hjmds/design-contracts` 2.1.0. Do not edit this file directly; run `pnpm contracts:sync`.
4
4
 
5
5
  This document is the human-readable projection of the same catalog and Showcase manifest consumed by product CI.
6
6
 
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "designSystemVersion": "1.50.0",
3
+ "designSystemVersion": "2.1.0",
4
4
  "source": "packages/design-contracts/docs/generated/showcase-manifest.json",
5
5
  "surfaces": {
6
6
  "web": {
7
7
  "packageName": "@hjmds/react",
8
- "packageVersion": "1.50.0",
8
+ "packageVersion": "2.1.0",
9
9
  "activeComponents": 132,
10
10
  "claimedComponents": 132,
11
11
  "completeScenarioComponents": 131,
@@ -7114,7 +7114,7 @@
7114
7114
  },
7115
7115
  "native": {
7116
7116
  "packageName": "@hjmds/react-native",
7117
- "packageVersion": "1.50.0",
7117
+ "packageVersion": "2.1.0",
7118
7118
  "activeComponents": 115,
7119
7119
  "claimedComponents": 115,
7120
7120
  "completeScenarioComponents": 112,
@@ -1,6 +1,6 @@
1
1
  # Renderer evidence coverage
2
2
 
3
- > Generated for HJM 1.50.0. Do not edit directly; run `pnpm evidence:sync`.
3
+ > Generated for HJM 2.1.0. Do not edit directly; run `pnpm evidence:sync`.
4
4
 
5
5
  This projection joins the canonical surface maturity manifest with first-party renderer claims. Missing scenarios are explicit beta promotion debt; stable surfaces are blocked by CI until none remain.
6
6
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "designSystemVersion": "1.50.0",
3
+ "designSystemVersion": "2.1.0",
4
4
  "packageName": "@hjmds/design-contracts",
5
5
  "components": [
6
6
  {
@@ -4295,7 +4295,8 @@
4295
4295
  "extensions": [
4296
4296
  "GridReveal",
4297
4297
  "ImageViewer",
4298
- "ImageViewerTrigger"
4298
+ "ImageViewerTrigger",
4299
+ "CircularPhotoCropper"
4299
4300
  ],
4300
4301
  "category": "data-display",
4301
4302
  "platform": "shared",
package/docs/tag.md CHANGED
@@ -4,7 +4,7 @@
4
4
  `2026 ์‹œ์ฆŒ` โ€” ์— ์ผ๊ด€๋œ ์ด๋ฆ„๊ณผ ํ†ค์„ ๋ถ™์ž…๋‹ˆ๋‹ค. ์ด ๋ผ๋ฒจ์€ ๋ˆ„๋ฅผ ์ˆ˜ ์—†๊ณ , ์„ ํƒ๋˜์ง€๋„ ์•Š๊ณ ,
5
5
  ์ง€์›Œ์ง€์ง€๋„ ์•Š์Šต๋‹ˆ๋‹ค.
6
6
 
7
- **์ผ๋ฐ˜ํ™”ํ•œ ๊ณ„์•ฝ.** ํ•„์ˆ˜ `label`๊ณผ ์„ ํƒ์  `tone`๋งŒ ๋ฐ›๋Š” ํ‘œํ˜„ ๊ณ„์•ฝ์ž…๋‹ˆ๋‹ค(Statistic์ฒ˜๋Ÿผ
7
+ **์ผ๋ฐ˜ํ™”ํ•œ ๊ณ„์•ฝ.** ํ•„์ˆ˜ `label`๊ณผ ์„ ํƒ์  `tone`ยท`variant`๋ฅผ ๋ฐ›๋Š” ํ‘œํ˜„ ๊ณ„์•ฝ์ž…๋‹ˆ๋‹ค(Statistic์ฒ˜๋Ÿผ
8
8
  ์ œํ’ˆ์ด ์™„์„ฑํ•œ ๋ฌธ์ž์—ด์„ ๋ฐ›์Šต๋‹ˆ๋‹ค). id, collection ๋ฉค๋ฒ„์‹ญ, ์„ ํƒ state๋Š” ์—†์Šต๋‹ˆ๋‹ค โ€” Tag๋Š”
9
9
  Collection ๊ธฐ๋ณธ ๊ณ„์•ฝ์˜ ๋Œ€์ƒ์ด ์•„๋‹ˆ๋ผ ๋‚ฑ๊ฐœ ํ‘œ์‹œ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค.
10
10
 
@@ -28,9 +28,10 @@ Notice/Badge์˜ ์—ญํ• ์ด๊ณ , Tag๋Š” ์‚ฌ์‹ค์„ ๋‚˜์—ดํ•  ๋ฟ ์œ„ํ˜‘ํ•˜์ง€ ์•Š์Šต
28
28
  - `closable`(์ง€์šธ ์ˆ˜ ์žˆ๋Š” ํƒœ๊ทธ)์€ ์ด ๊ณ„์•ฝ์— **์—†์Šต๋‹ˆ๋‹ค**. ํƒœ๊ทธ๋ฅผ ์ง€์šฐ๋Š” ๊ฒƒ์€ ์„ ํƒ ํ•ด์ œ์ด๊ณ ,
29
29
  ์„ ํƒ ํ•ด์ œ๋Š” Chip์˜ `selected` ์ถ•์ด ์ด๋ฏธ ์†Œ์œ ํ•œ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. Tag์— ๋ณ„๋„์˜ dismiss ์ถ•์„
30
30
  ์ถ”๊ฐ€ํ•˜๋ฉด ๊ฐ™์€ "์„ ํƒ ํ•ด์ œ"๋ฅผ ๋‘ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ ์ด๋ฆ„์œผ๋กœ ๊ณ„์•ฝํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
31
- - ๋ชจ์–‘๋„ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. `chipRecipe`์™€ `badgeRecipe`๋Š” `radius: "full"`(pill)์„ ์“ฐ์ง€๋งŒ
32
- `tagRecipe`๋Š” `radius: "sm"`์˜ ์‚ฌ๊ฐํ˜•์ž…๋‹ˆ๋‹ค. ์ •์  ๋ผ๋ฒจ์„ pill๋กœ ๊ทธ๋ฆฌ๋ฉด ๋ˆ„๋ฅผ ์ˆ˜ ์žˆ๋‹ค๋Š”
33
- ์•”๋ฌต์  ์‹ ํ˜ธ๋ฅผ ์ฃผ๋ฏ€๋กœ, ํ˜•ํƒœ ์ž์ฒด๋กœ๋„ ์ƒํ˜ธ์ž‘์šฉ ๊ฐ€๋Šฅ์„ฑ์„ ๋ถ€์ •ํ•ฉ๋‹ˆ๋‹ค.
31
+ - ๊ธฐ๋ณธ์€ `variant="outline"`์ธ ๋ฐฐ๊ฒฝ ์—†๋Š” capsule(`full`)์ž…๋‹ˆ๋‹ค.
32
+ ์ž‘์€ ์‚ฌ๊ฐํ˜•์€ ๋ช…์‹œ์ ์ธ `variant="default"`๋กœ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
33
+ 2026-10-29 ์„ ์ˆ˜ ํ”„๋กœํ•„ ์ •๋ณด ์‹œ์•ˆ ์Šน์ธ์— ๋”ฐ๋ผ ์„ ํƒ UI์™€ ๋ถ„๋ฆฌ๋œ ์ฝ๊ธฐ ์ „์šฉ ํ˜•ํƒœ๋กœ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
34
+ ํ˜•ํƒœ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๋ฒ„ํŠผ roleยท์„ ํƒ ์ƒํƒœยทํฌ์ปค์Šคยทํ™œ์„ฑํ™” ์ด๋ฒคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
34
35
  - ์•ผ์ž˜์•Œ์˜ `AppBadge`๊ฐ€ ์ด ์—ญํ• (ํฌ์ง€์…˜ยท๋“ฑ๊ธ‰ยท์‹œ์ฆŒ ๋ผ๋ฒจ)์„ ์ˆ˜ํ–‰ํ•œ ์ œํ’ˆ ๊ทผ๊ฑฐ์—์„œ Tag ๊ณ„์•ฝ์„
35
36
  ์ถ”์ถœํ–ˆ์Šต๋‹ˆ๋‹ค. Web/RN ๊ณต์‹ renderer๋Š” ์ด ๊ณ„์•ฝ์„ ์ง์ ‘ ์†Œ๋น„ํ•˜๋ฉฐ, ์ œํ’ˆ์˜ ๊ธฐ์กด ์ด๋ฆ„์„ Tag๋กœ
36
37
  ๋ฐ”๊พธ๋Š” ์ผ ์ž์ฒด๋Š” ์†Œ๋น„ ์•ฑ์˜ ํ›„์† migration์ž…๋‹ˆ๋‹ค.
@@ -42,7 +43,7 @@ keyboard, focus, accessibilityState ๊ณ„์•ฝ์ด ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค โ€” ์ ‘๊ทผ์„ฑ
42
43
  ๋…ธ๋“œ ํ•˜๋‚˜๋กœ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ Tag๋ฅผ ํ•œ ์ค„์— ๋‚˜์—ดํ•  ๋•Œ ๊ฐ„๊ฒฉ ์กฐํ•ฉ์€ ๋‚˜์ค‘์— Stack/Inline
43
44
  recipe๊ฐ€ ์•ˆ์ •ํ™”๋˜๋ฉด ๊ทธ์ชฝ์— ์œ„์ž„ํ•˜๊ณ , ์ด ๊ณ„์•ฝ์—๋Š” ๋„ฃ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
44
45
 
45
- ๊ธฐ๋ณธ ๋†’์ด๋Š” 20px์ด์ง€๋งŒ ๊ณ ์ • ๋†’์ด๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. Dynamic Type์—์„œ ํ•œ ์ค„ ๋ผ๋ฒจ์˜ ๊ณ ์œ  ๋†’์ด๋งŒํผ
46
+ ๊ธฐ๋ณธ ์ตœ์†Œ ๋†’์ด๋Š” 32px์ด๋ฉฐ ๊ณ ์ • ๋†’์ด๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. Dynamic Type์—์„œ ํ•œ ์ค„ ๋ผ๋ฒจ์˜ ๊ณ ์œ  ๋†’์ด๋งŒํผ
46
47
  ๋Š˜์–ด๋‚˜๋ฉฐ, renderer๋Š” ๋ผ๋ฒจ์„ ๊ณ ์ • ํ”„๋ ˆ์ž„ ์•ˆ์—์„œ ์ž๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
47
48
 
48
49
  ## ํ˜„์žฌ maturity์™€ ๋‚จ์€ ์ฆ๊ฑฐ
@@ -54,3 +55,11 @@ scenario matrix๊ฐ€ dark(๋ผ์ดํŠธ ์ „์šฉ ์ƒ‰ ๋ˆ„์ˆ˜ ์—†์Œ), 2๋ฐฐ ๊ธ€์ž, RTL,
54
55
 
55
56
  stable์€ ์ „์ฒด ํ™˜๊ฒฝ ์ธ์ฆ์„ ๋œปํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. screen reader ์‹ค์‚ฌ์šฉ๊ณผ ์‹ค์ œ device screenshot์€
56
57
  ์ œํ’ˆ ๊ธฐ๊ธฐ QA์˜ ๋ฒ”์œ„์ž…๋‹ˆ๋‹ค.
58
+
59
+ ## Outline ํ‘œํ˜„
60
+
61
+ `variant`๋Š” `default | outline`, ๊ธฐ๋ณธ๊ฐ’์€ `outline`์ž…๋‹ˆ๋‹ค.
62
+ 2026-10-29 ์‚ฌ์šฉ์ž๊ฐ€ ์Šน์ธํ•œ ํ…Œ๋‘๋ฆฌํ˜•์„ ๊ธฐ๋ณธ์œผ๋กœ ์ง€์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. Outline์€ ํˆฌ๋ช… ๋ฐฐ๊ฒฝยท1px ํ…Œ๋‘๋ฆฌยท
63
+ `radius.full`, ์ตœ์†Œ ๋†’์ด 32, ์ขŒ์šฐ 10ยท์ƒํ•˜ 6, caption regular(400)์ž…๋‹ˆ๋‹ค.
64
+ neutral ๊ธ€์ž๋Š” `content.primary`, ๋‹ค๋ฅธ tone์€ ๊ธฐ์กด ๊ธ€์žยทํ…Œ๋‘๋ฆฌ ์˜๋ฏธ๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
65
+ ํฐ ๋ฐฐ๊ฒฝ ์ „์šฉ ์ƒ‰์„ ํ•˜๋“œ์ฝ”๋”ฉํ•˜์ง€ ์•Š๊ณ  Provider์˜ light/dark ๋ฐ ์ œํ’ˆ ํŒ”๋ ˆํŠธ๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.
@@ -122,13 +122,15 @@ SegmentedControl์˜ `selectionTone="brand"`์™€ solid ํƒญ์€ ๋ธŒ๋žœ๋“œ ๊ฐ•์กฐ๋ฅผ
122
122
 
123
123
  | ์˜๋ฏธ | ์ƒ‰์ƒ ๊ธฐ์ค€ | ํ•ด๋‹น ๊ณต๊ฐœ ํ‘œ๋ฉด |
124
124
  | --- | --- | --- |
125
- | ์ง€์†๋˜๋Š” ์ผ๋ฐ˜ ์„ ํƒ ๋ฉด | `selection.background`/`selection.content`, ๊ฒฝ๊ณ„๊ฐ€ ํ•„์š”ํ•˜๋ฉด `selection.border` | Chipยท์นฉ ๊ทธ๋ฃน, neutral SegmentedControl, TimePicker, filled Tabs, capsule BottomNavigation, ReactionPicker, ToggleGroup, selected ButtonยทIconButton, ์„ ํƒ CardยทListRowยทSelectยทComboboxยทMenuยทTreeยทTransferListยทDataTable ํ–‰ |
125
+ | ์ง€์†๋˜๋Š” ์ผ๋ฐ˜ ์„ ํƒ ๋ฉด | `selection.background`/`selection.content`, ๊ฒฝ๊ณ„๊ฐ€ ํ•„์š”ํ•˜๋ฉด `selection.border` | Chipยท์นฉ ๊ทธ๋ฃน, neutral SegmentedControl, TimePicker, filled Tabs, capsule BottomNavigation, ReactionPicker, ToggleGroup, ์„ ํƒ CardยทListRowยทSelectยทComboboxยทMenuยทTreeยทTransferListยทDataTable ํ–‰ |
126
126
  | ํ˜„์žฌ ์กฐ์ž‘ ์œ„์น˜ | ๊ธฐ์กด focus/hover/pressed ์—ญํ• . ํ™•์ • ์„ ํƒ๊ณผ ํ•ฉ์น˜์ง€ ์•Š์Œ | CommandPaletteยทMentionsยทTagsInput ์ œ์•ˆ์˜ active descendant, SelectยทCombobox์˜ active ํ•ญ๋ชฉ, ํฌ์ปค์Šค ๋ง |
127
127
  | ์ง„ํ•œ ๋ธŒ๋žœ๋“œ ๋ฉด | `primary`/`onPrimary` | `Tabs indicator="solid"`, `SegmentedControl` ๊ธฐ๋ณธ `selectionTone="brand"`, `BottomNavigation selection="tile"`, prominent destination |
128
128
  | ์ž‘์€ ๊ฐ’ ํ‘œ์‹œยทํ˜„์žฌ ์œ„์น˜ ํ‘œ์‹œ | ๊ธฐ์กด ์ „์šฉ ์ฒดํฌยท์ ยท๋ฐ‘์ค„ยทํ…Œ๋‘๋ฆฌ ๊ณ„์•ฝ | CheckboxยทRadio์˜ glyph, Switch on, Calendar ๋‚ ์งœ ์›, underline/text Tabs, ์ผ๋ฐ˜ BottomNavigation ์•„์ด์ฝ˜ยท๊ธ€์ž, PaginationยทStepsยทAnchorยทCarousel ์œ„์น˜ ํ‘œ์‹œ, ์ฝ๊ธฐ ์ „์šฉ ๋ฐ˜์‘ ์š”์•ฝ |
129
129
  | ๋ธŒ๋žœ๋“œยท์žฅ์‹ยท์—…๋ฌด ๊ฐ•์กฐ | ๊ธฐ์กด ๋ธŒ๋žœ๋“œ/์ƒํƒœ ์—ญํ•  | ChatMessage outgoing, ํด๋” ๊ทธ๋ฆผ, liquid toast, ํ‘œ์˜ `highlighted` ์—…๋ฌด ๊ฐ•์กฐ, ๋ธŒ๋žœ๋“œ Surface/Badge, ์˜ค๋ฅ˜ยท์„ฑ๊ณต ๋“ฑ ์ƒํƒœ |
130
130
  | ๋ฏธ๋””์–ด ์œ„ ์„ ํƒ ์ฆ๊ฑฐ | ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ์ „์šฉ ๋งยท์Šคํฌ๋ฆผ ์œ ์ง€ | ImageChoiceยทMediaSelection. ํšŒ์ƒ‰ ๋ฉด์œผ๋กœ ์‚ฌ์ง„์„ ๋ฎ์ง€ ์•Š์Œ |
131
131
 
132
+ ํ–‰๋™ ํ† ๊ธ€์ธ ButtonยทIconButton๊ณผ ํ˜„์žฌ ๊ฒฝ๋กœ์ธ IconButtonLink๋Š” 2026-10-28 ์‚ฌ์šฉ์ž ์ง€์‹œ๋กœ ์ด ์„ ํƒ ๋ฉด์„ ์ ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ์„ ํƒ ์ „ํ›„ ์›๋ž˜ tone ๋ฐฐ๊ฒฝยทํ…Œ๋‘๋ฆฌ๋ฅผ ์œ ์ง€ํ•˜๋ฉฐ ์ „๊ฒฝ ํ‘œํ˜„ยท์ฑ„์šด glyph์™€ ์ ‘๊ทผ์„ฑ ์ƒํƒœ๋กœ ์„ ํƒ์„ ์•Œ๋ฆฐ๋‹ค. ์Šคํ•€ํŠธ์˜ ๊ฝƒ ๋ฐ˜์‘ ์ฃผ๋ณ€์— ์„ ํƒ ๋ฐฐ๊ฒฝ์ด ์ค‘๋ณต์œผ๋กœ ์ƒ๊ธด ์‚ฌ๋ก€์— ๋”ฐ๋ฅธ ๋ณ€๊ฒฝ์ด๋‹ค. ์„ ํƒ ์ปดํฌ๋„ŒํŠธ์˜ ์ „์šฉ ์ƒํƒœ ๊ณ„์•ฝ์€ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ ์ง€์นจ์ด ์†Œ์œ ํ•œ๋‹ค.
133
+
132
134
  ### ์ ์šฉ ๊ทœ์น™
133
135
 
134
136
  - ์„ ํƒ ํ† ํฐ์€ ํ˜„์žฌ theme๊ณผ product palette๋กœ ํ•ด์„ํ•œ๋‹ค. ์ œํ’ˆ๋ณ„ ์ง์ ‘ ์ƒ‰์ƒ ๋ถ„๊ธฐ๋Š” ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋Š”๋‹ค.
@@ -88,6 +88,7 @@ import { AuthScreenFooter, AuthScreenHero, AuthScreenLayout } from "@hjmds/react
88
88
  | `layoutStyle` | marginยทwidthยทflexยท`alignSelf` | โ€” | ๋ฐ”๊นฅ ๋ฐฐ์น˜ ์ „์šฉ(WebยทNative ๋ชจ๋‘) |
89
89
  | `initialFocus` | `first-input` ยท `none` | `first-input` | ํŽ˜์ด์ง€ ์ง„์ž… ์‹œ ์ฒซ ์œ ํšจ ์ž…๋ ฅ ๋˜๋Š” `initialFocusTarget` ์นธ์— ํ•œ ๋ฒˆ ํฌ์ปค์Šคํ•œ๋‹ค. ๊ธฐ๋ณธ๊ฐ’ยท์ง„์ž… ํ•œ์ • ์ •์ฑ… ๋ณ€๊ฒฝ์€ 1.33.0๋ถ€ํ„ฐ ์ œ๊ณต. ์ง„์ž… ์ข…๋ฃŒ ๋’ค ๋‹จ๊ณ„ ๋ณ€๊ฒฝยท์ž…๋ ฅ ํ‘œ์‹œ๋กœ ์ž๋™ ํฌ์ปค์Šค๋ฅผ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜์ง€ ์•Š์œผ๋ฉฐ ํ•˜๋‹จ ์‹œํŠธ ์•ˆ์—์„œ๋Š” ์ž๋™ ํฌ์ปค์Šคํ•˜์ง€ ์•Š๋Š”๋‹ค. [์ง„์ž… ํฌ์ปค์Šค](screen-layout.md#์ง„์ž…-ํฌ์ปค์Šค) |
90
90
  | Native `testID` | `string` | โ€” | ๋ณธ๋ฌธ `ScrollView` ์‹๋ณ„(๋จธ๋ฆฌ ๋ ๊ฐ€ ์žˆ์–ด๋„ ๊ฐ™์€ `ScrollView`) |
91
+ | Native `backdrop` | `ReactNode` | โ€” | ๋ฏธ๊ฒŒ์‹œ(checkout ๊ฐœ๋ฐœ). ์Šคํฌ๋กค ๋‚ด์šฉ ๋งจ ์œ„ ๋’ค์— ๊น”๋ฆฌ๋Š” ์žฅ์‹ ์žฅ๋ฉด(์ œํ’ˆ ํ•˜๋Š˜ ๋  ๋“ฑ). heroยทmainยทfooter์™€ **ํ•จ๊ป˜ ์Šคํฌ๋กค**ํ•˜๊ณ  ํ„ฐ์น˜ยท์ ‘๊ทผ์„ฑ์—์„œ ๋น ์ง„๋‹ค. ์ œํ’ˆ ๋…ธ๋“œ๋Š” ๋‚ด์šฉ ์œ„ ๋ ๊ธฐ์ค€ ์ ˆ๋Œ€ ์œ„์น˜๋กœ ๋†“์œผ๋ฉฐ ์ขŒ์šฐ ์—ฌ๋ฐฑ ๋ฐ–๊นŒ์ง€ ๋‹ฟ๋Š”๋‹ค. ํ™”๋ฉด ๋’ค์— ๊ณ ์ •ํ•œ ๊ทธ๋ฆผ ์œ„๋กœ ๊ธ€์ž๊ฐ€ ์ง€๋‚˜๊ฐ€์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค |
91
92
  | Native `safeAreaBottom` | 0 ์ด์ƒ์˜ ์œ ํ•œ์ˆ˜ | `0` | ํ™”๋ฉด์ด ๋‹ฟ๋Š” ์ฐฝ ์•„๋ž˜ ๊ฐ€์žฅ์ž๋ฆฌ์˜ safe area(`useSafeAreaInsets().bottom`, ์ด๋ฏธ ์•„๋ž˜๋ฅผ ๋“ค์ธ host ์•ˆ์ด๋ฉด 0). ์•„๋ž˜ ์—ฌ๋ฐฑ์ด `max(paddingBlock, inset)` โ€” `regular` max(40, inset), `compact` max(20, inset) โ€” ์œผ๋กœ ํ•œ ๋ฒˆ๋งŒ ๋“ค์–ด๊ฐ„๋‹ค(ScreenLayout๊ณผ ๊ฐ™์€ ๊ทœ์น™, ํ•ฉ์‚ฐ ์—†์Œ). ํ‚ค๋ณด๋“œ๊ฐ€ ์—ด๋ฆฌ๋ฉด paddingBlock๋งŒ. ์Œ์ˆ˜ยท๋ฌดํ•œ๋Œ€๋Š” `RangeError` |
92
93
  | `AuthScreenHero` `title` ยท `eyebrow` ยท `description` ยท `mark` ยท `headingLevel` | ๋ฌธ์ž์—ด ยท `ReactNode` ยท `1`~`3` | `headingLevel` 1 | ์ œ๋ชฉ์€ ํ•„์ˆ˜์ด๊ณ  ๋นˆ ๋ฌธ์ž์—ด์ด๋ฉด `TypeError`. ๋ฐ€๋„๋Š” ๋ฐ”๊นฅ `AuthScreenLayout`์„ ๋”ฐ๋ฅธ๋‹ค |
93
94
  | `AuthScreenFooter` `links` ยท `notice` ยท `children` | ๋งํฌ ๋ฐฐ์—ด(1๊ฐœ ์ด์ƒ) ยท `ReactNode` | โ€” | ๋นˆ ๋งํฌ ๋ชฉ๋กยท๋นˆ ๋ผ๋ฒจ์€ `TypeError`(LS-04: ์•ฝ๊ด€ ๋งํฌ๋Š” ๋กœ๊ทธ์ธ ์ „ ๊ฐ™์€ ํ™”๋ฉด์— ์žˆ์–ด์•ผ ํ•œ๋‹ค). `children`์€ ๋งํฌ ์•„๋ž˜ ์ œํ’ˆ ๋‚ด์šฉ |
@@ -156,6 +157,7 @@ import { AuthScreenFooter, AuthScreenHero, AuthScreenLayout } from "@hjmds/react
156
157
 
157
158
  - `title`ยท`exit`๋ฅผ ์“ฐ๋ฉด ๋ฐ”๊นฅ์— ์–น๋˜ TopBarยท์ œํ’ˆ ํ—ค๋”๋ฅผ ์ง€์šด๋‹ค. ๋‘˜ ๋‹ค ๋‘๋ฉด ๋จธ๋ฆฌ๊ฐ€ ๋‘ ์ค„์ด ๋œ๋‹ค. Native์—์„œ host TopBar๊ฐ€ ๋งก๋˜ ์œ„ safe area(`safeAreaTop`)๋Š” host์˜ SafeAreaView top์œผ๋กœ ์˜ฎ๊ธด๋‹ค.
158
159
 
160
+ - Native์—์„œ ํ•˜๋Š˜ ๋  ๊ฐ™์€ ์žฅ์‹์„ ๋ ˆ์ด์•„์›ƒ ๋ฐ”๊นฅ(ํ™”๋ฉด ๋’ค)์— ๊ณ ์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋‚ด์šฉ์ด ๋„˜์ณ ์Šคํฌ๋กค๋˜๋ฉด ๊ธ€์ž๊ฐ€ ๊ทธ๋ฆผ ์œ„๋กœ ์ง€๋‚˜๊ฐ€ ์ฝํžˆ์ง€ ์•Š๋Š”๋‹ค(2026-10-28 ๋‹ค์—๋ฆฌ ๊ฐ€์ž… ๋‹จ๊ณ„). `backdrop`์œผ๋กœ ๋„˜๊ธด๋‹ค.
159
161
  - Native์—์„œ ์•„๋ž˜ ํ™ˆ ์ธ๋””์ผ€์ดํ„ฐ๋ฅผ `SafeAreaView edges={['bottom']}`ยท`paddingBottom: insets.bottom` ๋ž˜ํผ๋กœ ๋“ค์ด์ง€ ๋ง๊ณ  `safeAreaBottom`์œผ๋กœ ๋„˜๊ธด๋‹ค. ๋‘˜์„ ๊ฐ™์ด ์ฃผ๋ฉด inset + paddingBlock์ด ๋‹ค์‹œ ๋”ํ•ด์ง„๋‹ค.
160
162
  - 1.28.0๊นŒ์ง€ Native๋Š” iOS `contentInsetAdjustmentBehavior="automatic"`์œผ๋กœ ์œ„ยท์•„๋ž˜ safe area๋ฅผ iOS์—์„œ๋งŒ ๋”ํ–ˆ๋‹ค. 1.28.1๋ถ€ํ„ฐ ์œ„๋Š” host, ์•„๋ž˜๋Š” `safeAreaBottom`์ด๋‹ค. ์œ„ safe area๋ฅผ ์•„๋ฌด๋„ ๋“ค์ด์ง€ ์•Š๋˜ ํ™”๋ฉด์€ host์—์„œ TopBar `safeAreaTop`์ด๋‚˜ SafeAreaView top์œผ๋กœ ๋“ค์ธ๋‹ค.
161
163
  - Native๋Š” ์ž์ฒด๋กœ ํ‚ค๋ณด๋“œ ์Šคํฌ๋กค ์—ฌ๋ฐฑ์„ ์ค€๋‹ค. ๋ถ€๋ชจ๊ฐ€ ๋‹ค์‹œ keyboard avoidance๋ฅผ ํ•˜๋ฉด ์—ฌ๋ฐฑ์ด ์ค‘๋ณต๋  ์ˆ˜ ์žˆ์œผ๋‹ˆ
@@ -180,6 +180,10 @@ Avatar๋ฅผ ์“ฐ์ง€ ๋ชปํ•œ ์ด์œ (์ด๋ฆ„ ํ•„์ˆ˜, Web ์ด๋ฏธ์ง€ ํ˜ธ์ŠคํŠธ ์—†์Œ, 4
180
180
  ์ผ๋ฐ˜ ๋ชฉ๋ก์€ `ListRow.leadingAction`, ์นด๋“œ ์ž‘์„ฑ์ž๋Š” `Card.leadingAction`, ๋Œ€ํ™” ๋ชฉ๋ก์€ `ConversationRow.avatarAction` + `renderBody`, ์ฐธ์—ฌ์ž๋Š” `ChatRoomMembers.memberProfile`์ด๋‹ค.
181
181
  ๊ฐ™์€ ์‚ฌ์ง„์— `Avatar.action`๊ณผ ์ƒ์œ„ `avatarAction`์„ ํ•จ๊ป˜ ๋„ฃ์ง€ ์•Š๋Š”๋‹ค. ๋ณธ๋ฌธ ๋งํฌ/๋ฒ„ํŠผ๊ณผ ๋…๋ฆฝ ํ”„๋กœํ•„ ๋ฒ„ํŠผ์€ ํ˜•์ œ๋‹ค.
182
182
 
183
+ ์‚ฌ์ง„ ์˜†์— ๋”ฐ๋กœ ๋ณด์ด๋Š” ํšŒ์› ๋‹‰๋„ค์ž„๋„ ๊ฐ™์€ ํ”„๋กœํ•„๋กœ ์ด๋™ํ•œ๋‹ค(2026-10-29 ์‚ฌ์šฉ์ž ์ง€์‹œ: Spint ๋Œ“๊ธ€์—์„œ ๋‹‰๋„ค์ž„์„ ๋ˆŒ๋Ÿฌ๋„ ๋ฐ˜์‘์ด ์—†์—ˆ๋‹ค).
184
+ ๋Œ“๊ธ€ `CommentThreadRow`๋Š” `avatarAction`์ด ์žˆ์œผ๋ฉด ์ž‘์„ฑ์ž ์ด๋ฆ„๋„ ๊ฐ™์€ ํ–‰๋™ยท์ ‘๊ทผ์„ฑ ์ด๋ฆ„์˜ ๊ธ€์ž ๋ฒ„ํŠผ์œผ๋กœ ๊ทธ๋ฆฐ๋‹ค(Web `hjm-identity-name-action`,
185
+ Native ์ค‘์ฒฉ `Text` press). ์ œํ’ˆ์€ ์ถ”๊ฐ€ prop ์—†์ด `avatarAction`๋งŒ ๊ณต๊ธ‰ํ•œ๋‹ค. ํ–‰ ์ „์ฒด๊ฐ€ ๋Œ€ํ™”๋ฐฉยท์•Œ๋ฆผ ๋ชฉ์ ์ง€์ธ ๊ธ€์ž ์•ˆ์˜ ์ด๋ฆ„์€ ๋ฒ„ํŠผ์„ ์ค‘์ฒฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
186
+
183
187
  ํšŒ์› ์‹๋ณ„์ž๊ฐ€ ์—†๋Š” ์žฅ์‹ยท๋ฐฉ ์‚ฌ์ง„์„ ํšŒ์› ํ”„๋กœํ•„๋กœ ์—ฐ๊ฒฐํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ช…์‹œ์ ์ธ ์‚ฌ์ง„ ํŽธ์ง‘ยท๊ณต์œ  ๋Œ€์ƒ ์„ ํƒ์€ ์›๋ž˜ ์ž‘์—…์„ ์œ ์ง€ํ•œ๋‹ค.
184
188
 
185
189
  ๋‚ด ํ”„๋กœํ•„ ํ™”๋ฉด์˜ ๋‚ด ์‚ฌ์ง„์€ ์˜ˆ์™ธ๋กœ ๊ทธ ์ž์ฒด๊ฐ€ ์‚ฌ์ง„ ๋ณ€๊ฒฝ ๋ฒ„ํŠผ์ด๋‹ค(2026-10-25 ์‚ฌ์šฉ์ž ์ง€์‹œ). `action={{label: t("photo.change"), onPress: openPhotoPicker}}`๋ฅผ ์—ฐ๊ฒฐํ•˜๊ณ  ์‚ฌ์ง„ ๊ณ ๋ฅด๊ธฐยท์ž๋ฅด๊ธฐยท์—…๋กœ๋“œ๋Š” ์ œํ’ˆ์ด ์†Œ์œ ํ•œ๋‹ค. ๊ฐ™์€ ํ™”๋ฉด์— ์ค‘๋ณต "์‚ฌ์ง„ ๋ฐ”๊พธ๊ธฐ" ๋ฒ„ํŠผ์„ ๋‘์ง€ ์•Š๋Š”๋‹ค.
@@ -192,3 +196,12 @@ Avatar๋ฅผ ์“ฐ์ง€ ๋ชปํ•œ ์ด์œ (์ด๋ฆ„ ํ•„์ˆ˜, Web ์ด๋ฏธ์ง€ ํ˜ธ์ŠคํŠธ ์—†์Œ, 4
192
196
  ์‚ฌ์šฉ์ž ์š”์ฒญ์œผ๋กœ WebยทNative ๊ธฐ๋ณธ๊ฐ’์€ `size="large"`(48), `shape="circle"`์ด๋‹ค. ํ”„๋กœํ•„๊ณผ ์ผ๋ฐ˜ ํšŒ์› ๋ชฉ๋ก์—๋„ 48์„ ์“ฐ๊ณ  ๋Œ“๊ธ€ยท์ฑ„ํŒ… ๋“ฑ ์ด˜์ด˜ํ•œ ๋ชฉ๋ก์˜ ๋ช…์‹œ์ ์ธ ์ž‘์€ ๋‹จ๊ณ„๋Š” ์œ ์ง€ํ•œ๋‹ค. ์ œํ’ˆ๋ณ„ ์‚ฌ์ง„ยท์ด๋‹ˆ์…œยท์ด๋ชจ์ง€ยท๋™๋ฌผ ๊ทธ๋ฆผ๋„ ๊ฐ™์€ ํšŒ์› ํ”„๋ ˆ์ž„์„ ๋”ฐ๋ฅธ๋‹ค. ์ฝ˜ํ…์ธ  ์ธ๋„ค์ผยท์•ฑ ์•„์ด์ฝ˜์˜ ๋ชจ์–‘์ด๋‚˜ ํฌ๊ธฐ์—๋Š” ์ ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
193
197
 
194
198
  ๊ธฐ๋ณธ `frame="outlined"`๋Š” `border.control` 50% alpha, 1px์˜ ์›ํ˜• ์œค๊ณฝ์ด๋‹ค. 2026-10-25 ๋ฒˆ๋š ์ด๋ชจ์ง€ ์•„๋ฐ”ํƒ€์—์„œ ๊ธฐ์กด ์„ ์€ ํ๋ฆฌ๊ณ  2px ๋ถˆํˆฌ๋ช… ์„ ์€ ์ง„ํ•˜๋‹ค๋Š” ํ”ผ๋“œ๋ฐฑ์„ ๊ฑฐ์ณ 1pxยท50%๋กœ ์กฐ์ •ํ–ˆ์œผ๋ฉฐ, ์‚ฌ์šฉ์ž๊ฐ€ ์ด ๊ฐ’์„ ๋””์ž์ธ ์‹œ์Šคํ…œ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ง€์ •ํ–ˆ๋‹ค. ์ง€๋ฆ„์€ ํ…Œ๋‘๋ฆฌ ํฌํ•จ์ด๊ณ  WebยทNative์— ๋™์ผํ•˜๋‹ค. ๋ฏธ๊ฒŒ์‹œ ๊ฐœ๋ฐœ ์ค‘ ์ถ”๊ฐ€ํ–ˆ๋˜ `strong` ์˜ต์…˜์€ ๊ธฐ๋ณธ๊ฐ’๊ณผ ํ•ฉ์ณ ์ œ๊ฑฐํ–ˆ๋‹ค. ์ œํ’ˆ์—์„œ ๋ณ„๋„ frame์„ ์ง€์ •ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
199
+
200
+ ### ๊ทธ๋ฆผ ์ „์ฒด ํ‘œ์‹œ (2026-10-28)
201
+
202
+ ์Šคํ•€ํŠธ์—์„œ ์ง์ ‘ ๊ทธ๋ฆฐ ์ •์‚ฌ๊ฐํ˜• ๊ทธ๋ฆผ์˜ ๋ชจ์„œ๋ฆฌ๊ฐ€ ์›ํ˜• ํ”„๋กœํ•„์—์„œ ์ž˜๋ฆฐ ์‚ฌ๋ก€๋กœ `imageFit="contain"`์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค. ๊ธฐ๋ณธ `cover`๋Š” ๊ธฐ์กด ์‚ฌ์ง„ ์ฑ„์›€์„ ์œ ์ง€ํ•œ๋‹ค. `contain`์€ ์‚ฌ์ง„ยท๊ทธ๋ฆผ ์ „์ฒด๋ฅผ ํ”„๋ ˆ์ž„ ์•ˆ์— ๋„ฃ๊ณ  ์—ฌ๋ฐฑ์„ ๋‚จ๊ธด๋‹ค. ๊ณตํ†ต recipe์˜ `1 / โˆš2` ๋น„์œจ๋กœ ์ •์‚ฌ๊ฐํ˜• ๋Œ€๊ฐ์„ ๊นŒ์ง€ ์› ์•ˆ์— ๋“ค์–ด์˜ค๋ฉฐ ์ด๋ฏธ์ง€์˜ ๋ชจ์„œ๋ฆฌ๋ฅผ ๋‹ค์‹œ ๋‘ฅ๊ธ€๊ฒŒ ์ž๋ฅด์ง€ ์•Š๋Š”๋‹ค. WebยทNative์™€ ๊ธฐ์กด ์ €์žฅ ์ด๋ฏธ์ง€์— ๋™์ผํ•˜๊ฒŒ ์ ์šฉํ•œ๋‹ค. Native `renderImage`์˜ `size`์—๋Š” ์‹ค์ œ ์ด๋ฏธ์ง€ ์˜์—ญ ํฌ๊ธฐ๊ฐ€ ์ „๋‹ฌ๋œ๋‹ค.
203
+
204
+ **ํšŒ์› ํ”„๋กœํ•„ ์‚ฌ์ง„์€ ์›์„ ๊ฝ‰ ์ฑ„์šด๋‹ค(`cover`, ๊ธฐ๋ณธ๊ฐ’) โ€” 2026-10-29 ์‚ฌ์šฉ์ž ์ง€์‹œ.** `contain`์€ ์ž‘์€ ์› ์•ˆ์— ๋„ค๋ชจ๋‚œ ์‚ฌ์ง„์ด ์ž‘๊ฒŒ ๋“ค์–ด๊ฐ€๊ณ 
205
+ ๋นˆ ํ…Œ๋‘๋ฆฌ๊ฐ€ ๋‚จ์•„ ์‚ฌ์ง„์ฒ˜๋Ÿผ ๋ณด์ด์ง€ ์•Š์•˜๋‹ค(Spint). ์‚ฌ์ง„ ์ „์ฒด๋Š” ํ”„๋กœํ•„์˜ "์‚ฌ์ง„ ํฌ๊ฒŒ ๋ณด๊ธฐ"(`ProfileSummary.avatarView` + `ImageViewer`)์—์„œ
206
+ ๋ณธ๋‹ค. ํšŒ์› ์•„๋ฐ”ํƒ€ยท๋Œ“๊ธ€ยท์ฑ„ํŒ…ยท์•Œ๋ฆผยท๋ชฉ๋ก์˜ ์‚ฌ์ง„์— `contain`์„ ์“ฐ์ง€ ์•Š๋Š”๋‹ค. `contain`์€ ์ž˜๋ฆฌ๋ฉด ๋œป์ด ๋ฐ”๋€Œ๋Š” ๋น„์‚ฌ์ง„ ๊ทธ๋ฆผ(๋กœ๊ณ ยท์Šคํ‹ฐ์ปคยท๋„๊ฐ
207
+ ์ผ๋Ÿฌ์ŠคํŠธ)์—๋งŒ ์“ฐ๊ณ  ๊ทธ ์ด์œ ๋ฅผ ์‚ฌ์šฉ์ฒ˜ ์ฃผ์„์— ๋‚จ๊ธด๋‹ค.
@@ -131,7 +131,7 @@ WebยทNative์™€ ๋ผ์ดํŠธยท๋‹คํฌ ํ…Œ๋งˆ์— ๋™์ผํ•˜๊ฒŒ ์ ์šฉํ•œ๋‹ค.
131
131
  Native์—์„œ `style`ยท`labelStyle`์„ ๋„˜๊ธฐ๋ฉด ์‹คํ–‰ ์ค‘ `TypeError`๊ฐ€ ๋‚œ๋‹ค.
132
132
  - ์ƒ‰์€ tone๊ณผ ์ œํ’ˆ ํ…Œ๋งˆ ํ† ํฐ์œผ๋กœ ๋ฐ”๊พผ๋‹ค. ๋ฒ„ํŠผ๋งˆ๋‹ค ๋ธŒ๋žœ๋“œ ์ƒ‰์„ ํ•˜๋“œ์ฝ”๋”ฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
133
133
  - ์ง„ํ–‰ ์ค‘ ์ƒํƒœ๋Š” `loading`์œผ๋กœ ํ‘œ์‹œํ•˜๊ณ  ๊ฐ™์€ ์ž๋ฆฌ์— ๋ณ„๋„ Spinner๋ฅผ ๊ฒน์น˜์ง€ ์•Š๋Š”๋‹ค. children์„ ๋นˆ ๋ฌธ์ž์—ด์ด๋‚˜ ๋‹ค๋ฅธ ๊ธธ์ด์˜ ์ง„ํ–‰ ๋ฌธ๊ตฌ๋กœ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค. ๋ Œ๋”๋Ÿฌ๊ฐ€ ๋ฌธ๊ตฌยท์•„์ด์ฝ˜์„ ์ˆจ๊ธด ์ž๋ฆฌ์— ์ค‘์•™ ์Šคํ”ผ๋„ˆ๋งŒ ํ‘œ์‹œํ•˜๊ณ  ๊ธฐ์กด ํฌ๊ธฐยท์ ‘๊ทผ์„ฑ ์ด๋ฆ„์„ ์œ ์ง€ํ•œ๋‹ค.
134
- - "ํ•œ ํ™”๋ฉด `primary` ํ•˜๋‚˜"์˜ ์˜ˆ์™ธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. `selected`๋ฅผ ์ค€ ๋ฒ„ํŠผ์€ tone๊ณผ ๊ด€๊ณ„์—†์ด `buttonRecipe.states.selected`(๋ฐฐ๊ฒฝ `bg`, ๊ธ€์ž `contentBrand`, ์„ ํƒ ํ…Œ๋‘๋ฆฌ ์—†์Œ)๋กœ ์น ํ•ด์ ธ primary ์ฑ„์›€์ด ์•„๋‹ˆ๋ฏ€๋กœ ์„ธ์ง€ ์•Š๊ณ (Web `.hjm-button[data-selected="true"]`, Native `internal/recipe-button.tsx`, ์ž‘ํ’ˆ ์นด๋“œ์˜ ์ €์žฅ ํ† ๊ธ€), SheetยทDialog ์•ˆ์˜ ํ–‰๋™์€ ๊ทธ ํ‘œ๋ฉด ์•ˆ์—์„œ primary ํ•˜๋‚˜๋ฅผ ์„ผ๋‹ค([Sheet](sheet.md) `footer`, ์ž‘ํ’ˆ ํƒ์ƒ‰ยท๋žœ๋”ฉ ์Šคํ† ๋ฆฌ์˜ ์‹œํŠธ).
134
+ - `selected`๋„ ์›๋ž˜ tone์˜ ๋ฐฐ๊ฒฝ์„ ์œ ์ง€ํ•˜๋ฏ€๋กœ `primary` ๋ฒ„ํŠผ์ด๋ฉด ์ฃผ ํ–‰๋™ ๊ฐœ์ˆ˜์— ํฌํ•จํ•œ๋‹ค. SheetยทDialog ์•ˆ์˜ ํ–‰๋™์€ ๊ทธ ํ‘œ๋ฉด ์•ˆ์—์„œ primary ํ•˜๋‚˜๋ฅผ ์„ผ๋‹ค([Sheet](sheet.md) `footer`).
135
135
 
136
136
  - `selected` ๋ฒ„ํŠผ ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ๋‚˜๋ž€ํžˆ ๋‘์–ด ํ•˜๋‚˜ ๊ณ ๋ฅด๊ธฐยท์—ฌ๋Ÿฌ ๊ฐœ ๊ณ ๋ฅด๊ธฐยท์ผœ๊ณ  ๋„๊ธฐ๋ฅผ ํ‰๋‚ด ๋‚ด์ง€ ์•Š๋Š”๋‹ค. ๊ทธ ์˜๋„๋Š” [์„ ํƒ ์กฐ๋ฆฝ ๊ทœ์น™](../SELECTION.md#์กฐ๋ฆฝ-๊ทœ์น™ํ•„์ˆ˜)์˜ SegmentedControlยทRadioGroupยทChipยทSwitch๋ฅผ ์“ด๋‹ค.
137
137
 
@@ -150,8 +150,7 @@ WebยทNative์™€ ๋ผ์ดํŠธยท๋‹คํฌ ํ…Œ๋งˆ์— ๋™์ผํ•˜๊ฒŒ ์ ์šฉํ•œ๋‹ค.
150
150
 
151
151
  - footer ์ˆœ์„œ๋ฅผ ๋ชจ๋“  ๋ณธ๋ฌธ ํ–‰๋™์— ๊ฐ•์ œํ•˜์ง€ ์•Š๋Š”๋‹ค. ์‹œ๊ฐ„ ์„ ํƒ์ฒ˜๋Ÿผ ์ฃผ ํ–‰๋™ ๋‹ค์Œ์— ์ดˆ๊ธฐํ™”๊ฐ€ ์˜ค๋Š” ๊ตฌ์„ฑ๊ณผ AlertDialog์˜ ์ข์€ ํญ ์ˆœ์„œ๋Š” ๊ฐ๊ฐ ๋ช…์‹œ๋œ ๊ตฌ์„ฑ ๊ณ„์•ฝ์„ ๋”ฐ๋ฅธ๋‹ค.
152
152
 
153
- `selected`์—๋Š” ์„ ํƒ ํ…Œ๋‘๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋Š”๋‹ค. `selected` ์ ‘๊ทผ์„ฑ ์ƒํƒœ์™€
154
- contentBrand ๊ธ€์žยท์•„์ด์ฝ˜ ์ƒ‰์€ ์œ ์ง€ํ•˜๋ฉฐ ํ‚ค๋ณด๋“œ focus outline์€ ์„ ํƒ๊ณผ ๋ณ„๋„๋กœ ์œ ์ง€ํ•œ๋‹ค.
153
+ 2026-10-28 ์‚ฌ์šฉ์ž ์ง€์‹œ: `selected`๋กœ ๋ฐฐ๊ฒฝ์ƒ‰์„ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค. ์›๋ž˜ tone์˜ ๋ฐฐ๊ฒฝยทํ…Œ๋‘๋ฆฌ๋ฅผ ์œ ์ง€ํ•˜๊ณ  ์„ ํƒ ํ…Œ๋‘๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋Š”๋‹ค. ghost๋Š” ํˆฌ๋ช…ํ•˜๊ฒŒ ๋‚จ์œผ๋ฉฐ primaryยทdanger์˜ ์ฑ„์›€๊ณผ ์—ญ์ƒ ๊ธ€์ž์ƒ‰๋„ ์œ ์ง€ํ•œ๋‹ค. ์„ ํƒ์€ ์ „๊ฒฝ์ƒ‰ยท์ฑ„์šด ์•„์ด์ฝ˜ ๋˜๋Š” ์ƒํƒœ ๋ผ๋ฒจ๋กœ ํ‘œํ˜„ํ•˜๋ฉฐ `aria-pressed`/Native ์ ‘๊ทผ์„ฑ ์ƒํƒœ์™€ ํ‚ค๋ณด๋“œ focus outline์€ ์œ ์ง€ํ•œ๋‹ค. ๊ฝƒ ๋ฐ˜์‘์— ์„ ํƒ ๋ฉด์ด ์ค‘๋ณต์œผ๋กœ ์ƒ๊ธด ์‚ฌ๋ก€ ๋•Œ๋ฌธ์— ๊ธฐ์กด ์„ ํƒ ๋ฐฐ๊ฒฝ ์ง€์นจ์„ ๋Œ€์ฒดํ–ˆ๋‹ค. IconButton๋„ ๊ฐ™์€ ๊ทœ์น™์„ ์“ด๋‹ค.
155
154
 
156
155
  ## ์ €์žฅ ํ™•์ • ๋ผ๋ฒจ๊ณผ ํ”„๋ ˆ์ž„ ์˜ˆ์•ฝ (npm 1.21.0 ยท 2026-10-15)
157
156
 
@@ -237,3 +236,5 @@ import { ButtonLink } from "@hjmds/react/actions";
237
236
  2026-10-20 ๋‹ค์—๋ฆฌ์˜ ์ง€๋„/์žฅ์†Œ ์ด๋™์—์„œ ์ดˆ์•ˆ ๋ณดํ˜ธยท์˜คํ”„๋ผ์ธ ์ฐจ๋‹จ์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€ํ–ˆ๋‹ค.
238
237
  ์ „๋‹ฌ๋ฐ›์€ anchor props์™€ `ref`๋ฅผ ์‹ค์ œ ๋งํฌ์— ๊ทธ๋Œ€๋กœ ์—ฐ๊ฒฐํ•˜๋ฉฐ, `href`๊ฐ€ ์—†์œผ๋ฉด ๋‚ด๋ถ€ ๋ผ์šฐํŒ…๋„ ํ•˜์ง€ ์•Š๋Š”๋‹ค.
239
238
  ๋ฒ„ํŠผ์˜ ์ƒ‰/ํฌ๊ธฐ/์ดˆ์ /๋น„ํ™œ์„ฑยท์ˆ˜์ •ํ‚ค/์ƒˆ ํƒญ ์˜๋ฏธ๋Š” HJM์ด ์†Œ์œ ํ•˜๊ณ  ์ œํ’ˆ์€ ๋ชฉ์ ์ง€ยท์ด๋™ ์ฒ˜๋ฆฌ๋ฅผ ์—ฐ๊ฒฐํ•œ๋‹ค.
239
+
240
+ ์„ ํƒ recipe ์ง์ ‘ ์†Œ๋น„์˜ ๋ณ€๊ฒฝ: `states.selected`๋Š” ์ „๊ฒฝ๋งŒ ์†Œ์œ ํ•˜๋ฉฐ ๋ฐฐ๊ฒฝยทํ…Œ๋‘๋ฆฌ๋Š” `tones[tone]`์—์„œ ์ฝ๋Š”๋‹ค. ํ•„๋“œ ์ œ๊ฑฐ๋Š” ๋‹ค์Œ contracts major ๊ฒŒ์‹œ ๋Œ€์ƒ์œผ๋กœ ๊ธฐ๋กํ•˜๋ฉฐ ์ปดํฌ๋„ŒํŠธ์˜ `selected` ์‚ฌ์šฉ๋ฒ•์€ ๋™์ผํ•˜๋‹ค.
@@ -116,6 +116,7 @@ import { BarChart, Sparkline } from "@hjmds/react-native/chart";
116
116
  | `focusIndex` | `number \| null` | โ€” | ์ œ์–ด ํฌ์ปค์Šค |
117
117
  | `defaultFocusIndex` | `number \| null` | ๋งˆ์ง€๋ง‰ ๊ด€์ธก index | ๋น„์ œ์–ด ์‹œ์ž‘ ํฌ์ปค์Šค |
118
118
  | `onFocusChange` | `(index: number \| null, detail: { scrubbing: boolean }) => void` | โ€” | ์Šคํฌ๋Ÿฝ ์ค‘ `scrubbing: true`, ์†์„ ๋–ผ๋ฉด `false` |
119
+ | `showFocusLabel` | `boolean` | `false` | LineChartยทBarChart์—์„œ ๋ˆ„๋ฆ„์„ ๋†“์€ ๋’ค์—๋„ ์„ ํƒํ•œ ์ ์˜ ๋งํ’์„  ์œ ์ง€ (2026-10-29 ๊ธฐ๋ก ์ฐจํŠธ ์š”์ฒญ) |
119
120
  | `cursorLabel` | `(index: number) => string` | `categories[index]` | ์Šคํฌ๋Ÿฝ ์ค‘ ์ปค์„œ ์œ„ ๋งํ’์„  |
120
121
 
121
122
  ### BarChart
@@ -65,10 +65,12 @@ import { CommentThreadRow } from "@hjmds/react-native/screen-flows";
65
65
  | `id`ยท`parentId`ยท`author`ยท`body`ยท`timeLabel`ยท`likeCountLabel`ยท`likeIcon`ยท`likeLabel` | ํ•„์ˆ˜ | `parentId`๊ฐ€ `null`์ด๋ฉด ์ตœ์ƒ์œ„ ๋Œ“๊ธ€ |
66
66
  | `bodyText` | ์—†์Œ | ์ฃผ๋ฉด `author`์™€ ๋ณธ๋ฌธ์„ ํ•œ ๋ฌธ์žฅ์œผ๋กœ, ์‹œ๊ฐ์€ ์•„๋ž˜ ํ–‰๋™ ์ค„๋กœ ์˜ฎ๊ธด๋‹ค |
67
67
  | `timePlacement` | `"auto"` | ๋ฏธ๊ฒŒ์‹œ. `"auto"`๋Š” `bodyText`๊ฐ€ ์—†์œผ๋ฉด ์‹œ๊ฐ์„ ์ž‘์„ฑ์ž ์˜†์— ๋‘”๋‹ค. `"actions"`๋Š” ๊ธ€์ด ์•„๋‹Œ ๋ณธ๋ฌธ(๊ฝƒ ๋„์žฅยท์นด๋“œ)์—์„œ๋„ ์‹œ๊ฐ์„ ๋ณธ๋ฌธ ์•„๋ž˜ ๋ฐ˜์‘ ์š”์•ฝยท๋‹ต๊ธ€๊ณผ ๊ฐ™์€ ์ค„ ๋งจ ์•ž์œผ๋กœ ์˜ฎ๊ธด๋‹ค |
68
+ | `textActions` | ์—†์Œ | ๋ฏธ๊ฒŒ์‹œ. ๋‹ต๊ธ€ ์ค„์˜ ๋‹ต๊ธ€ ๋’ค์— ๋ถ™๋Š” ์ œํ’ˆ ๋ฌธ๊ตฌ ํ–‰๋™ `{ id, label, onPress, disabled? }[]`. ๋‹ต๊ธ€๊ณผ ๊ฐ™์€ muted caption ๋ฌธ๊ตฌ ๋ฒ„ํŠผ์ด๋ฉฐ ์ˆœ์„œ๋Œ€๋กœ ๋†“์ธ๋‹ค(์˜ˆ: ๋ฒˆ์—ญ ๋Œ“๊ธ€์˜ "์›๋ณธ ๋ณด๊ธฐ"/"๋ฒˆ์—ญ ๋ณด๊ธฐ"). ๋ฌธ๊ตฌยท์ƒํƒœ๋Š” ์ œํ’ˆ์ด ์†Œ์œ ํ•œ๋‹ค |
68
69
  | `panel` | ์—†์Œ | ๋ฏธ๊ฒŒ์‹œ. ๋Œ“๊ธ€๋ณ„ ์ธ๋ผ์ธ ํŒจ๋„(์ˆ˜์ •ยท์‹ ๊ณ ยท์ฐจ๋‹จ ์–‘์‹). ๋‹ต๊ธ€ ์ค„ ์•„๋ž˜, ๊ธธ๊ฒŒ ๋ˆ„๋ฅด๊ธฐ ๋Œ€์ƒ ๋ฐ–์— `panelGap` 8 ๊ฐ„๊ฒฉ์œผ๋กœ ๋†“์ธ๋‹ค. ์—ด๋ฆผ ์ƒํƒœยท์ดˆ์ ยท์ œ์ถœยท๋‹ซ๊ธฐ๋Š” ์ œํ’ˆ์ด ์†Œ์œ ํ•œ๋‹ค |
69
70
  | `mediaAlbum` | ์—†์Œ | ๋ฏธ๊ฒŒ์‹œ. ํ•œ ์žฅ์”ฉ ๋„˜๊ธฐ๋Š” ์‚ฌ์ง„ ๋ฌถ์Œ `{ count, renderPage(index), pageLabel(index, count), previousLabel, nextLabel, aspectRatio?, onPress?(index), index?ยทonIndexChange?ยทdefaultIndex? }`. `renderPage`๋Š” ํ™”๋ฉด์— ๋ณด์ด๋Š” ์žฅ์—๋งŒ ํ˜ธ์ถœ๋˜์–ด ์ œํ’ˆ์ด ๊ทธ ์‚ฌ์ง„๋งŒ ์กฐํšŒยท๋””์ฝ”๋”ฉํ•œ๋‹ค. ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘ยท์‹คํŒจ ์žฅ์€ `MediaAlbumPageStatus`(`@hjmds/react(-native)/screens`, [ChatMessage](chat-message.md#์ถ•๊ณผ-๊ธฐ๋ณธ๊ฐ’))๋กœ ๊ทธ๋ ค ์•ˆ์ชฝ ๋‚ด์šฉ์ด ๋‚ญ๋…๊ธฐ์— ๋‹ฟ๊ฒŒ ํ•œ๋‹ค. `media`์™€ ํ•จ๊ป˜ ์ฃผ๋ฉด `TypeError`. ๋ณธ๋ฌธ ์•„๋ž˜, ๊ธธ๊ฒŒ ๋ˆ„๋ฅด๊ธฐ ๋Œ€์ƒ ๋ฐ–์— ๋†“์ธ๋‹ค |
70
71
  | `authorBadge` | ์—†์Œ | ๋ฏธ๊ฒŒ์‹œ. ๋‹‰๋„ค์ž„ ๋ฐ”๋กœ ๋’ค, ๊ฐ™์€ ์ค„์— ๋†“๋Š” ์ œํ’ˆ ๋ฐฐ์ง€. ๊ฐ„๊ฒฉ `spacing.xxs` 4, ์ข์€ ํญ์—์„œ๋Š” ์ค„๋ฐ”๊ฟˆํ•œ๋‹ค. `bodyText` ๋ชจ๋“œ์—์„œ๋„ ์ž‘์„ฑ์žยท๋ฐฐ์ง€ยท๋ณธ๋ฌธ ์ˆœ์„œ๋กœ ๋ฐฐ์น˜ํ•œ๋‹ค. ๋ฐฐ์ง€ ๋ฌธ๊ตฌยท๋…ธ์ถœ ์กฐ๊ฑด์€ ์ œํ’ˆ์ด ์†Œ์œ ํ•œ๋‹ค |
71
72
  | `avatar` | ์—†์Œ | ์ œํ’ˆ ์•„๋ฐ”ํƒ€. ์ตœ์ƒ์œ„ 32ยท๋‹ต๊ธ€ 24 ํญ ์นธ์— ๋‘”๋‹ค |
73
+ | `avatarAction` | ์—†์Œ | ํšŒ์› ํ”„๋กœํ•„ ์ด๋™ `{ label, onPress, disabled? }`. ์‚ฌ์ง„๊ณผ **์ž‘์„ฑ์ž ์ด๋ฆ„**์ด ๊ฐ™์€ ํ–‰๋™์„ ์“ด๋‹ค(์ด๋ฆ„์€ 2026-10-29๋ถ€ํ„ฐ, [Avatar ์ง€์นจ](avatar.md#ํšŒ์›-ํ”„๋กœํ•„-์ด๋™-2026-10-25)) |
72
74
  | `canReply` | `parentId === null` | ๋‹ต๊ธ€ ํ–‰๋™ ํ‘œ์‹œ ์—ฌ๋ถ€ |
73
75
  | `replyDisabled` | false | ๋‹ต๊ธ€ ํ–‰๋™ ๋น„ํ™œ์„ฑ |
74
76
  | `likeAction`ยท`actions` | ์—†์Œ | ๊ธฐ๋ณธ ์ข‹์•„์š” ๋ฒ„ํŠผ ๋Œ€์ฒดยท์˜†์— ์ถ”๊ฐ€ |
@@ -92,7 +94,7 @@ import { CommentThreadRow } from "@hjmds/react-native/screen-flows";
92
94
 
93
95
  - ๊ฐ€์ƒํ™” host์—์„œ ํ–‰ ์‚ฌ์ด ๊ฐ„๊ฒฉ(`commentThreadRecipe.rowGap`)๊ณผ ๋‹ต๊ธ€ ๋“ค์—ฌ์“ฐ๊ธฐ(`replyIndent`)๋ฅผ ์ง์ ‘ ์ค„ ๋•Œ๋„ recipe ๊ฐ’์„ ์“ด๋‹ค. ์ œํ’ˆ ์ˆซ์ž๋กœ ๋‹ค์‹œ ์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค.
94
96
  - ๋‹ต๊ธ€ ํŽผ์น˜๊ธฐยท์ •๋ ฌยท๊ถŒํ•œยท์ข‹์•„์š” ์ €์žฅ์€ ์ œํ’ˆ์ด ์†Œ์œ ํ•œ๋‹ค. ํ–‰์€ ์ฝœ๋ฐฑ๋งŒ ํ˜ธ์ถœํ•œ๋‹ค.
95
- - ํ–‰ ์•ˆ์— ๋‘ ๋ฒˆ์งธ ๋ ˆ์ด์•„์›ƒ์„ ๋งŒ๋“ค์ง€ ๋ง๊ณ  `actions`ยท`likeAction`ยท`media`ยท`mediaAlbum`ยท`panel` ์Šฌ๋กฏ์œผ๋กœ ์ถ”๊ฐ€ํ•œ๋‹ค. ์‹œ๊ฐยท๋‹ต๊ธ€ ์ค„์„ ๋ณธ๋ฌธ ๋…ธ๋“œ ์•ˆ์— ๋‹ค์‹œ ๊ทธ๋ฆฌ์ง€ ๋ง๊ณ  `timePlacement: "actions"`์™€ `canReply`๋ฅผ ์“ด๋‹ค.
97
+ - ํ–‰ ์•ˆ์— ๋‘ ๋ฒˆ์งธ ๋ ˆ์ด์•„์›ƒ์„ ๋งŒ๋“ค์ง€ ๋ง๊ณ  `actions`ยท`likeAction`ยท`media`ยท`mediaAlbum`ยท`panel` ์Šฌ๋กฏ์œผ๋กœ ์ถ”๊ฐ€ํ•œ๋‹ค. ์‹œ๊ฐยท๋‹ต๊ธ€ ์ค„์„ ๋ณธ๋ฌธ ๋…ธ๋“œ ์•ˆ์— ๋‹ค์‹œ ๊ทธ๋ฆฌ์ง€ ๋ง๊ณ  `timePlacement: "actions"`์™€ `canReply`๋ฅผ ์“ด๋‹ค. ๋‹ต๊ธ€ ์˜† ๋ฌธ๊ตฌ ํ–‰๋™๋„ ๋ณธ๋ฌธ์— ๋ฒ„ํŠผ์„ ๋‘์ง€ ๋ง๊ณ  `textActions`๋กœ ์ค€๋‹ค.
96
98
  - `mediaAlbum`์˜ ์‚ฌ์ง„ ์กฐํšŒยท๊ถŒํ•œ ํ™•์ธยท์บ์‹œ ์ˆ˜๋ช…์€ ์ œํ’ˆ์ด `renderPage` ์•ˆ์—์„œ ์†Œ์œ ํ•œ๋‹ค. HJM์€ ๋ณด์ด๋Š” ์žฅ๋งŒ ๋งˆ์šดํŠธํ•˜๊ณ  ๋„˜๊น€ ์ƒํƒœ๋ฅผ ํ–‰ ์•ˆ์— ๋‘”๋‹ค(์ œ์–ดํ˜•์€ `index`+`onIndexChange`).
97
99
 
98
100
  ## ์ƒํƒœ์™€ ์ƒํ˜ธ์ž‘์šฉ
@@ -106,6 +108,7 @@ import { CommentThreadRow } from "@hjmds/react-native/screen-flows";
106
108
  - 2026-10-20 Spint์˜ โ€œ๋‚˜โ€ ๋ฐฐ์ง€๊ฐ€ ๋ณธ๋ฌธ ์ฒซ ์ค„์„ ์ฐจ์ง€ํ•˜๋˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด `authorBadge` ์Šฌ๋กฏ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค. ์ž‘์„ฑ์ž์— ๋ถ™๋Š” ํ‘œ์‹œ๋Š” HJM์ด ๋‹‰๋„ค์ž„ ์˜†์— ๋ฐฐ์น˜ํ•˜๊ณ  ์ œํ’ˆ์€ ๋ฐฐ์ง€ ๋‚ด์šฉ๋งŒ ๊ณต๊ธ‰ํ•œ๋‹ค. ๊ฐœ๋ฐœ ์†Œ์Šค ๋ณ€๊ฒฝ์ด๋ฉฐ ๋ฏธ๊ฒŒ์‹œ๋‹ค.
107
109
 
108
110
  - 2026-10-20 Spint ๋Œ“๊ธ€์ด ์ด˜์ด˜ํ•˜๋‹ค๋Š” ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ์— ๋”ฐ๋ผ ๊ณตํ†ต ํ–‰ ์‚ฌ์ด ๊ฐ„๊ฒฉ์„ 12โ†’20, ๋‚ด๋ถ€ ์ค„ ์‚ฌ์ด ๊ฐ„๊ฒฉ์„ 2โ†’4๋กœ ์กฐ์ •ํ–ˆ๋‹ค. WebยทNative๊ฐ€ ๊ฐ™์€ recipe๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๊ฐœ๋ฐœ ์†Œ์Šค ๋ณ€๊ฒฝ์ด๋ฉฐ ๋ฏธ๊ฒŒ์‹œ๋‹ค.
111
+ - `textActions`: 2026-10-29 Spint ๋Œ“๊ธ€ ๋ฒˆ์—ญ. ์‚ฌ์šฉ์ž๊ฐ€ Instagram์ฒ˜๋Ÿผ "๋‹ต๊ธ€ ๋‹ฌ๊ธฐ" ์˜†์— "์›๋ณธ ๋ณด๊ธฐ"/"๋ฒˆ์—ญ ๋ณด๊ธฐ"๋ฅผ ๋‘๋ผ๊ณ  ์š”์ฒญํ–ˆ๋Š”๋ฐ, ๋‹ต๊ธ€ ์ค„์— ์ œํ’ˆ ํ–‰๋™ ์ž๋ฆฌ๊ฐ€ ์—†์–ด ๋ณธ๋ฌธ ์•„๋ž˜ ๋ณ„๋„ ์ค„๋กœ ๊ทธ๋ฆฌ๊ณ  ์žˆ์—ˆ๋‹ค.
109
112
  - `timePlacement`ยท`panel`ยท`mediaAlbum`: 2026-10-18 HJM 1.30.0 ์ฑ„ํƒ ๊ฐ์‚ฌ. Spint๋Š” ๊ฝƒ ๋„์žฅ ๋ณธ๋ฌธ์—์„œ ์‹œ๊ฐ์„ ๋‹ต๊ธ€ ์ค„์— ๋‘๋ ค๊ณ  ์‹œ๊ฐยท๋‹ต๊ธ€ ํ–‰์„ ๋ณธ๋ฌธ ์•ˆ์— ๋‹ค์‹œ ๊ทธ๋ ธ๊ณ , Utilverse๋Š” ์ˆ˜์ •ยท์‹ ๊ณ ยท์ฐจ๋‹จ ์–‘์‹์„ ๋ณธ๋ฌธ ๋…ธ๋“œ ์•ˆ(๊ธธ๊ฒŒ ๋ˆ„๋ฅด๊ธฐ ๋Œ€์ƒ ์•ˆ)์— ๋„ฃ์—ˆ์œผ๋ฉฐ, ์‚ฌ์ง„ ๋ฌถ์Œ์€ ์„ ํƒํ•œ ์žฅ๋งŒ ์กฐํšŒํ•˜๋Š” ์ œํ’ˆ ์•จ๋ฒ”์„ ๋”ฐ๋กœ ๋งŒ๋“ค์—ˆ๋‹ค.
110
113
 
111
114
  ## ํ”Œ๋žซํผ ์ฐจ์ด
@@ -4,7 +4,7 @@
4
4
  - ์ƒํƒœ: ๋ฐฐํฌ
5
5
  - ์ง€์›: Web ยท Native
6
6
  - ์ ์šฉ: 1.12.1
7
- - ๊ฒ€ํ† ์ผ: 2026-10-25
7
+ - ๊ฒ€ํ† ์ผ: 2026-10-28
8
8
  - ๊ทผ๊ฑฐ: [DateRange](../../date-range.md), ๊ฒฉ์ž๋Š” [Calendar](../../calendar.md), ๊ทœ์น™ ํ•จ์ˆ˜ `src/date-range.ts`
9
9
  - ์Šคํ† ๋ฆฌ๋ถ: `๋ฐฐํฌ/์ปดํฌ๋„ŒํŠธ/์ž…๋ ฅ/๊ธฐ๊ฐ„ ์„ ํƒ`
10
10
 
@@ -20,7 +20,7 @@
20
20
  | --- | --- |
21
21
  | ๋‚ ์งœ ํ•˜๋‚˜ | [DatePicker](date-picker.md) |
22
22
  | ๋‹ฌ๋ ฅ์„ ๋ณด์—ฌ ์ฃผ๊ธฐ๋งŒ ํ•˜๊ฑฐ๋‚˜ ๋‚ ์งœ ํ•˜๋‚˜๋ฅผ ์ธ๋ผ์ธ์œผ๋กœ ๊ณ ๋ฆ„ | [Calendar](calendar.md) |
23
- | ์ ‘ํžŒ ํ•„๋“œ์—์„œ ๋ˆŒ๋Ÿฌ ์—ฌ๋Š” ๊ตฌ๊ฐ„ ์„ ํƒ | ์—†์Œ. ํ•„์š”ํ•˜๋ฉด [Sheet](sheet.md)ยท[Popover](popover.md) ์•ˆ์— ์ œํ’ˆ์ด ํ•ฉ์„ฑํ•œ๋‹ค |
23
+ | ์˜ค๋ฒ„๋ ˆ์ด์—์„œ ๊ณ ๋ฅด๊ณ  ์ ์šฉ ๋ฒ„ํŠผ์œผ๋กœ ํ™•์ • | [Sheet](sheet.md) ์•ˆ์— ํ•ฉ์„ฑํ•˜๊ณ  `footer`์— `BottomCTA`๋ฅผ ๋‘”๋‹ค. ๋ณธ๋ฌธ ์•ˆ์—์„œ ์ ‘์–ด ๋‘๊ธฐ๋งŒ ํ•˜๋ฉด `collapsible`์„ ์“ด๋‹ค |
24
24
 
25
25
  ## ๊ณต๊ฐœ ์ด๋ฆ„๊ณผ import
26
26
 
@@ -80,7 +80,8 @@ import { DateRangePicker } from "@hjmds/react-native/date-range";
80
80
  | `previousMonth` ยท `nextMonth` | `{ month: string, label: string }` | โ€” | ์—†์œผ๋ฉด ์ด๋™ ๋ฒ„ํŠผ์ด ์—†๋‹ค |
81
81
  | `renderCellContent` | `(date: string) => ReactNode` | โ€” | ๋‚ ์งœ ์•„๋ž˜ ๋ณด์กฐ ํ‘œ์‹œ. ๋„˜๊ธธ ๋•Œ๋งŒ ํ–‰์— ๋ณด์กฐ ์ค„์ด ์ƒ๊ธด๋‹ค |
82
82
  | `summary` | `{ startLabel, endLabel, placeholder, formatDate(date) }` | โ€” | ๊ฒฉ์ž ์œ„ ์‹œ์ž‘์ผยท์ข…๋ฃŒ์ผ ์นธ. ์ง€๊ธˆ ๊ณ ๋ฅด๋Š” ์ชฝ(`resolveDateRangeActiveEdge`: ๋น„์—ˆ์œผ๋ฉด ์‹œ์ž‘, ๊ณ ๋ฅด๋Š” ์ค‘์ด๋ฉด ๋, ์™„์„ฑ๋˜๋ฉด ์—†์Œ)์„ ํ…Œ๋‘๋ฆฌ๋กœ ํ‘œ์‹œํ•œ๋‹ค. ๋ฌธ๊ตฌ๋Š” i18n, ๋‚ ์งœ ํ˜•์‹์€ ์ œํ’ˆ ์†Œ์œ  |
83
- | `presets` | `{ id, label, length: { days } \| { months } }[]` | โ€” | ์š”์•ฝ ์•„๋ž˜ ๋‹จ์ผ ์„ ํƒ Chip ์ค„(์ตœ๊ทผ 7์ผยท1๊ฐœ์›” ๋“ฑ). ๋ชจ๋‘ `grid.todayDate`๋กœ ๋๋‚˜๊ณ (`resolveDateRangePreset`), ๊ฐ’์ด ๊ทธ ๊ตฌ๊ฐ„๊ณผ ๊ฐ™์„ ๋•Œ๋งŒ ์„ ํƒ์œผ๋กœ ๋ณด์ธ๋‹ค. ๋ˆ„๋ฅด๋ฉด ๋ ๋‹ฌ๋กœ `onFocusedMonthChange(month, "jump")`๋ฅผ ์š”์ฒญํ•œ๋‹ค |
83
+ | `presets` | `{ id, label, length: { days } \| { months } \| { all: true } }[]` | โ€” | ์š”์•ฝ ์•„๋ž˜ ๋‹จ์ผ ์„ ํƒ Chip ์ค„. `days`ยท`months`๋Š” `grid.todayDate`๋กœ ๋๋‚˜๊ณ  `all`์€ ๋นˆ ๊ตฌ๊ฐ„(๋‚ ์งœ ์ œํ•œ ์—†์Œ)์ด๋‹ค(`resolveDateRangePreset`). ๊ฐ’์ด ๊ทธ ๊ตฌ๊ฐ„๊ณผ ๊ฐ™์„ ๋•Œ๋งŒ ์„ ํƒ์œผ๋กœ ๋ณด์ธ๋‹ค. ๋ˆ„๋ฅด๋ฉด ๋ ๋‹ฌ๋กœ `onFocusedMonthChange(month, "jump")`๋ฅผ ์š”์ฒญํ•œ๋‹ค(`all` ์ œ์™ธ). ๊ธฐ๊ฐ„ ํ•„ํ„ฐ์˜ ํ‘œ์ค€ ์นฉ์€ `dateRangeRecipe.presets.standard`(1์ผยท1์ฃผ์ผยท1๊ฐœ์›”ยท3๊ฐœ์›”ยท์ „์ฒด)์ด๊ณ  ๋ฌธ๊ตฌ๋Š” ์ œํ’ˆ i18n์ด๋‹ค |
84
+ | `collapsible` | `{ expanded?, defaultExpanded?, onExpandedChange? }` | ์—†์Œ(๋Š˜ ํŽผ์นจ) | ์ฃผ๋ฉด ๊ฒฉ์ž๋งŒ `summary` ๋’ค๋กœ ์ ‘๊ณ  `presets`๋Š” ๊ณ„์† ๋ณด์ธ๋‹ค. `summary`๊ฐ€ ํ•„์ˆ˜์ด๊ณ (์—†์œผ๋ฉด `TypeError`) ๊ทธ ์ค„์ด ํŽผ์นจ ๋ฒ„ํŠผ(`aria-expanded`ยทNative `expanded` ์ƒํƒœ, ๋์— ์•„๋ž˜/์œ„ ํ™”์‚ดํ‘œ)์ด ๋œ๋‹ค. ์ ‘ํžŒ ์ฑ„ ์‹œ์ž‘ํ•˜๊ณ (`defaultExpanded` ๊ธฐ๋ณธ `false`), ํ”„๋ฆฌ์…‹์„ ๋ˆ„๋ฅด๊ฑฐ๋‚˜ ๋‚ ์งœ ํƒญ์œผ๋กœ ๊ตฌ๊ฐ„์ด ์™„์„ฑ๋˜๋ฉด ๋‹ค์‹œ ์ ‘๋Š”๋‹ค(`resolveDateRangeExpandedAfterSelection`) |
84
85
  | Web `layoutStyle` | ๋ฐฐ์น˜ ์ „์šฉ style | โ€” | ๋ฃจํŠธ ๋ฐฐ์น˜ |
85
86
 
86
87
  ## ๋ฐฐ์น˜
@@ -98,6 +99,7 @@ import { DateRangePicker } from "@hjmds/react-native/date-range";
98
99
  | ์ƒ‰ | ๋  `primary` 15% tint(๋‘ ๋๊ณผ ๊ฐ™์€ ๋ธŒ๋žœ๋“œ์ƒ‰. ์ฒ˜์Œ ์“ด `surfaceSelected`๋Š” ๊ธฐ๋ณธ ํŒ”๋ ˆํŠธ์—์„œ #f0f1f3 ํšŒ์ƒ‰์ด๋ผ ๊ตฌ๊ฐ„์ด ๊ฑฐ์˜ ๋ณด์ด์ง€ ์•Š์•˜๋‹ค), ๋‘ ๋ Calendar ์„ ํƒ ์›(`action.brand`). ์นธ ๋ฐ”ํƒ• `surface.sunken`, ๋‹ค์Œ์— ๊ณ ๋ฅผ ์นธ ํ…Œ๋‘๋ฆฌ 2 `action.brand`, ๋นˆ ๊ฐ’ `content.tertiary` | `dateRangeRecipe` |
99
100
  | ์ˆœ์„œยท์ •๋ ฌ | ์˜ค๋ฒ„๋ ˆ์ด ์—†์ด ๋ณธ๋ฌธ(ํผยทํ•„ํ„ฐ ์˜์—ญยท[Section](section.md)) ์•ˆ์— ๊ฒฉ์ž๋ฅผ ํŽผ์ณ ๋‘”๋‹ค. ์œ„์—์„œ `summary`(์‹œ์ž‘์ผ ยท ์ข…๋ฃŒ์ผ) โ†’ `presets` ์นฉ ์ค„ โ†’ ์›” ๋จธ๋ฆฌ(์ด์ „ ยท ์›” ์ด๋ฆ„ ยท ๋‹ค์Œ) โ†’ ์š”์ผ โ†’ ๋‚ ์งœ ๊ฒฉ์ž. ์ œ์ถœ ํ–‰๋™์€ ๊ฒฉ์ž **์•„๋ž˜**์— ๋‘”๋‹ค. ์‹œํŠธ์—์„œ๋Š” `footer`์— `BottomCTA` [๋ณด์กฐ(์ „์ฒด ๋‚ ์งœ ๋“ฑ)][์ฃผ(์ ์šฉ)] ํ•œ ์ค„๋กœ ๋‘๊ณ  ์‹œํŠธ ๋†’์ด๋Š” `auto`๋กœ ๋งž์ถ˜๋‹ค. `summary`๋ฅผ ์“ฐ๋ฉด ๋ณ„๋„ ์•ˆ๋‚ดยท๊ฒฐ๊ณผ ๋ฌธ์žฅ์„ ๋‘์ง€ ์•Š๋Š”๋‹ค. ์ œ์ถœ ๋ฒ„ํŠผ์€ `isCompleteDateRange(value)`๊ฐ€ ์ฐธ์ผ ๋•Œ๋งŒ ํ™œ์„ฑ์ด๋‹ค | ๊ฐ™์€ ํŒŒ์ผ |
100
101
  | ๊ณ ์ •ยท์Šคํฌ๋กค | ๋ณธ๋ฌธ๊ณผ ํ•จ๊ป˜ ์Šคํฌ๋กคํ•œ๋‹ค(์˜ค๋ฒ„๋ ˆ์ด ์—†์Œ) | โ€” |
102
+ | ์ ‘๊ธฐ | `collapsible`์ด๋ฉด ์š”์•ฝ ๋‘ ์นธ ๋’ค์— ํ™”์‚ดํ‘œ ์—ด(`dateRangeRecipe.toggle`: ์•„์ด์ฝ˜ `glyph.sm` 20, ์นธ๊ณผ ๊ฐ„๊ฒฉ `spacing.xs` 8)์ด ๋ถ™๊ณ  ์š”์•ฝ ์ค„ ์ „์ฒด๊ฐ€ ๋ฒ„ํŠผ์ด๋‹ค. ์ ‘ํžˆ๋ฉด ์š”์•ฝ๊ณผ ํ”„๋ฆฌ์…‹ ์นฉ ์ค„๋งŒ ๋‚จ๊ณ  ๊ฒฉ์ž๋Š” ๊ทธ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. ๊ฒ€์ƒ‰ ํ•„๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ ์•„๋ž˜์— ์š”์•ฝ โ†’ ์นฉ โ†’ (ํŽผ์ณค์„ ๋•Œ) ๊ฒฉ์ž ์ˆœ์„œ๋กœ ๋‘”๋‹ค | `dateRangeRecipe.toggle`, `.hjm-date-range__summary[data-toggle]` |
101
103
  | ์ข์€ ํญยทํฐ ๊ธ€์ž | Web ๊ฒฉ์ž ์ตœ์†Œ ํญ 308๋ณด๋‹ค ์ข์€ ์ปจํ…Œ์ด๋„ˆ์— ๋‘์ง€ ์•Š๋Š”๋‹ค | `packages/react/src/styles.css` `.hjm-calendar__grid` |
102
104
 
103
105
  ## ๊ผญ ์ง€ํ‚ฌ ๊ฒƒ
@@ -107,6 +109,10 @@ import { DateRangePicker } from "@hjmds/react-native/date-range";
107
109
  - `rangeLabels`์™€ ์…€ ์ด๋ฆ„(`composeAccessibleName`)์€ i18n ํ‚ค๋กœ ๋งŒ๋“ ๋‹ค. ๊ตฌ๊ฐ„ ์œ„์น˜ ์ ‘๋ฏธ์‚ฌ๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€
108
110
  ์…€ ์ด๋ฆ„ ๋’ค์— `, `๋กœ ๋ถ™์ธ๋‹ค.
109
111
  - ๋‹ฌ์„ ๋„˜๊ธฐ๋ฉด ์ œํ’ˆ์ด ์ƒˆ ๋‹ฌ์˜ `cells`์™€ `monthLabel`์„ ๋‹ค์‹œ ๋งŒ๋“ค์–ด ๋„˜๊ธด๋‹ค.
112
+ - ํ•„ํ„ฐ์ฒ˜๋Ÿผ ๊ณ ๋ฅธ ๊ธฐ๊ฐ„์„ ๋ฐ”๋กœ ๋ฐ˜์˜ํ•˜๋Š” ์ž๋ฆฌ์—์„œ๋Š” `collapsible`๊ณผ ํ‘œ์ค€ ํ”„๋ฆฌ์…‹(1์ผยท1์ฃผ์ผยท1๊ฐœ์›”ยท3๊ฐœ์›”ยท์ „์ฒด)์„ ์“ฐ๊ณ ,
113
+ ๋นˆ ๊ฐ’(์ „์ฒด)์„ ๊ธฐ๋ณธ์œผ๋กœ ๋‘”๋‹ค. [์ „์ฒดยท์˜ค๋Š˜ยท๊ธฐ๊ฐ„ ์ง€์ •] ๊ฐ™์€ ๋ณ„๋„ ํƒญ์„ ์œ„์— ๊ฒน์ณ ๋‘์ง€ ์•Š๋Š”๋‹ค. ํ”„๋ฆฌ์…‹๊ณผ ๊ฐ™์€ ์„ ํƒ์„ ๋‘ ๋ฒˆ ํ•œ๋‹ค.
114
+ 2026-10-28 ์‚ฌ์šฉ์ž ์š”์ฒญ: ๋‹ค์—๋ฆฌ ๋งŒ๋‚œ ๋™๋ฌผ ํ•„ํ„ฐ์—์„œ ๊ธฐ๊ฐ„์„ ๊ณ ๋ฅธ ๋’ค์—๋„ ๋‹ฌ๋ ฅ์ด ๋Š˜ ํŽผ์ณ์ ธ ๋ชฉ๋ก์„ ๋ฐ€์–ด๋ƒˆ๊ณ , ํƒญ๊ณผ ํ”„๋ฆฌ์…‹์ด ๊ฒน์ณค๋‹ค.
115
+ - ์ ์šฉ ๋ฒ„ํŠผ์ด ์žˆ๋Š” ์‹œํŠธ์—์„œ๋Š” `all` ์นฉ ๋Œ€์‹  `BottomCTA` ๋ณด์กฐ ํ–‰๋™(์ „์ฒด ๋‚ ์งœ)์„ ๋‘”๋‹ค. ์ ์šฉ ๋ฒ„ํŠผ์€ ์™„์„ฑ๋œ ๊ตฌ๊ฐ„์—์„œ๋งŒ ์ผœ์ง„๋‹ค.
110
116
 
111
117
  ## ํ”Œ๋žซํผ ์ฐจ์ด
112
118
 
@@ -80,14 +80,13 @@ import { NotificationBell } from "@hjmds/react-native/notification-bell";
80
80
  ### ์•„์ด์ฝ˜ ๋งํฌ โ€” `IconButtonLink` (2026-10-23, ๊ฐœ๋ฐœ ์†Œ์Šคยท๋ฏธ๊ฒŒ์‹œ)
81
81
 
82
82
  Button์— ๋Œ€ํ•œ ButtonLink์ฒ˜๋Ÿผ IconButton ํ‹€(`tone`ยท`size`ยท`shape`ยท`edge`ยท`hitSlop`ยท`layoutStyle`)์„ ์ด๋™ ๋งํฌ์— ์“ด๋‹ค.
83
- ํ† ๊ธ€ยท๋กœ๋”ฉยท๋ณ€๊ฒฝ ํ–‰๋™์€ IconButton์— ๋‚จ๋Š”๋‹ค. `current`๋Š” ์ง€๊ธˆ ๋ณด๊ณ  ์žˆ๋Š” ํ™”๋ฉด์˜ ์ง„์ž…์ ์ด๋ฉฐ ์„ ํƒ๋œ IconButton๊ณผ ๊ฐ™์€ ๋ฉด
84
- (`iconButtonRecipe.states.selected`)์œผ๋กœ ์น ํ•˜๊ณ  Web `aria-current="page"`, Native `accessibilityState.selected`๋กœ ์•Œ๋ฆฐ๋‹ค.
83
+ ํ† ๊ธ€ยท๋กœ๋”ฉยท๋ณ€๊ฒฝ ํ–‰๋™์€ IconButton์— ๋‚จ๋Š”๋‹ค. `current`๋Š” ์ง€๊ธˆ ๋ณด๊ณ  ์žˆ๋Š” ํ™”๋ฉด์˜ ์ง„์ž…์ ์ด๋ฉฐ ์„ ํƒ๋œ IconButton๊ณผ ๊ฐ™์ด ๊ธฐ์กด tone์˜ ๋ฐฐ๊ฒฝ์„ ์œ ์ง€ํ•˜๊ณ  Web `aria-current="page"`, Native `accessibilityState.selected`๋กœ ์•Œ๋ฆฐ๋‹ค.
85
84
 
86
85
  | prop | Web | Native | ๊ธฐ๋ณธ๊ฐ’ |
87
86
  | --- | --- | --- | --- |
88
87
  | ๋ชฉ์ ์ง€ | `href: string` | `destination: LinkDestination` | ํ•„์ˆ˜ |
89
88
  | ์ด๋ฆ„ | `label: string`(์ ‘๊ทผ์„ฑ ์ด๋ฆ„, ๋นˆ ๊ฐ’์ด๋ฉด `TypeError`) | ๊ฐ™์Œ | ํ•„์ˆ˜ |
90
- | `current` | `boolean` โ†’ `aria-current="page"` + ์„ ํƒ ๋ฉด | `boolean` โ†’ `selected` + ์„ ํƒ ๋ฉด | `false` |
89
+ | `current` | `boolean` โ†’ `aria-current="page"` + ๊ธฐ์กด ๋ฐฐ๊ฒฝ ์œ ์ง€ | `boolean` โ†’ `selected` + ๊ธฐ์กด ๋ฐฐ๊ฒฝ ์œ ์ง€ | `false` |
91
90
  | `disabled` | `href`๋ฅผ ์ง€์šฐ๊ณ  ํด๋ฆญยท๊ฐ€์šด๋ฐ ํด๋ฆญ์„ ๋ง‰๋Š”๋‹ค(ButtonLink์™€ ๊ฐ™๋‹ค) | ๋ˆ„๋ฆ„์„ ๋ง‰๋Š”๋‹ค | `false` |
92
91
  | router ์—ฐ๊ฒฐ | `renderAnchor(props)` โ€” `ref`ยท`href`ยท์ด๋ฆ„ยท`aria-current`ยท์ฐจ๋‹จ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋‹ด๊ธด props๋ฅผ ์ž๊ธฐ ๋งํฌ์— ํŽผ์นœ๋‹ค | `onNavigate(destination)` ๋˜๋Š” `renderLink(props)`(TopBarAction `intent="link"`์™€ ๊ฐ™๋‹ค). ๋‘˜ ๋‹ค ์—†์œผ๋ฉด `TypeError` | โ€” |
93
92
 
@@ -124,7 +123,7 @@ Client Component์—์„œ ์—ฐ๊ฒฐํ•œ๋‹ค.
124
123
  | `size` | `small` 36 ยท `medium` 44 ยท `large` 52 | `medium` | `small`์€ ํ„ฐ์น˜ ์—ฌ๋ฐฑ 4 ์ถ”๊ฐ€. ์•„์ด์ฝ˜ ํฌ๊ธฐ๋Š” ๊ฐ๊ฐ `sm`ยท`md`ยท`lg` |
125
124
  | `shape` | `rounded` ยท `circle` | `rounded` | โ€” |
126
125
  | `elevation` | `none` ยท `raised` ยท `floating` ยท `overlay` | `none` | ๋–  ์žˆ๋Š” ์ปจํŠธ๋กค(๋ฒˆ๋š Dock ๊ฐ€์šด๋ฐ ๋ฒ„ํŠผ)์˜ ๊ทธ๋ฆผ์ž ํ† ํฐ ๋‹จ๊ณ„(`iconButtonRecipe.elevation`). `primary`ยท`secondary`ยท`danger`๋งŒ ๋ฐ›๊ณ  `ghost`์— ์ฃผ๋ฉด `TypeError`. Web์€ `--hjm-shadow-<๋‹จ๊ณ„>`, Native๋Š” iOS ๊ทธ๋ฆผ์ž + Android elevation. `IconButtonLink`์—๋Š” ์—†๋‹ค(๋ฏธ๊ฒŒ์‹œ, 2026-10-23) |
127
- | `selected` | `true` ยท `false` | โ€” | ์ฃผ๋ฉด ํ† ๊ธ€์ด ๋œ๋‹ค(Web `aria-pressed`, Native ์ ‘๊ทผ์„ฑ state) |
126
+ | `selected` | `true` ยท `false` | โ€” | ์ฃผ๋ฉด ํ† ๊ธ€์ด ๋œ๋‹ค(Web `aria-pressed`, Native ์ ‘๊ทผ์„ฑ state). ์„ ํƒ ์ „ํ›„ tone ๋ฐฐ๊ฒฝ์€ ์œ ์ง€ํ•œ๋‹ค |
128
127
  | `edge` | `start` ยท `end` ยท `both` | โ€” | ๋ฒ„ํŠผ์ด ๋†“์ธ ์ปจํ…Œ์ด๋„ˆ ๊ฐ€์žฅ์ž๋ฆฌ(๋…ผ๋ฆฌ ๋ฐฉํ–ฅ, RTL์—์„œ ๋’ค์ง‘ํž˜). `ghost`๋ฉด ํ„ฐ์น˜ ์˜์—ญ์€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ  ๊ทธ ์ชฝ์œผ๋กœ (๋ณด์ด๋Š” ์ง€๋ฆ„ โˆ’ ์•„์ด์ฝ˜ ์นธ) / 2๋งŒํผ ๋‚ด๋ฏผ๋‹ค(`medium` 10, `small` 8, `large` 12, `resolveIconButtonEdgeOverhang`). ํ…Œ๋‘๋ฆฌยท๋ฐฐ๊ฒฝ์ด ์žˆ๋Š” tone์€ ํ‹€์ด ์ •๋ ฌ ๊ธฐ์ค€์ด๋ผ ์›€์ง์ด์ง€ ์•Š๋Š”๋‹ค |
129
128
  | `loading` | `true` ยท `false` | โ€” | ๋ˆ„๋ฆ„์„ ๋ง‰๊ณ  ์Šคํ”ผ๋„ˆ๋กœ ๋ฐ”๊พผ๋‹ค. Native๋Š” ๊ธฐ๋ณธ์œผ๋กœ ํฌ์ปค์Šค๋ฅผ ์œ ์ง€ํ•œ๋‹ค(`disableWhileLoading`์œผ๋กœ๋งŒ ์˜› ๋™์ž‘) |
130
129
  | `loading` ์ค‘ `renderLoadingIndicator`(Native) | `(props: { color: string; size: "small" }) => ReactNode` | ๊ธฐ๋ณธ ์Šคํ”ผ๋„ˆ | ์Šคํ”ผ๋„ˆ๋ฅผ ๋ฐ”๊ฟ€ ๋•Œ๋งŒ |
@@ -187,8 +186,7 @@ TopBar
187
186
  `tone="inverse"`๋ฅผ ์ค€๋‹ค. ๋‹ค๋ฅธ ์•„์ด์ฝ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง์ ‘ ๋„ฃ์œผ๋ฉด(Native) ์ƒ‰์„ ์ œํ’ˆ์ด ๋„˜๊ฒจ์•ผ ํ•œ๋‹ค.
188
187
  - Native `IconButton`์˜ `style`์€ ์ œ๊ฑฐ๋˜์—ˆ๋‹ค. ๋ฐฐ์น˜๋Š” `layoutStyle`, ์™ธํ˜•์€ `tone`ยท`size`ยท`shape`๋กœ ์ง€์ •ํ•œ๋‹ค.
189
188
 
190
- 2026-10-10 ์‚ฌ์šฉ์ž ์š”์ฒญ: Button๊ณผ ๊ฐ™์€ ์„ ํƒ ํ‘œํ˜„์„ ์“ด๋‹ค. ์„ ํƒ์€ bg ๋ฐ”ํƒ•ยทcontentBrand ์•„์ด์ฝ˜์œผ๋กœ
191
- ํ‘œ์‹œํ•˜๊ณ  ์ถ”๊ฐ€ ์„ ํƒ ํ…Œ๋‘๋ฆฌ๋Š” ์—†๋‹ค. aria-pressed/Native selected์™€ ํ‚ค๋ณด๋“œ focus outline์€ ์œ ์ง€ํ•œ๋‹ค.
189
+ 2026-10-28 ์‚ฌ์šฉ์ž ์ง€์‹œ: **์„ ํƒํ•ด๋„ ๋ฐฐ๊ฒฝ์ƒ‰์€ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค.** ButtonยทIconButtonยทํ˜„์žฌ ๊ฒฝ๋กœ์ธ IconButtonLink๋Š” ์›๋ž˜ tone์˜ ๋ฐฐ๊ฒฝ๊ณผ ํ…Œ๋‘๋ฆฌ๋ฅผ ์œ ์ง€ํ•œ๋‹ค. ghost๋Š” ์„ ํƒ ํ›„์—๋„ ํˆฌ๋ช…ํ•˜๋ฉฐ primaryยทdanger๋Š” ์›๋ž˜ ์ฑ„์›€๊ณผ ๋Œ€๋น„๋˜๋Š” ๊ธ€์ž์ƒ‰์„ ์œ ์ง€ํ•œ๋‹ค. ์„ ํƒ์€ ๊ธ€์žยท์•„์ด์ฝ˜์˜ ์ „๊ฒฝ ํ‘œํ˜„ ๋˜๋Š” ์ฑ„์šด glyph๋กœ ํ‘œ์‹œํ•œ๋‹ค. Web `aria-pressed`/`aria-current`, Native `accessibilityState.selected`์™€ ํ‚ค๋ณด๋“œ focus outline์„ ์œ ์ง€ํ•œ๋‹ค. ์Šคํ•€ํŠธ ๊ฝƒ ๋ฐ˜์‘ ์ฃผ๋ณ€์— ์„ ํƒ ๋ฉด์ด ์ค‘๋ณต์œผ๋กœ ์ƒ๊ธด ์‚ฌ๋ก€๊ฐ€ ๊ณ„๊ธฐ์ด๋ฉฐ 2026-10-10์˜ ์„ ํƒ ๋ฐฐ๊ฒฝ ์ง€์นจ์„ ๋Œ€์ฒดํ•œ๋‹ค. ์ œํ’ˆ๋ณ„ ์ ‘๊ทผ์„ฑ ์ƒํƒœ ์šฐํšŒ๋‚˜ ๋ฐฐ๊ฒฝ ๋ฎ์–ด์“ฐ๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  `selected`๋ฅผ ๊ทธ๋Œ€๋กœ ์“ด๋‹ค.
192
190
 
193
191
  ## ๊ทผ๊ฑฐ
194
192
 
@@ -199,3 +197,5 @@ TopBar
199
197
  ์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค ์˜คํ”„์…‹์„ ๋”ฐ๋กœ ๋‘์ง€ ์•Š๊ณ  ์ด ํ•œ ๊ทœ์น™๊ณผ ๊ณ„์•ฝ ๊ฐ’์œผ๋กœ ๋งž์ท„๋‹ค. Toast ๋‹ซ๊ธฐ(๊ฐ™์€ ๋‚  `toastRecipe.close.inlineOverhang`)์™€ ๊ฐ™์€ ์‹์ด๋‹ค.
200
198
  - ๋„๊ตฌ์„ฑ ํ–‰๋™์€ IconButton: 2026-10-17 ์‚ฌ์šฉ์ž ์ง€์‹œ โ€” '๋‚˜์˜ ๊ธฐ๋ก' ํ—ค๋”์˜ ์ƒˆ๋กœ๊ณ ์นจยท์ƒˆ ๊ธฐ๋ก ๊ธ€์ž ๋ฒ„ํŠผ.
201
199
  - ์„ ํƒ ํ‘œํ˜„: 2026-10-10 ์‚ฌ์šฉ์ž ์š”์ฒญ โ€” Button๊ณผ ๊ฐ™์€ ์„ ํƒ ํ‘œํ˜„์„ ์“ด๋‹ค.
200
+
201
+ ๊ณต๊ฐœ recipe ์ง์ ‘ ์†Œ๋น„ ์‹œ `states.selected.background`ยท`border` ๋Œ€์‹  `tones[tone].background`ยท`border`๋ฅผ ์ฝ๋Š”๋‹ค. ์„ ํƒ recipe๋Š” ์ „๊ฒฝ๋งŒ ์†Œ์œ ํ•œ๋‹ค. ์ด recipe ํ•„๋“œ ์ œ๊ฑฐ๋Š” ๋‹ค์Œ ๊ฒŒ์‹œ ์‹œ contracts์˜ major ๋ณ€๊ฒฝ์œผ๋กœ ๊ธฐ๋กํ•˜๋ฉฐ ์ปดํฌ๋„ŒํŠธ์˜ `selected`ยท`current` ์‚ฌ์šฉ๋ฒ•์€ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค.
@@ -66,6 +66,13 @@ import { ImageViewer } from "@hjmds/react-native/image-viewer";
66
66
  errorLabel={t("viewer.loadFailed")} retryLabel={t("common.retry")} />
67
67
  ```
68
68
 
69
+ ### ์•„๋ž˜๋กœ ๋Œ์–ด ๋‹ซ๊ธฐ (๊ธฐ๋ณธ ๋™์ž‘, 2026-10-30)
70
+
71
+ ๋‹ค๋ฅธ ์‚ฌ์ง„ ๋ทฐ์–ด์ฒ˜๋Ÿผ ํ™•๋Œ€ํ•˜์ง€ ์•Š์€ ์‚ฌ์ง„์„ ์•„๋ž˜๋กœ ๋Œ๋ฉด `onClose`๊ฐ€ ๋ถˆ๋ฆฐ๋‹ค(์‚ฌ์šฉ์ž ์š”์ฒญ). ์‚ฌ์ง„์ด ์†๊ฐ€๋ฝ์„ ๋”ฐ๋ผ
72
+ ๋‚ด๋ ค์˜ค๊ณ  ์œ„์•„๋ž˜ ์กฐ์ž‘๋ถ€๊ฐ€ ํ๋ ค์ง€๋ฉฐ, 120pt ์ด์ƒ ๋Œ๊ฑฐ๋‚˜ ๋น ๋ฅด๊ฒŒ ๋‚ด๋ฆฌ๋ฉด ๋‹ซํžŒ๋‹ค. ์งง๊ฒŒ ๋Œ๊ฑฐ๋‚˜ ์œ„๋กœ ๋Œ๋ฉด ์ œ์ž๋ฆฌ๋กœ
73
+ ๋Œ์•„์˜จ๋‹ค. ํ™•๋Œ€ํ•œ ์‚ฌ์ง„์€ ๋Œ๋ฉด ์ด๋™ํ•˜๋ฏ€๋กœ ๋‹ซํžˆ์ง€ ์•Š๋Š”๋‹ค. ์ œํ’ˆ์€ ๋”ฐ๋กœ ์—ฐ๊ฒฐํ•  ๊ฒƒ์ด ์—†๊ณ , ๋‹ซ๊ธฐ ๋ฒ„ํŠผ์€ ์ ‘๊ทผ์„ฑ ๊ฒฝ๋กœ๋กœ ๋‚จ๋Š”๋‹ค.
74
+ Web๋„ ๊ฐ™์€ ๊ธฐ์ค€์œผ๋กœ ํ„ฐ์น˜ยทํŽœ์œผ๋กœ ๋Œ๋ฉด ๋‹ซํžˆ๊ณ , ๋งˆ์šฐ์Šค๋Š” ๋‹ซ๊ธฐ ๋ฒ„ํŠผ๊ณผ Esc๋ฅผ ์“ด๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๋ชจ๋“  ์•ฑยท์›น์˜ ๊ธฐ๋ณธ ์ง€์นจ์œผ๋กœ ์ •ํ–ˆ์œผ๋ฏ€๋กœ ์ œํ’ˆ์ด ์ด ๋™์ž‘์„ ๋„๊ฑฐ๋‚˜ ์ž์ฒด ๋ทฐ์–ด๋กœ ๋Œ€์ฒดํ•˜์ง€ ์•Š๋Š”๋‹ค.
75
+
69
76
  ### ์‚ฌ์ง„์„ ๋ˆŒ๋Ÿฌ ํฌ๊ฒŒ ๋ณด๊ธฐ (`ImageViewerTrigger`, 2026-10-23)
70
77
 
71
78
  ์‚ฌ์ง„์„ ์›์‹œ `button`ยท`Pressable`๋กœ ๊ฐ์‹ธ ๋ทฐ์–ด๋ฅผ ์—ด์ง€ ์•Š๋Š”๋‹ค(ํฌํŠธํด๋ฆฌ์˜ค ์‚ฌ์ดํŠธ `App.tsx`, Spint `MarkPhoto`ยท`ComposeScreen`์ด๋˜ ์šฐํšŒ).
@@ -50,7 +50,7 @@ import { ProfileSummary } from '@hjmds/react-native/screen-flows';
50
50
  | badge | ReactNode | ์—†์Œ | ์ด๋ฆ„ ์˜† ๋ฐฐ์ง€ |
51
51
  | nameActions | ReactNode | ์—†์Œ | ์ด๋ฆ„ ํ–‰์˜ ํ–‰๋™ |
52
52
  | nameEdit | `{label,onAction,disabled?,pending?}` | ์—†์Œ | ์ด๋ฆ„ ์˜† ํŽธ์ง‘(์—ฐํ•„) ์•„์ด์ฝ˜ ๋ฒ„ํŠผ. ๋‚ด ํ”„๋กœํ•„์—์„œ ๋‹‰๋„ค์ž„ ๋ฐ”๊พธ๊ธฐ๋ฅผ ์—ฐ๋‹ค(2026-10-25, ์•„๋ž˜ ์ ˆ) |
53
- | stats | `{id,value,label,onPress?,disabled?}[]` | `[]` | ํ†ต๊ณ„ ์ˆ˜์น˜. `onPress`๊ฐ€ ์žˆ์œผ๋ฉด ๋ฒ„ํŠผ |
53
+ | stats | `{id,value,label,onPress?,disabled?}[]` | `[]` | ํ†ต๊ณ„ ์ˆ˜์น˜. `onPress`๊ฐ€ ์žˆ์œผ๋ฉด ๋ฒ„ํŠผ. ํ•œ ์ค„ ์ตœ๋Œ€ 4์นธ(`profileSummaryRecipe.stats.maxPerRow`), ๋„˜์น˜๋ฉด ๋‹ค์Œ ์ค„๋กœ ๊ฐ€๊ณ  ๋งˆ์ง€๋ง‰ ์ค„๋„ ๊ฐ™์€ ์นธ ํญ์ด๋‹ค |
54
54
  | actionsLayout | `inline` ยท `stacked` ยท `equal` | `inline` | ๊ฐ€๋กœ ๋ณด์กฐ ํ–‰๋™์€ inline ์ค„๋ฐ”๊ฟˆ ํ–‰, ์„ธ๋กœ ์ „์ฒด ํญ์€ stacked, ๊ฐ™์€ ํญ์˜ ๊ฐ€๋กœ ํ–‰๋™์€ equal(๊ฐœ๋ฐœ ์†Œ์Šค) |
55
55
  | actions | ReactNode | ์—†์Œ | ์ˆ˜์น˜ ์•„๋ž˜ ํ–‰๋™ ์ค„. [ProfileScreen](profile-screen.md) `editPlacement="summary"` ์•ˆ์—์„œ๋Š” ๊ทธ ํ™”๋ฉด์˜ `edit`์ด ์ด ์ค„ ๋งจ ์•ž์— `secondary` `small` ๋ฒ„ํŠผ(`profileSummaryRecipe.editAction`)์œผ๋กœ ๋จผ์ € ๋“ค์–ด๊ฐ„๋‹ค(1.31.0) |
56
56
  | children | ReactNode | ์—†์Œ | ํ•˜๋‹จ footer |
@@ -62,7 +62,7 @@ import { ProfileSummary } from '@hjmds/react-native/screen-flows';
62
62
  | ํ•ญ๋ชฉ | ๊ฐ’ | ๊ทผ๊ฑฐ |
63
63
  | --- | --- | --- |
64
64
  | ํฌ๊ธฐ | ์›ํ˜• ์•„๋ฐ”ํƒ€ ํ”„๋ ˆ์ž„ 48. ProfileScreen ์ˆ˜์ • ํ–‰๋™์€ `Button` `secondary` `small` | `profileSummaryRecipe.avatarSize`ยท`editAction` |
65
- | ๊ฐ„๊ฒฉ | ์ˆ˜์น˜ ์นธ ์‚ฌ์ด `spacing.md` 16, ๊ฐ’ยท๋ผ๋ฒจ ์‚ฌ์ด `spacing.xxs` 4, ํ–‰๋™ ์‚ฌ์ด `spacing.xs` 8. ์˜์—ญ ๊ฐ„๊ฒฉ์€ [CompositionLayout](composition-layout.md) | ์–‘ renderer |
65
+ | ๊ฐ„๊ฒฉ | ์ˆ˜์น˜ ์นธ ์‚ฌ์ดยท์ˆ˜์น˜ ์ค„ ์‚ฌ์ด `spacing.md` 16, ๊ฐ’ยท๋ผ๋ฒจ ์‚ฌ์ด `spacing.xxs` 4, ํ–‰๋™ ์‚ฌ์ด `spacing.xs` 8. ์˜์—ญ ๊ฐ„๊ฒฉ์€ [CompositionLayout](composition-layout.md) | ์–‘ renderer |
66
66
  | ์ˆœ์„œยท์ •๋ ฌ | ๋จธ๋ฆฌ(์•„๋ฐ”ํƒ€ โ†’ ์ด๋ฆ„ยท๋ฐฐ์ง€ โ†’ nameActions) โ†’ ์ˆ˜์น˜ ์ค„ โ†’ actions โ†’ children, ์ˆ˜์น˜ ์นธ์€ ๊ท ๋“ฑ ํญยท๊ฐ€์šด๋ฐ | `CompositionLayout` + `flex: 1` ์นธ |
67
67
  | ๊ณ ์ •ยท์Šคํฌ๋กค | ์—†์Œ. ProfileScreen ๋ณธ๋ฌธ ์Šคํฌ๋กค ์•ˆ์— ๋†“์ธ๋‹ค | โ€” |
68
68
  | ์ข์€ ํญยทํฐ ๊ธ€์ž | ์ˆ˜์น˜ ์นธ์€ `minWidth: 0`์œผ๋กœ ๋‚˜๋ˆ  ๊ฐ–๊ณ , ํ–‰๋™ ์ค„์€ `wrap` | ์–‘ renderer |
@@ -103,6 +103,31 @@ Web์˜ ํ…์ŠคํŠธ/์ด๋ชจ์ง€ artwork๋Š” ์›ํ˜•72 ํ”„๋ ˆ์ž„ ์•ˆ์—์„œ glyph.xxl48
103
103
  - ๋‚จ์˜ ํ”„๋กœํ•„์—๋Š” `nameEdit`๋ฅผ ๋„ฃ์ง€ ์•Š๊ณ , ์‚ฌ์ง„์€ ํšŒ์› ์•„๋ฐ”ํƒ€ ๊ธฐ์ค€(ํ™•๋Œ€ยท์‹ ๊ณ )์„ ๋”ฐ๋ฅธ๋‹ค.
104
104
  - ์ œํ’ˆ์ด ProfileSummary ๋Œ€์‹  ๊ณ ์œ  ๋จธ๋ฆฌ๋ฅผ ๊ทธ๋ฆฌ๋Š” ๊ฒฝ์šฐ์—๋„ ์‚ฌ์ง„ = ์‚ฌ์ง„ ๋ณ€๊ฒฝ, ์ด๋ฆ„ = ๋‹‰๋„ค์ž„ ๋ณ€๊ฒฝ์„ ์ง€ํ‚จ๋‹ค. ์ด๋ฆ„์ด ๊ทธ๋ฆผ(์˜ˆ: Spint ๋‚ด ์ •์›์˜ ๋‚˜๋ฌด ํŒป๋ง)์ด๋ฉด ์—ฐํ•„์„ ๋”ฐ๋กœ ๋‘์ง€ ์•Š๊ณ  ๊ทธ ์ด๋ฆ„ ๊ทธ๋ฆผ ์ž์ฒด๋ฅผ ๋‹‰๋„ค์ž„ ๋ณ€๊ฒฝ ๋ฒ„ํŠผ์œผ๋กœ ํ•œ๋‹ค(2026-10-25 ์‚ฌ์šฉ์ž ๊ฒฐ์ •).
105
105
 
106
+ ### ํ”„๋กœํ•„ ์‚ฌ์ง„ ์˜ฌ๋ฆฌ๊ธฐ: ์›์œผ๋กœ ๋งž์ถ”๊ธฐ (2026-10-29)
107
+
108
+ ์‚ฌ์šฉ์ž ์ง€์‹œ๋กœ ๋ชจ๋“  ์•ฑยท์›น์—์„œ ํ”„๋กœํ•„ ์‚ฌ์ง„์„ ์˜ฌ๋ฆด ๋•Œ **์ธ์Šคํƒ€๊ทธ๋žจ์ฒ˜๋Ÿผ ์› ์•ˆ์—์„œ ์œ„์น˜์™€ ํฌ๊ธฐ๋ฅผ ๋งž์ถ˜๋‹ค.** ์‹œ์Šคํ…œ ์ •์‚ฌ๊ฐ ์ž๋ฅด๊ธฐ
109
+ (`allowsEditing`, `aspect: [1, 1]`)๋Š” ์›ํ˜• ์•„๋ฐ”ํƒ€์™€ ๋ชจ์–‘์ด ๋‹ฌ๋ผ ๊ฒฐ๊ณผ๋ฅผ ์˜ˆ์ธกํ•  ์ˆ˜ ์—†์—ˆ๋‹ค.
110
+
111
+ - ์‚ฌ์ง„์„ ๊ณ ๋ฅธ ๋’ค(์‹œ์Šคํ…œ ํŽธ์ง‘ ๋”) `image-viewer` ํ•˜์œ„ ๊ฒฝ๋กœ์˜ `CircularPhotoCropper`๋ฅผ ์—ฐ๋‹ค. ๋Œ์–ด์„œ ์˜ฎ๊ธฐ๊ณ  ๋‘ ์†๊ฐ€๋ฝ(์›น์€ ํœ ยท๋‘ ์†๊ฐ€๋ฝ)์œผ๋กœ
112
+ ํ™•๋Œ€ํ•œ๋‹ค. ์› ๋ฐ–์€ ์–ด๋‘ก๊ฒŒ ๊ฐ€๋ฆฌ๊ณ , ๋†“์œผ๋ฉด ์› ์•ˆ์— ๋นˆ ๊ณณ์ด ์ƒ๊ธฐ์ง€ ์•Š๊ฒŒ ๋˜๋Œ์•„๊ฐ„๋‹ค(`clampCircularPhotoCrop`, ์ตœ๋Œ€ `maxZoom` 4๋ฐฐ).
113
+ - `onConfirm(crop)`์€ ์›๋ณธ ํ”ฝ์…€ ์ •์‚ฌ๊ฐํ˜• `{ originX, originY, width, height }`์ด๋‹ค. ์ œํ’ˆ์ด ๊ทธ ์˜์—ญ์„ ์ž๋ฅด๊ณ (Native `expo-image-manipulator`
114
+ `crop`, Web canvas) ๊ธฐ์กด ํฌ๊ธฐ ์ œํ•œยท์••์ถ•์œผ๋กœ ์˜ฌ๋ฆฐ๋‹ค. ์ž๋ฅด๊ธฐยท์—…๋กœ๋“œ ์ค‘์—๋Š” `busy`๋กœ ๋‘ ๋ฒ„ํŠผ์„ ์ž ๊ทผ๋‹ค.
115
+ - ์ •์‚ฌ๊ฐํ˜•์„ ์ €์žฅํ•˜๊ณ  ์› ๋ชจ์–‘์€ ํ‘œ์‹œ(`cover`)๊ฐ€ ๋งŒ๋“ ๋‹ค. ์›ํ˜• ํˆฌ๋ช… PNG๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค.
116
+ - ๊ธ€ ์ฒจ๋ถ€ ์‚ฌ์ง„์ฒ˜๋Ÿผ ์›ํ˜•์ด ์•„๋‹Œ ์‚ฌ์ง„์—๋Š” ์“ฐ์ง€ ์•Š๋Š”๋‹ค.
117
+
118
+ ### ๋‚จ์˜ ํ”„๋กœํ•„: ์‚ฌ์ง„์„ ๋ˆ„๋ฅด๋ฉด ํฌ๊ฒŒ ๋ณด๊ธฐ (2026-10-29)
119
+
120
+ ์‚ฌ์šฉ์ž ์ง€์‹œ๋กœ ๋ชจ๋“  ์•ฑยท์›น์˜ ํšŒ์› ํ”„๋กœํ•„ ํ™”๋ฉด์—์„œ **๊ทธ ํšŒ์›์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„์„ ๋ˆ„๋ฅด๋ฉด ์‚ฌ์ง„์ด ํฌ๊ฒŒ ์—ด๋ฆฐ๋‹ค.** Spint ํšŒ์› ํ”„๋กœํ•„์—์„œ
121
+ ์‚ฌ์ง„์„ ๋ˆŒ๋Ÿฌ๋„ ๋ฐ˜์‘์ด ์—†์—ˆ๊ณ , Utilverse๋งŒ ์ œํ’ˆ ์ฝ”๋“œ๋กœ ํ™•๋Œ€๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด ์•ฑ๋งˆ๋‹ค ๋™์ž‘์ด ๋‹ฌ๋ž๋‹ค.
122
+
123
+ - `avatarView={{ label: t("photo.view"), onPress: () => setViewerOpen(true) }}`๋ฅผ ์ค€๋‹ค. HJM์ด ์•„๋ฐ”ํƒ€ ํ‹€ ์ „์ฒด๋ฅผ ์ด๋ฏธ์ง€ ๋ฒ„ํŠผ
124
+ (`ImageViewerTrigger`, Native `imagebutton` ์—ญํ• )์œผ๋กœ ๋งŒ๋“ค๊ณ  ํฌ๊ธฐยท์›ํ˜•ยท๋ˆ„๋ฆ„ ์ƒํƒœ๋ฅผ ์†Œ์œ ํ•œ๋‹ค.
125
+ - ์ œํ’ˆ์€ `image-viewer` ํ•˜์œ„ ๊ฒฝ๋กœ์˜ `ImageViewer`๋ฅผ ์—ฐ๋‹ค(Native๋Š” gesture-handlerยทzoom-toolkit ์„ ํƒ ์˜์กด์„ฑ์ด ๊ทธ ๊ฒฝ๋กœ์—๋งŒ ์žˆ๋‹ค).
126
+ ์‚ฌ์ง„ ์กฐํšŒยท๊ถŒํ•œยท์บ์‹œ๋Š” `items`/`renderImage`๋กœ ๊ณต๊ธ‰ํ•œ๋‹ค. ์‚ฌ์ง„ ์‹ ๊ณ ๋Š” ์ œํ’ˆ์˜ ๊ธฐ์กด ๊ฒฝ๋กœ๋ฅผ ์œ ์ง€ํ•˜๋ฉฐ ํ™•๋Œ€ ํ™”๋ฉด์— ๋‘˜ ๋•Œ๋Š” `actions`๋ฅผ ์“ด๋‹ค.
127
+ - ์‚ฌ์ง„์ด ์—†์–ด ์ด๋‹ˆ์…œยท๊ธฐ๋ณธ ์–ผ๊ตด๋งŒ ์žˆ์œผ๋ฉด `avatarView`๋ฅผ ๋„ฃ์ง€ ์•Š๋Š”๋‹ค(ํฌ๊ฒŒ ๋ณผ ์‚ฌ์ง„์ด ์—†๋‹ค).
128
+ - ๋‚ด ํ”„๋กœํ•„์€ ์‚ฌ์ง„ = ์‚ฌ์ง„ ๋ณ€๊ฒฝ(`Avatar.action`)์ด๋ฏ€๋กœ `avatarView`๋ฅผ ๋„ฃ์ง€ ์•Š๋Š”๋‹ค. ๋‘ ํ–‰๋™์„ ํ•œ ์‚ฌ์ง„์— ๊ฐ™์ด ๋„ฃ์ง€ ์•Š๋Š”๋‹ค.
129
+ - ์ œํ’ˆ ๊ณ ์œ  ๋จธ๋ฆฌ(ProfileSummary ๋Œ€์‹  ๊ทธ๋ฆฐ ์นด๋“œ)๋„ ๊ฐ™์€ ๊ทœ์น™์„ `ImageViewerTrigger` + `ImageViewer`๋กœ ์ง€ํ‚จ๋‹ค.
130
+
106
131
  ### ํ”„๋กœํ•„ ์นด๋“œ ํ‹€ (2026-10-25)
107
132
 
108
133
  ๊ฐ™์€ ๋‚  ์‚ฌ์šฉ์ž ์ง€์ : Utilverse ํ”„๋กœํ•„ ์นด๋“œ(ํ‰ํ‰ํ•œ `default`, radius `lg`)๊ฐ€ BurnTok ์นด๋“œ(`raised` ๊ทธ๋ฆผ์ž, ํ…Œ๋‘๋ฆฌ, radius `xl`)์™€
@@ -163,3 +163,21 @@ Chip/์ง์ ‘ wrap์œผ๋กœ ์กฐ๋ฆฝํ•˜๊ณ  ์žˆ์—ˆ๋‹ค. `presentation="chips"`๋Š” ๊ธฐ์กด
163
163
  ### ๊ทธ๋ฆผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์™€ ๊ธฐ๋ณธ ์„ ํƒ ํ‘œ์‹œ
164
164
 
165
165
  2026-10-20 ๋‹ค์—๋ฆฌ ๋ฐ˜์‘ ์นด๋“œ ์ด๊ด€: Web๋„ Native์™€ ๊ฐ™์ด `indicator="none"`์œผ๋กœ ๊ธฐ๋ณธ ์›ํ˜• ํ‘œ์‹œ๋ฅผ ์ˆจ๊ธธ ์ˆ˜ ์žˆ๋‹ค(๋กœ์ปฌยท๋ฏธ๊ฒŒ์‹œ). `renderLabel`์˜ ๋น„์ƒํ˜ธ์ž‘์šฉ ๊ทธ๋ฆผ/๋ฌธ๊ตฌ์™€ ๊ณตํ†ต ์นด๋“œ์˜ ์„ ํƒ ์ƒ‰์œผ๋กœ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ผ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. ์‹ค์ œ radioยท๊ทธ๋ฃน ์ด๋ฆ„ยทcheckedยทํ‚ค๋ณด๋“œ ์„ ํƒยท๋น„ํ™œ์„ฑ ๊ณ„์•ฝ์€ ์œ ์ง€๋˜๋ฉฐ, ๊ธฐ๋ณธ๊ฐ’ `default`๋Š” ๊ธฐ์กด ์›ํ˜• ํ‘œ์‹œ๋ฅผ ์œ ์ง€ํ•œ๋‹ค. ์ œํ’ˆ์—์„œ ์นด๋“œ ํ…Œ๋‘๋ฆฌ/๋ˆŒ๋ฆผ/์„ ํƒ ํ‹€์„ ๋‹ค์‹œ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค.
166
+
167
+ ### ๊ฐ€๋กœ ์Šคํฌ๋กค ์นด๋“œ (2026-10-28 ๊ฐœ๋ฐœ ์†Œ์Šค)
168
+
169
+ ์˜ค๋Š˜์•ผ๊ตฌ์˜ ์ผ์ž ์นด๋“œ ์š”์ฒญ์œผ๋กœ `overflow="scroll"`์„ Web/Native RadioGroup์— ์ถ”๊ฐ€ํ–ˆ๋‹ค.
170
+ `presentation="card"` ๋˜๋Š” `"chips"`์—์„œ `grid` ์—†์ด ์‚ฌ์šฉํ•˜๋ฉฐ ๊ธฐ๋ณธ `wrap` ๋™์ž‘์€ ์œ ์ง€ํ•œ๋‹ค.
171
+ ๊ณตํ†ต ๋ ˆ์ผ์ด ๊ฐ€๋กœ ์Šคํฌ๋กค๊ณผ ์„ ํƒ ํ•ญ๋ชฉ ๋…ธ์ถœ์„ ์†Œ์œ ํ•œ๋‹ค. ์ œํ’ˆ์€ `renderLabel`๋กœ ์š”์ผยท์ผ์ž ๋“ฑ
172
+ ์ฝ˜ํ…์ธ ๋งŒ ๊ณต๊ธ‰ํ•œ๋‹ค. ๋น„ํ™œ์„ฑ์ธ ์„ ํƒ ๋‚ ์งœ๋„ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค. ์กฐํšŒ ์‹คํŒจ๋ฅผ ๋น„ํ™œ์„ฑ ๋‚ ์งœ๋กœ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค.
173
+ ์ด API๋Š” ์•„์ง ๊ฒŒ์‹œํ•˜์ง€ ์•Š์€ ๊ฐœ๋ฐœ ์†Œ์Šค ๋ณ€๊ฒฝ์ด๋‹ค.
174
+
175
+ ### ์„ ํƒ ๋ฉด์˜ ๋ธŒ๋žœ๋“œ ๊ฐ•์กฐ (2026-10-29 ๊ฐœ๋ฐœ ์†Œ์Šค ยท ๋ฏธ๊ฒŒ์‹œ)
176
+
177
+ `selectionTone="neutral" | "brand" | "brand-outline"`๋ฅผ Web/Native์—์„œ ์ง€์ •ํ•œ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ `neutral`์ด๋‹ค.
178
+ ์˜ค๋Š˜์•ผ๊ตฌ ๋‚ ์งœ ์นด๋“œ์˜ ์„ ํƒ์ผ์„ ํŒŒ๋ž€ ๋ฐฐ๊ฒฝยทํฐ ๊ธ€์ž๋กœ ๊ฐ•์กฐํ•ด ๋‹ฌ๋ผ๋Š” ์š”์ฒญ์ด ๊ณ„๊ธฐ๋‹ค.
179
+ `brand`๋Š” ํ…Œ๋งˆ์˜ `primary`/`onPrimary` ์Œ์„ ์‚ฌ์šฉํ•˜๊ณ  ๋ฏธ์„ ํƒ ๋ฉด์€ ๊ธฐ์กด canvas๋ฅผ ์œ ์ง€ํ•œ๋‹ค(light๋Š” ํฐ์ƒ‰).
180
+ ๋ผ๋ฒจยท์„ค๋ช…๊ณผ Native `renderLabel` ์•ˆ์˜ ์ค‘๋ฆฝ Text๋Š” ์„ ํƒ ๋ฉด์˜ ๊ธ€์ž์ƒ‰์„ ์ƒ์†ํ•œ๋‹ค.
181
+ ๋ธŒ๋žœ๋“œ์ƒ‰ยท์ƒํƒœ์ƒ‰์„ ๋ช…์‹œํ•œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋Š” ์ œํ’ˆ์ด ์˜๋ฏธ์— ๋งž๊ฒŒ ๊ณต๊ธ‰ํ•œ๋‹ค.
182
+
183
+ ๊ฐ™์€ ๋‚  ์‚ฌ์šฉ์ž ํ›„์† ์š”์ฒญ์œผ๋กœ `brand-outline`์€ ์„ ํƒ ๋ฐฐ๊ฒฝ๋„ canvas(light ํฐ์ƒ‰)๋กœ ์œ ์ง€ํ•˜๊ณ  ํ…Œ๋‘๋ฆฌ๋งŒ ๋Œ€ํ‘œ์ƒ‰ primary๋กœ ๊ฐ•์กฐํ•œ๋‹ค. ์ผ๋ฐ˜ ๊ธ€์ž์ƒ‰๊ณผ ๋น„ํ™œ์„ฑ ๊ณ„์•ฝ์€ ์œ ์ง€ํ•œ๋‹ค.
@@ -134,7 +134,7 @@ Native ์Šคํฌ๋กค ๋ณธ๋ฌธ์€ viewport ๋†’์ด๋งŒํผ content๋ฅผ ์ฑ„์šด๋‹ค(๋ฏธ๊ฒŒ์‹œ)
134
134
  | --- | --- | --- |
135
135
  | ํฌ๊ธฐ | ํ•˜๋‹จ ์‹œํŠธ ๋†’์ด: `auto` ๋‚ด์šฉ ๋†’์ด(์ตœ๋Œ€ ํ™”๋ฉด ๋†’์ด ร— `maxHeightRatio` 0.9), `medium` 60%, `large` 85%, `full` ์œ„ safe area ์•ˆ ์ „์ฒด ๋†’์ด(0.9 ์ƒํ•œ ์—†์Œ). Web ํ•˜๋‹จ ์‹œํŠธ ์ตœ๋Œ€ ํญ 640. ์œ„ ๋‘ ๋ชจ์„œ๋ฆฌ๋งŒ `radius.xl` 24. ์˜† ์‹œํŠธ(`start`ยท`end`): Web ํญ `min(28rem, 100%)`ยท๋†’์ด ํ™”๋ฉด โˆ’ 32, Native ํญ 88%(์ตœ๋Œ€ 420)ยทํ™”๋ฉด ๋†’์ด | `sheetRecipe.sizes`ยท`content`ยท`web`, `.hjm-sheet`, Native `Sheet` |
136
136
  | ๊ฐ„๊ฒฉ | ๋‘ ํ”Œ๋žซํผ ๊ฐ™์€ ๊ฐ’: ์ขŒ์šฐ `content.paddingHorizontal`(`spacing.lg` 20), ์œ„ยท์•„๋ž˜ `content.paddingTop/Bottom`(`spacing.sm` 12, ํ•˜๋‹จ ์‹œํŠธ๋Š” ์•„๋ž˜ safe area๋ฅผ ๋”ํ•จ), ๋จธ๋ฆฌยท๋ณธ๋ฌธยทfooter ์‚ฌ์ด `body.gap`(`spacing.md` 16), footer ์œ„ `footer.paddingTop`(`spacing.sm` 12), ์ œ๋ชฉโ€“๋‹ซ๊ธฐ `header.gap`(`spacing.sm` 12), footer ๋ฒ„ํŠผ ์‚ฌ์ด `footer.gap`(`spacing.xs` 8) | `.hjm-sheet__*`, `sheetRecipe.content`ยท`body`ยท`footer` |
137
- | ์ˆœ์„œยท์ •๋ ฌ | ์†์žก์ด(ํ•˜๋‹จ) โ†’ ๋จธ๋ฆฌ(์„ค๋ช… + ๋‹ซ๊ธฐ, ๋‘˜ ๋‹ค ์—†์œผ๋ฉด ๊ทธ๋ฆฌ์ง€ ์•Š์Œ) โ†’ ๋ณธ๋ฌธ โ†’ `footer`. ๋ณด์ด๋Š” ์ œ๋ชฉ ์ค„์€ ์—†๋‹ค(๋ฏธ๊ฒŒ์‹œ). ๋จธ๋ฆฌ ์ตœ์†Œ ๋†’์ด 44(`control.minTouchTarget`), ร—๋Š” ์„ค๋ช…์ด ์—†์–ด๋„ ๋ ์ชฝ ๊ฐ™์€ ์ž๋ฆฌ๋‹ค. ๊ธฐ๋ณธ ํ•˜๋‹จ ์‹œํŠธ๋Š” ๋จธ๋ฆฌ๊ฐ€ ์—†์–ด ๋ณธ๋ฌธ์ด ์†์žก์ด ์˜์—ญ ๋ฐ”๋กœ ์•„๋ž˜ `body.gap` 16์—์„œ ์‹œ์ž‘ํ•œ๋‹ค. Web footer๋Š” ์˜ค๋ฅธ์ชฝ ์ •๋ ฌ ๊ฐ€๋กœ ์ค„ [๋ณด์กฐ][์ฃผ]. Native footer๋Š” ์„ธ๋กœ ์—ด์ด๋ผ ๊ฝ‰ ์ฐฌ ํญ ๋ฒ„ํŠผ์„ ์ฃผ ํ–‰๋™ ๋จผ์ € ๋‘”๋‹ค. ํ•˜๋‹จ ์‹œํŠธ๋Š” ํ•ญ์ƒ ์œ„ ๊ฐ€์šด๋ฐ 36ร—4 ์†์žก์ด(ํ„ฐ์น˜ ์ตœ์†Œ ํญยท๋†’์ด44)๋ฅผ ๋‘”๋‹ค. `detents`๊ฐ€ ์žˆ์œผ๋ฉด ๋†’์ด ์ „ํ™˜ ๋ฒ„ํŠผ, ์—†๊ณ  ร—๊ฐ€ ์—†์œผ๋ฉด `closeLabel` ์ด๋ฆ„์˜ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ์ด๋‹ค. `showClose: true`๋กœ ร—๋ฅผ ํ‘œ์‹œํ•˜๋ฉด ์†์žก์ด๋Š” ์žฅ์‹์œผ๋กœ ๋‚จ๊ฒจ ๊ฐ™์€ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ์ด ์ค‘๋ณต๋˜์ง€ ์•Š๋Š”๋‹ค(๋ฏธ๊ฒŒ์‹œ). ์†์žก์ดยท๋จธ๋ฆฌ ์˜์—ญ์„ ์•„๋ž˜๋กœ ๋Œ์–ด `sheetDragRecipe.dismissDistanceRatio`(์‹œํŠธ ๋†’์ด์˜ 25%) ๋˜๋Š” `dismissVelocity` 0.5px/ms๋ฅผ ๋„˜๊ธฐ๋ฉด `swipe`๋กœ ๋‹ซ๊ณ , ์•„๋‹ˆ๋ฉด ์ œ์ž๋ฆฌ๋กœ ๋Œ์•„๊ฐ„๋‹ค(๋ฏธ๊ฒŒ์‹œ, WebยทNative ๋™์ผ). ๋ณธ๋ฌธ๋„ ๊ฐ™์€ ๋Œ๊ธฐ๋กœ ๋‹ซ๋Š”๋‹ค(2026-10-27 ์‚ฌ์šฉ์ž ๊ฒฐ์ •, ๋ฏธ๊ฒŒ์‹œ). ์Šคํฌ๋กค ๋ณธ๋ฌธ์€ ์ด๋ฏธ ๋งจ ์œ„์ผ ๋•Œ ์‹œ์ž‘ํ•œ ์•„๋ž˜ ๋Œ๊ธฐ๋งŒ ์‹œํŠธ๊ฐ€ ๊ฐ€์ ธ๊ฐ€๊ณ , ์Šคํฌ๋กค๋œ ์ƒํƒœ์—์„œ๋Š” ๊ฐ™์€ ๋™์ž‘์ด ๋จผ์ € ๋‚ด์šฉ์„ ์œ„๋กœ ๋˜๋Œ๋ฆฐ๋‹ค. iOS๋Š” UIScrollView๊ฐ€ JS ์‘๋‹ต์ž ์•„๋ž˜์„œ๋„ ์Šค์Šค๋กœ ๋Œ๊ธฐ๋ฅผ ๊ฐ€์ ธ๊ฐ€๋ฏ€๋กœ ๋ณธ๋ฌธ ๋งจ ์œ„์˜ ๋‹น๊น€(overscroll)์œผ๋กœ ์‹œํŠธ๋ฅผ ์›€์ง์ด๊ณ  UIKit ํƒ„์„ฑ ๊ฑฐ๋ฆฌ๋กœ ํ™˜์‚ฐํ•œ ๊ฐ™์€ ์ž„๊ณ„๊ฐ’์„ ์“ด๋‹ค. Web ๋ณธ๋ฌธ ๋Œ๊ธฐ๋Š” ํ„ฐ์น˜๋งŒ ๋ฐ›๊ณ  ๋งˆ์šฐ์Šค๋Š” ๊ธ€์ž ์„ ํƒ์„ ์œ„ํ•ด ์†์žก์ด๋ฅผ ์“ด๋‹ค. ์ž…๋ ฅ์นธ์—์„œ ์‹œ์ž‘ํ•œ ๋Œ๊ธฐ์™€ ๊ฐ€๋กœ ๋Œ๊ธฐ๋Š” ๋ณธ๋ฌธ ์•ˆ์˜ ํ–‰๋™์œผ๋กœ ๋‚จ๊ธด๋‹ค | `sheetRecipe.header`ยท`handle`, `.hjm-sheet__footer`, `.hjm-sheet__handle` |
137
+ | ์ˆœ์„œยท์ •๋ ฌ | ์†์žก์ด(ํ•˜๋‹จ) โ†’ ๋จธ๋ฆฌ(์„ค๋ช… + ๋‹ซ๊ธฐ, ๋‘˜ ๋‹ค ์—†์œผ๋ฉด ๊ทธ๋ฆฌ์ง€ ์•Š์Œ) โ†’ ๋ณธ๋ฌธ โ†’ `footer`. ๋ณด์ด๋Š” ์ œ๋ชฉ ์ค„์€ ์—†๋‹ค(๋ฏธ๊ฒŒ์‹œ). ๋จธ๋ฆฌ ์ตœ์†Œ ๋†’์ด 44(`control.minTouchTarget`), ร—๋Š” ์„ค๋ช…์ด ์—†์–ด๋„ ๋ ์ชฝ ๊ฐ™์€ ์ž๋ฆฌ๋‹ค. ๊ธฐ๋ณธ ํ•˜๋‹จ ์‹œํŠธ๋Š” ๋จธ๋ฆฌ๊ฐ€ ์—†์–ด ๋ณธ๋ฌธ์ด ์†์žก์ด ์˜์—ญ ๋ฐ”๋กœ ์•„๋ž˜ `body.gap` 16์—์„œ ์‹œ์ž‘ํ•œ๋‹ค. Web footer๋Š” ์˜ค๋ฅธ์ชฝ ์ •๋ ฌ ๊ฐ€๋กœ ์ค„ [๋ณด์กฐ][์ฃผ]. Native footer๋Š” ์„ธ๋กœ ์—ด์ด๋ผ ๊ฝ‰ ์ฐฌ ํญ ๋ฒ„ํŠผ์„ ์ฃผ ํ–‰๋™ ๋จผ์ € ๋‘”๋‹ค. ํ•˜๋‹จ ์‹œํŠธ๋Š” ํ•ญ์ƒ ์œ„ ๊ฐ€์šด๋ฐ 36ร—4 ์†์žก์ด(ํ„ฐ์น˜ ์ตœ์†Œ ํญยท๋†’์ด44)๋ฅผ ๋‘”๋‹ค. `detents`๊ฐ€ ์žˆ์œผ๋ฉด ๋†’์ด ์ „ํ™˜ ๋ฒ„ํŠผ, ์—†๊ณ  ร—๊ฐ€ ์—†์œผ๋ฉด `closeLabel` ์ด๋ฆ„์˜ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ์ด๋‹ค. `showClose: true`๋กœ ร—๋ฅผ ํ‘œ์‹œํ•˜๋ฉด ์†์žก์ด๋Š” ์žฅ์‹์œผ๋กœ ๋‚จ๊ฒจ ๊ฐ™์€ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ์ด ์ค‘๋ณต๋˜์ง€ ์•Š๋Š”๋‹ค(๋ฏธ๊ฒŒ์‹œ). ์†์žก์ดยท๋จธ๋ฆฌ ์˜์—ญ์„ ์•„๋ž˜๋กœ ๋Œ์–ด `sheetDragRecipe.dismissDistanceRatio`(์‹œํŠธ ๋†’์ด์˜ 25%) ๋˜๋Š” `dismissVelocity` 0.5px/ms๋ฅผ ๋„˜๊ธฐ๋ฉด `swipe`๋กœ ๋‹ซ๊ณ , ์•„๋‹ˆ๋ฉด ์ œ์ž๋ฆฌ๋กœ ๋Œ์•„๊ฐ„๋‹ค(๋ฏธ๊ฒŒ์‹œ, WebยทNative ๋™์ผ). ๋ณธ๋ฌธ๋„ ๊ฐ™์€ ๋Œ๊ธฐ๋กœ ๋‹ซ๋Š”๋‹ค(2026-10-27 ์‚ฌ์šฉ์ž ๊ฒฐ์ •, ๋ฏธ๊ฒŒ์‹œ). ์Šคํฌ๋กค ๋ณธ๋ฌธ์€ ์ด๋ฏธ ๋งจ ์œ„์ผ ๋•Œ ์‹œ์ž‘ํ•œ ์•„๋ž˜ ๋Œ๊ธฐ๋งŒ ์‹œํŠธ๊ฐ€ ๊ฐ€์ ธ๊ฐ€๊ณ , ์Šคํฌ๋กค๋œ ์ƒํƒœ์—์„œ๋Š” ๊ฐ™์€ ๋™์ž‘์ด ๋จผ์ € ๋‚ด์šฉ์„ ์œ„๋กœ ๋˜๋Œ๋ฆฐ๋‹ค. ์Šคํฌ๋กคํ•˜์ง€ ์•Š๋Š” ๋ณธ๋ฌธ ์•ˆ์— ๋“ค์–ด๊ฐ„ ๋ชฉ๋ก ์Šคํฌ๋กค(์‹œํŠธ ์•ˆ `SearchScreen embedded`)๋„ ๊ฐ™์€ ๊ทœ์น™์„ ๋”ฐ๋ฅธ๋‹ค. Native๋Š” ๊ทธ ๋ชฉ๋ก์ด ์‹œํŠธ์˜ ๋Œ๊ธฐ ์—ฐ๊ฒฐ์„ ๋„˜๊ฒจ๋ฐ›์•„ ๋ชฉ๋ก ์œ„์น˜๋กœ ํŒ๋‹จํ•œ๋‹ค(2026-10-30 Spint ๋„์‹œ ์‹œํŠธ์—์„œ ์Šคํฌ๋กค๋œ ๋ชฉ๋ก์„ ๋˜๋Œ๋ฆฌ๋ฉด ์‹œํŠธ๋„ ํ•จ๊ป˜ ๋‚ด๋ ค๊ฐ€๋˜ ๊ฒฐํ•จ, ๋ฏธ๊ฒŒ์‹œ). iOS๋Š” UIScrollView๊ฐ€ JS ์‘๋‹ต์ž ์•„๋ž˜์„œ๋„ ์Šค์Šค๋กœ ๋Œ๊ธฐ๋ฅผ ๊ฐ€์ ธ๊ฐ€๋ฏ€๋กœ ๋ณธ๋ฌธ ๋งจ ์œ„์˜ ๋‹น๊น€(overscroll)์œผ๋กœ ์‹œํŠธ๋ฅผ ์›€์ง์ด๊ณ  UIKit ํƒ„์„ฑ ๊ฑฐ๋ฆฌ๋กœ ํ™˜์‚ฐํ•œ ๊ฐ™์€ ์ž„๊ณ„๊ฐ’์„ ์“ด๋‹ค. Web ๋ณธ๋ฌธ ๋Œ๊ธฐ๋Š” ํ„ฐ์น˜๋งŒ ๋ฐ›๊ณ  ๋งˆ์šฐ์Šค๋Š” ๊ธ€์ž ์„ ํƒ์„ ์œ„ํ•ด ์†์žก์ด๋ฅผ ์“ด๋‹ค. ์ž…๋ ฅ์นธ์—์„œ ์‹œ์ž‘ํ•œ ๋Œ๊ธฐ์™€ ๊ฐ€๋กœ ๋Œ๊ธฐ๋Š” ๋ณธ๋ฌธ ์•ˆ์˜ ํ–‰๋™์œผ๋กœ ๋‚จ๊ธด๋‹ค | `sheetRecipe.header`ยท`handle`, `.hjm-sheet__footer`, `.hjm-sheet__handle` |
138
138
  | ๊ณ ์ •ยท์Šคํฌ๋กค | ๋ณธ๋ฌธ๋งŒ ์Šคํฌ๋กค, ๋จธ๋ฆฌยท`footer` ๊ณ ์ •(Native๋Š” `scrollable`์ผ ๋•Œ ScrollView). ์Šคํฌ๋กค ์˜์—ญ์€ ์‹œํŠธ ์ขŒ์šฐ ์—ฌ๋ฐฑ(`sheetRecipe.content.paddingHorizontal` 20) ๋๊นŒ์ง€ ๋„“๊ณ  ์—ฌ๋ฐฑ์€ ์Šคํฌ๋กค ๋‚ด์šฉ ์•ˆ์ชฝ์— ๋‘”๋‹ค(Web `.hjm-sheet__body` padding, Native ScrollView `marginHorizontal: -20` + ๋‚ด์šฉ `paddingHorizontal: 20`). ๊ทธ๋ž˜์„œ ๋ณธ๋ฌธ ListRow์˜ 8 ํ‘œ๋ฉด์ด ์ž˜๋ฆฌ์ง€ ์•Š๊ณ  ๋‚ด์šฉ ์œ„์น˜๋Š” ๊ทธ๋Œ€๋กœ๋‹ค. ๊ณ ์ • ๋†’์ด(`medium`ยท`large`ยท`full`, ์˜† ์‹œํŠธ)์—์„œ๋Š” ๋ณธ๋ฌธ์ด ๋จธ๋ฆฌยทfooter๋ฅผ ๋บ€ ๋‚จ์€ ๋†’์ด๋ฅผ ์ฐจ์ง€ํ•˜๊ณ  `footer`๋Š” ์‹œํŠธ ์•„๋ž˜์— ๋ถ™๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ๋ณธ๋ฌธ์˜ `flex: 1` ์ž์‹(SearchScreen, ๋ชฉ๋ก ํ™”๋ฉด)์ด ๊ทธ ๋†’์ด๋ฅผ ์ฑ„์šด๋‹ค(Web `.hjm-sheet__body` `flex: 1 1 auto`, Native ๋ณธ๋ฌธ `flexGrow: 1`ยท`minHeight: 0`, 1.13.1 ๋ณ€๊ฒฝ). `auto`๋Š” ๋‚ด์šฉ ๋†’์ด ๊ทธ๋Œ€๋กœ๋‹ค. ์ €์žฅยทํ™•์ธ์€ `footer`์—. ์•„๋ž˜ ์•ˆ์ „ ์˜์—ญ์€ ์‹œํŠธ๊ฐ€ ์ž๊ธฐ ์—ฌ๋ฐฑ์— ๋”ํ•œ๋‹ค(`spacing.sm` 12 + ์•„๋ž˜ inset, Web์€ `env(safe-area-inset-bottom)`). ์œ„ inset์€ ์‹œํŠธ๊ฐ€ ์˜ฌ๋ผ๊ฐˆ ๋†’์ด๋ฅผ ์ค„์ธ๋‹ค. ์ œํ’ˆ์ด inset์„ ๋‹ค์‹œ ๋”ํ•˜์ง€ ์•Š๋Š”๋‹ค | `.hjm-sheet__body`, `sheetRecipe.safeArea`, Native `Sheet` |
139
139
  | ์ข์€ ํญยทํฐ ๊ธ€์ž | ํญ < 640์ด๋ฉด ํ•˜๋‹จ ์‹œํŠธ๊ฐ€ ํ™”๋ฉด ํญ์„ ์ฑ„์šด๋‹ค. Web footer๋Š” ์ค„์„ ๋ฐ”๊พผ๋‹ค. ํฐ ๊ธ€์ž๋กœ ๋‚ด์šฉ์ด ๊ธธ์–ด์ง€๋ฉด `auto`๋Š” ์ตœ๋Œ€ 90%์—์„œ ๋ฉˆ์ถ”๊ณ  ๋ณธ๋ฌธ์ด ์Šคํฌ๋กค๋œ๋‹ค(`full`์€ ์œ„ safe area๊นŒ์ง€) | `.hjm-sheet[data-placement="bottom"]`, `maxHeightRatio` |
140
140