@hjmds/design-contracts 1.27.2 → 1.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/activity-chip.d.ts +1 -1
- package/dist/activity-chip.js +1 -1
- package/dist/activity-chip.js.map +1 -1
- package/dist/auth-screen.d.ts +77 -5
- package/dist/auth-screen.d.ts.map +1 -1
- package/dist/auth-screen.js +63 -2
- package/dist/auth-screen.js.map +1 -1
- package/dist/base-recipes.d.ts +1 -1
- package/dist/base-recipes.js +1 -1
- package/dist/base-recipes.js.map +1 -1
- package/dist/behaviors.d.ts +1 -1
- package/dist/breadcrumb.d.ts +1 -1
- package/dist/breadcrumb.js +1 -1
- package/dist/breadcrumb.js.map +1 -1
- package/dist/calendar.d.ts +1 -1
- package/dist/calendar.js +2 -2
- package/dist/calendar.js.map +1 -1
- package/dist/catalog.d.ts +131 -87
- package/dist/catalog.d.ts.map +1 -1
- package/dist/component-contracts.d.ts +1 -1
- package/dist/component-contracts.js +1 -1
- package/dist/component-contracts.js.map +1 -1
- package/dist/component-recipes.d.ts +85 -57
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +98 -60
- package/dist/component-recipes.js.map +1 -1
- package/dist/conversation-row.d.ts +2 -2
- package/dist/conversation-row.d.ts.map +1 -1
- package/dist/conversation-row.js +6 -4
- package/dist/conversation-row.js.map +1 -1
- package/dist/counter-badge-recipe.d.ts +1 -1
- package/dist/counter-badge-recipe.js +1 -1
- package/dist/counter-badge-recipe.js.map +1 -1
- package/dist/data-table.d.ts +4 -4
- package/dist/data-table.js +4 -4
- package/dist/data-table.js.map +1 -1
- package/dist/date-picker.d.ts +1 -1
- package/dist/design-profile.d.ts.map +1 -1
- package/dist/design-profile.js +19 -16
- package/dist/design-profile.js.map +1 -1
- package/dist/file-picker.d.ts +1 -1
- package/dist/file-picker.js +1 -1
- package/dist/file-picker.js.map +1 -1
- package/dist/foundations.d.ts +18 -14
- package/dist/foundations.d.ts.map +1 -1
- package/dist/foundations.js +18 -13
- package/dist/foundations.js.map +1 -1
- package/dist/heading.d.ts +13 -13
- package/dist/heading.js.map +1 -1
- package/dist/number-field.d.ts +1 -1
- package/dist/otp-field.d.ts +1 -1
- package/dist/password-field.d.ts +1 -1
- package/dist/result.d.ts +1 -1
- package/dist/result.js +2 -2
- package/dist/result.js.map +1 -1
- package/dist/screen-patterns.d.ts +36 -0
- package/dist/screen-patterns.d.ts.map +1 -1
- package/dist/screen-patterns.js +20 -0
- package/dist/screen-patterns.js.map +1 -1
- package/dist/semantic-colors.d.ts +10 -0
- package/dist/semantic-colors.d.ts.map +1 -1
- package/dist/semantic-colors.js +6 -0
- package/dist/semantic-colors.js.map +1 -1
- package/dist/sheet.d.ts +7 -4
- package/dist/sheet.d.ts.map +1 -1
- package/dist/sheet.js +8 -5
- package/dist/sheet.js.map +1 -1
- package/dist/side-panel.d.ts +1 -1
- package/dist/side-panel.js +2 -2
- package/dist/side-panel.js.map +1 -1
- package/dist/steps.d.ts +1 -1
- package/dist/steps.js +1 -1
- package/dist/steps.js.map +1 -1
- package/dist/tag.d.ts +1 -1
- package/dist/tag.js +1 -1
- package/dist/tag.js.map +1 -1
- package/dist/timeline.d.ts +1 -1
- package/dist/timeline.js +1 -1
- package/dist/timeline.js.map +1 -1
- package/dist/top.d.ts +4 -4
- package/dist/top.d.ts.map +1 -1
- package/dist/top.js +3 -1
- package/dist/top.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/activity-chip.md +5 -1
- package/docs/auth-screen.md +2 -0
- package/docs/design-profile.md +14 -10
- package/docs/generated/component-maturity.md +1 -1
- package/docs/generated/renderer-evidence.json +9 -5
- package/docs/generated/renderer-evidence.md +1 -1
- package/docs/generated/showcase-manifest.json +1 -1
- package/docs/heading.md +14 -9
- package/docs/sheet.md +2 -0
- package/docs/top.md +4 -3
- package/docs/usage/README.md +1 -1
- package/docs/usage/SELECTION.md +1 -0
- package/docs/usage/components/auth-screen-layout.md +44 -11
- package/docs/usage/components/bottom-cta.md +6 -2
- package/docs/usage/components/breadcrumb.md +5 -1
- package/docs/usage/components/chat-screen.md +1 -0
- package/docs/usage/components/checkbox-group.md +5 -1
- package/docs/usage/components/collapsible.md +1 -0
- package/docs/usage/components/comment-thread-screen.md +2 -0
- package/docs/usage/components/dialog.md +1 -0
- package/docs/usage/components/editor-screen.md +1 -0
- package/docs/usage/components/effect-surface.md +2 -2
- package/docs/usage/components/field.md +1 -0
- package/docs/usage/components/heading.md +14 -12
- package/docs/usage/components/list-detail-screen.md +1 -0
- package/docs/usage/components/list-row.md +7 -3
- package/docs/usage/components/list.md +25 -4
- package/docs/usage/components/notification-item.md +5 -1
- package/docs/usage/components/onboarding-screen.md +1 -0
- package/docs/usage/components/photo-source-sheet.md +1 -1
- package/docs/usage/components/screen-layout.md +28 -3
- package/docs/usage/components/search-field.md +1 -0
- package/docs/usage/components/search-screen.md +4 -2
- package/docs/usage/components/section.md +5 -1
- package/docs/usage/components/segmented-control.md +1 -1
- package/docs/usage/components/sheet.md +18 -13
- package/docs/usage/components/skeleton.md +42 -15
- package/docs/usage/components/statistic.md +5 -1
- package/docs/usage/components/switch.md +3 -2
- package/docs/usage/components/tag.md +5 -1
- package/docs/usage/components/text-area.md +1 -0
- package/docs/usage/components/text.md +6 -4
- package/docs/usage/components/top.md +1 -1
- package/docs/usage/compositions/adaptive-content.md +5 -1
- package/docs/usage/compositions/compound-controls.md +2 -1
- package/docs/usage/compositions/design-profile-comparison.md +1 -1
- package/docs/usage/compositions/family-drawer.md +5 -3
- package/docs/usage/compositions/input-sheet.md +1 -1
- package/docs/usage/compositions/interaction-flow-apply.md +3 -3
- package/docs/usage/compositions/photo-source.md +3 -3
- package/docs/usage/compositions/selection-motion.md +5 -1
- package/docs/usage/compositions/visual-foundations.md +5 -4
- package/docs/usage/screens/common-login.md +9 -7
- package/docs/usage/screens/landing.md +15 -14
- package/docs/usage/screens/notification-settings.md +4 -2
- package/docs/usage/tokens/typography.md +21 -13
- package/package.json +1 -1
package/dist/foundations.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"foundations.js","sourceRoot":"","sources":["../src/foundations.ts"],"names":[],"mappings":"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","sourcesContent":["/** Numeric units are consumed as CSS pixels on web and density-independent points on native. */\nexport const spacing = {\n xxs: 4,\n xs: 8,\n sm: 12,\n md: 16,\n lg: 20,\n xl: 24,\n xxl: 32,\n xxxl: 40,\n} as const;\n\nexport const radius = {\n sm: 8,\n md: 12,\n lg: 16,\n xl: 24,\n full: 999,\n} as const;\n\n/**\n * Ordered fallbacks keep the contract platform-neutral: Web renderers join\n * the list as a CSS stack, while native renderers request the first named\n * family (or their OS default mapping), rather than receiving a CSS stack.\n */\nexport const fontFamily = {\n // Pretendard is bundled by HJM (Web `@hjmds/react/fonts/pretendard.css`, Native `@hjmds/react-native/fonts`)\n // so every device renders the same face (2026-10-17: Inter/Pretendard were listed but never loaded, so the\n // result depended on what each device had installed). System fonts remain the fallback until it loads.\n ui: [\n \"Pretendard Variable\",\n \"Pretendard\",\n \"-apple-system\",\n \"BlinkMacSystemFont\",\n \"Segoe UI\",\n \"sans-serif\",\n ],\n // Chrome ignores ui-monospace and cannot address SF Mono as \"SFMono-Regular\", so name the macOS/\n // Windows monospace faces before the generic fallback (2026-10-17: Chrome rendered code in Courier).\n code: [\n \"ui-monospace\",\n \"SFMono-Regular\",\n \"SF Mono\",\n \"Menlo\",\n \"Monaco\",\n \"Cascadia Mono\",\n \"Consolas\",\n \"Liberation Mono\",\n \"monospace\",\n ],\n} as const;\n\nexport type FontFamily = keyof typeof fontFamily;\n\n/**\n * Native has no variable-weight selection on every platform, so each weight is its own registered face.\n * Keys match the asset map exported by `@hjmds/react-native/fonts`; 400 doubles as the family name inputs use.\n */\nexport const pretendardNativeFaces = {\n \"400\": \"Pretendard\",\n \"500\": \"Pretendard-Medium\",\n \"600\": \"Pretendard-SemiBold\",\n \"700\": \"Pretendard-Bold\",\n \"800\": \"Pretendard-ExtraBold\",\n} as const;\n\n/** Nearest bundled face for a CSS/RN font weight (\"normal\", \"bold\", 100–900). */\nexport function resolvePretendardNativeFace(weight: string | number | undefined): (typeof pretendardNativeFaces)[keyof typeof pretendardNativeFaces] {\n const numeric = weight === undefined || weight === \"normal\" ? 400 : weight === \"bold\" ? 700 : Number(weight);\n const steps = [400, 500, 600, 700, 800] as const;\n const nearest = Number.isFinite(numeric) ? steps.reduce((best, step) => Math.abs(step - numeric) < Math.abs(best - numeric) ? step : best, 400) : 400;\n return pretendardNativeFaces[String(nearest) as keyof typeof pretendardNativeFaces];\n}\n\n/** Display/reading roles are optional so existing ui/code product profiles retain their fonts. */\nexport type FontRole = \"ui\" | \"display\" | \"reading\" | \"code\";\nexport type FontFamilyRoles = Readonly<{ ui: readonly string[]; code: readonly string[]; display?: readonly string[]; reading?: readonly string[] }>;\n\nexport function resolveFontFamilyStack(families: FontFamilyRoles, role: FontRole): readonly string[] {\n if (![\"ui\", \"display\", \"reading\", \"code\"].includes(role)) throw new TypeError(\"Unsupported font role\");\n // Unspecified reading/display follow the current ui stack, rather than a\n // frozen neutral stack that would silently undo an app's existing ui override.\n return families[role] ?? families.ui;\n}\n\n\n/** String weights translate unchanged to CSS and React Native text styles. */\nexport const fontWeight = {\n regular: \"400\",\n medium: \"500\",\n semibold: \"600\",\n bold: \"700\",\n heavy: \"800\",\n} as const;\n\nexport type FontWeight = keyof typeof fontWeight;\nexport type FontWeightValue = (typeof fontWeight)[FontWeight];\n\n/** Numeric tracking uses CSS pixels on Web and density-independent points on Native. */\nexport const letterSpacing = {\n tight: -0.2,\n normal: 0,\n wide: 0.2,\n} as const;\n\nexport type LetterSpacing = keyof typeof letterSpacing;\n\n/** Renderers map the shared intent to `font-variant-numeric` / `fontVariant`. */\nexport const numeric = {\n proportional: \"proportional-nums\",\n tabular: \"tabular-nums\",\n} as const;\n\nexport type NumericVariant = keyof typeof numeric;\n\nexport const typography = {\n caption: { fontSize: 11, lineHeight: 16, fontWeight: fontWeight.regular },\n // Label (form labels, section subtitles) sits between body 400 and titles 600: at 600 it competed with\n // titles, and Text rendered it 400 outside a profile, too weak against body (2026-10-17 user decision).\n label: { fontSize: 12, lineHeight: 18, fontWeight: fontWeight.medium },\n body: { fontSize: 14, lineHeight: 20, fontWeight: fontWeight.regular },\n bodyLarge: { fontSize: 16, lineHeight: 24, fontWeight: fontWeight.regular },\n // Title steps are semibold (2026-10-17 user review: 800, then 700, still read too heavy on route\n // titles). Display levels 1–2 are bold, see `heading`.\n title: { fontSize: 18, lineHeight: 26, fontWeight: fontWeight.semibold },\n titleLarge: { fontSize: 20, lineHeight: 28, fontWeight: fontWeight.semibold },\n heading: { fontSize: 24, lineHeight: 32, fontWeight: fontWeight.semibold },\n} as const;\n\nexport type TextVariant = keyof typeof typography;\n\n/** Visual variants choose a default family role; semantics and sizes remain independent. */\nexport function resolveTextFontRole(variant: TextVariant, role?: FontRole): FontRole {\n if (role !== undefined) {\n if (![\"ui\", \"display\", \"reading\", \"code\"].includes(role)) throw new TypeError(\"Unsupported font role\");\n return role;\n }\n if (variant === \"body\" || variant === \"bodyLarge\") return \"reading\";\n if (variant === \"title\" || variant === \"titleLarge\" || variant === \"heading\") return \"display\";\n return \"ui\";\n}\n\n\n/**\n * Document heading hierarchy without widening the established `TextVariant`\n * union. Levels 3–5 reuse the existing semantic styles; larger levels are\n * additive display roles for dense product pages and documentation.\n */\nexport const heading = {\n // Bold, not heavy: Pretendard 800 read heavy and rough at 32–40px (2026-10-17 user decision), while\n // staying one step above the semibold title levels 3–5.\n level1: { fontSize: 40, lineHeight: 48, fontWeight: fontWeight.bold },\n level2: { fontSize: 32, lineHeight: 40, fontWeight: fontWeight.bold },\n level3: typography.heading,\n level4: typography.titleLarge,\n level5: typography.title,\n} as const;\n\nexport type HeadingLevel = keyof typeof heading;\n\n/** Glyphs include icons and avatars; they do not inherit paragraph line-height. */\nexport const glyph = {\n xs: 14,\n sm: 20,\n md: 24,\n lg: 28,\n xl: 32,\n xxl: 44,\n xxxl: 48,\n} as const;\n\nexport type GlyphSize = keyof typeof glyph;\n\nexport const motion = {\n fast: 120,\n normal: 200,\n slow: 320,\n} as const;\n\n/**\n * Curves are stored as normalized cubic-bezier tuples so CSS and native\n * animation drivers can translate the same motion intent without sharing a\n * renderer dependency.\n */\nexport const easing = {\n standard: [0.2, 0, 0, 1],\n enter: [0, 0, 0, 1],\n exit: [0.3, 0, 1, 1],\n emphasized: [0.2, 0, 0, 1],\n} as const;\n\nexport type ReducedMotionBehavior = \"instant\" | \"opacity\" | \"static\";\n\n/** Shared transition intent; renderers translate easing tuples to their animation API. */\nexport const motionPreset = {\n micro: {\n duration: motion.fast,\n easing: \"standard\",\n reducedMotion: \"instant\",\n },\n enter: {\n duration: motion.normal,\n easing: \"enter\",\n reducedMotion: \"opacity\",\n },\n exit: {\n duration: motion.fast,\n easing: \"exit\",\n reducedMotion: \"instant\",\n },\n context: {\n duration: motion.slow,\n easing: \"emphasized\",\n reducedMotion: \"opacity\",\n },\n} as const satisfies Record<\n \"micro\" | \"enter\" | \"exit\" | \"context\",\n {\n duration: number;\n easing: keyof typeof easing;\n reducedMotion: ReducedMotionBehavior;\n }\n>;\n\n/** Native renderers may use these values directly; web renderers use easing. */\nexport const spring = {\n responsive: { stiffness: 760, damping: 52, mass: 1 },\n expressive: { stiffness: 520, damping: 38, mass: 1 },\n} as const;\n\n/** Reusable state strengths. Components still decide which states are valid. */\nexport const opacity = {\n disabled: 0.5,\n muted: 0.72,\n pressed: 0.86,\n dragged: 0.64,\n} as const;\n\n/** Overlaying the content color at these strengths creates predictable states. */\nexport const stateLayer = {\n hover: 0.06,\n focus: 0.08,\n pressed: 0.1,\n selected: 0.1,\n} as const;\n\nexport const stroke = {\n subtle: 1,\n default: 1,\n strong: 2,\n focus: 2,\n} as const;\n\nexport const control = {\n minTouchTarget: 44,\n buttonHeight: {\n small: 36,\n medium: 44,\n large: 52,\n },\n buttonHitSlop: {\n small: 4,\n medium: 0,\n large: 0,\n },\n fieldHeight: 44,\n chipHeight: {\n small: 36,\n medium: 44,\n },\n selectionIndicator: 24,\n} as const;\n\n/**\n * Font scale at which components switch to a large-text layout. One decision,\n * previously two literals in `segmentedControlRecipe` and `topBarRecipe`; see\n * issue #20 for why it sits with the tokens instead of beside the scale axis.\n */\nexport const largeTextThreshold = 1.6;\n\n/** Product renderers may narrow these widths, but should not invent new rhythm. */\nexport const layout = {\n pagePadding: {\n compact: spacing.md,\n regular: spacing.lg,\n spacious: spacing.xl,\n },\n sectionGap: spacing.xl,\n contentGap: spacing.md,\n rowHeight: {\n singleLine: 56,\n twoLine: 68,\n },\n readingMaxWidth: 720,\n contentMaxWidth: 1200,\n} as const;\n\n/** Width thresholds use CSS pixels on Web and density-independent points on Native. */\nexport const breakpoint = {\n compact: 0,\n medium: 600,\n expanded: 960,\n wide: 1280,\n} as const;\n\n/** Shared stacking intent. Values remain sparse to leave room for app layers. */\nexport const layer = {\n base: 0,\n sticky: 100,\n dropdown: 400,\n overlay: 800,\n modal: 900,\n tooltip: 950,\n toast: 1000,\n} as const;\n\nexport const overlay = {\n scrim: 0.6,\n veil: 0.25,\n} as const;\n\nexport type BackdropReference = Readonly<{\n color: \"#000000\";\n opacity: number;\n}>;\n\n/** Fixed backdrop contracts avoid renderer-specific names such as CSS `scrim`. */\nexport const backdrop = {\n modal: { color: \"#000000\", opacity: overlay.scrim },\n veil: { color: \"#000000\", opacity: overlay.veil },\n} as const satisfies Record<\"modal\" | \"veil\", BackdropReference>;\n\nexport const scrim = `rgba(0, 0, 0, ${overlay.scrim})`;\n\n/** Renderers translate this contract to box-shadow or native shadow/elevation properties. */\nexport const shadow = {\n raised: {\n color: \"#000000\",\n opacity: 0.08,\n radius: 4,\n offsetY: 1,\n },\n floating: {\n color: \"#000000\",\n opacity: 0.12,\n radius: 12,\n offsetY: 4,\n },\n overlay: {\n color: \"#000000\",\n opacity: 0.16,\n radius: 24,\n offsetY: 8,\n },\n} as const;\n"]}
|
|
1
|
+
{"version":3,"file":"foundations.js","sourceRoot":"","sources":["../src/foundations.ts"],"names":[],"mappings":"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","sourcesContent":["/** Numeric units are consumed as CSS pixels on web and density-independent points on native. */\nexport const spacing = {\n xxs: 4,\n xs: 8,\n sm: 12,\n md: 16,\n lg: 20,\n xl: 24,\n xxl: 32,\n xxxl: 40,\n} as const;\n\nexport const radius = {\n sm: 8,\n md: 12,\n lg: 16,\n xl: 24,\n full: 999,\n} as const;\n\n/**\n * Ordered fallbacks keep the contract platform-neutral: Web renderers join\n * the list as a CSS stack, while native renderers request the first named\n * family (or their OS default mapping), rather than receiving a CSS stack.\n */\nexport const fontFamily = {\n // Pretendard is bundled by HJM (Web `@hjmds/react/fonts/pretendard.css`, Native `@hjmds/react-native/fonts`)\n // so every device renders the same face (2026-10-17: Inter/Pretendard were listed but never loaded, so the\n // result depended on what each device had installed). System fonts remain the fallback until it loads.\n ui: [\n \"Pretendard Variable\",\n \"Pretendard\",\n \"-apple-system\",\n \"BlinkMacSystemFont\",\n \"Segoe UI\",\n \"sans-serif\",\n ],\n // Chrome ignores ui-monospace and cannot address SF Mono as \"SFMono-Regular\", so name the macOS/\n // Windows monospace faces before the generic fallback (2026-10-17: Chrome rendered code in Courier).\n code: [\n \"ui-monospace\",\n \"SFMono-Regular\",\n \"SF Mono\",\n \"Menlo\",\n \"Monaco\",\n \"Cascadia Mono\",\n \"Consolas\",\n \"Liberation Mono\",\n \"monospace\",\n ],\n} as const;\n\nexport type FontFamily = keyof typeof fontFamily;\n\n/**\n * Native has no variable-weight selection on every platform, so each weight is its own registered face.\n * Keys match the asset map exported by `@hjmds/react-native/fonts`; 400 doubles as the family name inputs use.\n */\nexport const pretendardNativeFaces = {\n \"400\": \"Pretendard\",\n \"500\": \"Pretendard-Medium\",\n \"600\": \"Pretendard-SemiBold\",\n \"700\": \"Pretendard-Bold\",\n \"800\": \"Pretendard-ExtraBold\",\n} as const;\n\n/** Nearest bundled face for a CSS/RN font weight (\"normal\", \"bold\", 100–900). */\nexport function resolvePretendardNativeFace(weight: string | number | undefined): (typeof pretendardNativeFaces)[keyof typeof pretendardNativeFaces] {\n const numeric = weight === undefined || weight === \"normal\" ? 400 : weight === \"bold\" ? 700 : Number(weight);\n const steps = [400, 500, 600, 700, 800] as const;\n const nearest = Number.isFinite(numeric) ? steps.reduce((best, step) => Math.abs(step - numeric) < Math.abs(best - numeric) ? step : best, 400) : 400;\n return pretendardNativeFaces[String(nearest) as keyof typeof pretendardNativeFaces];\n}\n\n/** Display/reading roles are optional so existing ui/code product profiles retain their fonts. */\nexport type FontRole = \"ui\" | \"display\" | \"reading\" | \"code\";\nexport type FontFamilyRoles = Readonly<{ ui: readonly string[]; code: readonly string[]; display?: readonly string[]; reading?: readonly string[] }>;\n\nexport function resolveFontFamilyStack(families: FontFamilyRoles, role: FontRole): readonly string[] {\n if (![\"ui\", \"display\", \"reading\", \"code\"].includes(role)) throw new TypeError(\"Unsupported font role\");\n // Unspecified reading/display follow the current ui stack, rather than a\n // frozen neutral stack that would silently undo an app's existing ui override.\n return families[role] ?? families.ui;\n}\n\n\n/** String weights translate unchanged to CSS and React Native text styles. */\nexport const fontWeight = {\n regular: \"400\",\n medium: \"500\",\n semibold: \"600\",\n bold: \"700\",\n heavy: \"800\",\n} as const;\n\nexport type FontWeight = keyof typeof fontWeight;\nexport type FontWeightValue = (typeof fontWeight)[FontWeight];\n\n/** Numeric tracking uses CSS pixels on Web and density-independent points on Native. */\nexport const letterSpacing = {\n tight: -0.2,\n normal: 0,\n wide: 0.2,\n} as const;\n\nexport type LetterSpacing = keyof typeof letterSpacing;\n\n/** Renderers map the shared intent to `font-variant-numeric` / `fontVariant`. */\nexport const numeric = {\n proportional: \"proportional-nums\",\n tabular: \"tabular-nums\",\n} as const;\n\nexport type NumericVariant = keyof typeof numeric;\n\nexport const typography = {\n caption: { fontSize: 11, lineHeight: 16, fontWeight: fontWeight.regular },\n // Label (form labels, section subtitles) stays 500 between body 400 and the titles: at 600 it competed\n // with titles, and Text rendered it 400 outside a profile, too weak against body (2026-10-17 user decision).\n label: { fontSize: 12, lineHeight: 18, fontWeight: fontWeight.medium },\n body: { fontSize: 14, lineHeight: 20, fontWeight: fontWeight.regular },\n bodyLarge: { fontSize: 16, lineHeight: 24, fontWeight: fontWeight.regular },\n // Title steps are medium (2026-10-17 user review: 800, then 700, then 600 still read too heavy —\n // \"굵기 좀더 줄여\").\n title: { fontSize: 18, lineHeight: 26, fontWeight: fontWeight.medium },\n titleLarge: { fontSize: 20, lineHeight: 28, fontWeight: fontWeight.medium },\n // `heading` is the largest text HJM draws. 2026-10-17 user decision (\"글자 너무 커\", \"큰 글자 정책은 없애\"):\n // the 40/32 display tier looked far too big on the service-intro screen and was removed; design profiles\n // reject any type step above this size (design-profile.ts `merge`).\n heading: { fontSize: 24, lineHeight: 32, fontWeight: fontWeight.medium },\n} as const;\n\nexport type TextVariant = keyof typeof typography;\n\n/** Visual variants choose a default family role; semantics and sizes remain independent. */\nexport function resolveTextFontRole(variant: TextVariant, role?: FontRole): FontRole {\n if (role !== undefined) {\n if (![\"ui\", \"display\", \"reading\", \"code\"].includes(role)) throw new TypeError(\"Unsupported font role\");\n return role;\n }\n if (variant === \"body\" || variant === \"bodyLarge\") return \"reading\";\n if (variant === \"title\" || variant === \"titleLarge\" || variant === \"heading\") return \"display\";\n return \"ui\";\n}\n\n\n/**\n * Document heading hierarchy without widening the established `TextVariant`\n * union. Every level reuses a semantic text style, so no heading exceeds `typography.heading`.\n */\nexport const heading = {\n /**\n * @deprecated Same as `level3` since the display tier was removed (2026-10-17 user decision, \"큰 글자\n * 정책은 없애\"; it was 40/48/600). Kept so existing consumers keep compiling; use `level3`.\n */\n level1: typography.heading,\n /** @deprecated Same as `level3` since the display tier was removed (was 32/40/600); use `level3`. */\n level2: typography.heading,\n level3: typography.heading,\n level4: typography.titleLarge,\n level5: typography.title,\n} as const;\n\nexport type HeadingLevel = keyof typeof heading;\n\n/** Glyphs include icons and avatars; they do not inherit paragraph line-height. */\nexport const glyph = {\n xs: 14,\n sm: 20,\n md: 24,\n lg: 28,\n xl: 32,\n xxl: 44,\n xxxl: 48,\n} as const;\n\nexport type GlyphSize = keyof typeof glyph;\n\nexport const motion = {\n fast: 120,\n normal: 200,\n slow: 320,\n} as const;\n\n/**\n * Curves are stored as normalized cubic-bezier tuples so CSS and native\n * animation drivers can translate the same motion intent without sharing a\n * renderer dependency.\n */\nexport const easing = {\n standard: [0.2, 0, 0, 1],\n enter: [0, 0, 0, 1],\n exit: [0.3, 0, 1, 1],\n emphasized: [0.2, 0, 0, 1],\n} as const;\n\nexport type ReducedMotionBehavior = \"instant\" | \"opacity\" | \"static\";\n\n/** Shared transition intent; renderers translate easing tuples to their animation API. */\nexport const motionPreset = {\n micro: {\n duration: motion.fast,\n easing: \"standard\",\n reducedMotion: \"instant\",\n },\n enter: {\n duration: motion.normal,\n easing: \"enter\",\n reducedMotion: \"opacity\",\n },\n exit: {\n duration: motion.fast,\n easing: \"exit\",\n reducedMotion: \"instant\",\n },\n context: {\n duration: motion.slow,\n easing: \"emphasized\",\n reducedMotion: \"opacity\",\n },\n} as const satisfies Record<\n \"micro\" | \"enter\" | \"exit\" | \"context\",\n {\n duration: number;\n easing: keyof typeof easing;\n reducedMotion: ReducedMotionBehavior;\n }\n>;\n\n/** Native renderers may use these values directly; web renderers use easing. */\nexport const spring = {\n responsive: { stiffness: 760, damping: 52, mass: 1 },\n expressive: { stiffness: 520, damping: 38, mass: 1 },\n} as const;\n\n/** Reusable state strengths. Components still decide which states are valid. */\nexport const opacity = {\n disabled: 0.5,\n muted: 0.72,\n pressed: 0.86,\n dragged: 0.64,\n} as const;\n\n/** Overlaying the content color at these strengths creates predictable states. */\nexport const stateLayer = {\n hover: 0.06,\n focus: 0.08,\n pressed: 0.1,\n selected: 0.1,\n} as const;\n\nexport const stroke = {\n subtle: 1,\n default: 1,\n strong: 2,\n focus: 2,\n} as const;\n\nexport const control = {\n minTouchTarget: 44,\n buttonHeight: {\n small: 36,\n medium: 44,\n large: 52,\n },\n buttonHitSlop: {\n small: 4,\n medium: 0,\n large: 0,\n },\n fieldHeight: 44,\n chipHeight: {\n small: 36,\n medium: 44,\n },\n selectionIndicator: 24,\n} as const;\n\n/**\n * Font scale at which components switch to a large-text layout. One decision,\n * previously two literals in `segmentedControlRecipe` and `topBarRecipe`; see\n * issue #20 for why it sits with the tokens instead of beside the scale axis.\n */\nexport const largeTextThreshold = 1.6;\n\n/** Product renderers may narrow these widths, but should not invent new rhythm. */\nexport const layout = {\n pagePadding: {\n compact: spacing.md,\n regular: spacing.lg,\n spacious: spacing.xl,\n },\n sectionGap: spacing.xl,\n contentGap: spacing.md,\n rowHeight: {\n singleLine: 56,\n twoLine: 68,\n },\n readingMaxWidth: 720,\n contentMaxWidth: 1200,\n} as const;\n\n/** Width thresholds use CSS pixels on Web and density-independent points on Native. */\nexport const breakpoint = {\n compact: 0,\n medium: 600,\n expanded: 960,\n wide: 1280,\n} as const;\n\n/** Shared stacking intent. Values remain sparse to leave room for app layers. */\nexport const layer = {\n base: 0,\n sticky: 100,\n dropdown: 400,\n overlay: 800,\n modal: 900,\n tooltip: 950,\n toast: 1000,\n} as const;\n\nexport const overlay = {\n scrim: 0.6,\n veil: 0.25,\n} as const;\n\nexport type BackdropReference = Readonly<{\n color: \"#000000\";\n opacity: number;\n}>;\n\n/** Fixed backdrop contracts avoid renderer-specific names such as CSS `scrim`. */\nexport const backdrop = {\n modal: { color: \"#000000\", opacity: overlay.scrim },\n veil: { color: \"#000000\", opacity: overlay.veil },\n} as const satisfies Record<\"modal\" | \"veil\", BackdropReference>;\n\nexport const scrim = `rgba(0, 0, 0, ${overlay.scrim})`;\n\n/** Renderers translate this contract to box-shadow or native shadow/elevation properties. */\nexport const shadow = {\n raised: {\n color: \"#000000\",\n opacity: 0.08,\n radius: 4,\n offsetY: 1,\n },\n floating: {\n color: \"#000000\",\n opacity: 0.12,\n radius: 12,\n offsetY: 4,\n },\n overlay: {\n color: \"#000000\",\n opacity: 0.16,\n radius: 24,\n offsetY: 8,\n },\n} as const;\n"]}
|
package/dist/heading.d.ts
CHANGED
|
@@ -2,10 +2,10 @@ import { type HeadingLevel } from "./foundations.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* 문서 제목 단계를 실제로 그리는 primitive다.
|
|
4
4
|
*
|
|
5
|
-
* `heading`(foundations)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* `heading`(foundations)의 단계를 문서 제목 단계(`h1`~`h6`)와 함께 꺼내 쓰게 한다.
|
|
6
|
+
* 이 계약은 새 크기를 만들지 않는다. 가장 큰 단계는 `typography.heading` 24px이며,
|
|
7
|
+
* level1 40px·level2 32px 큰 글자 단계는 2026-10-17 사용자 결정("큰 글자 정책은 없애")으로
|
|
8
|
+
* 폐지돼 두 이름은 level3과 같은 값이다(소비 코드 호환용, 새 코드는 level3~5).
|
|
9
9
|
*
|
|
10
10
|
* `Top`(화면 첫 제목)·`Section`(본문 묶음 제목)과 겹치지 않는다. 그 둘은 **자리**를
|
|
11
11
|
* 아는 블록이고 자기 여백·보조 문장·보조 행동을 갖는다. `Heading`은 자리를 모르는
|
|
@@ -30,29 +30,29 @@ export declare const headingRecipe: {
|
|
|
30
30
|
};
|
|
31
31
|
readonly levels: {
|
|
32
32
|
readonly level1: {
|
|
33
|
-
readonly fontSize:
|
|
34
|
-
readonly lineHeight:
|
|
35
|
-
readonly fontWeight: "
|
|
33
|
+
readonly fontSize: 24;
|
|
34
|
+
readonly lineHeight: 32;
|
|
35
|
+
readonly fontWeight: "500";
|
|
36
36
|
};
|
|
37
37
|
readonly level2: {
|
|
38
|
-
readonly fontSize:
|
|
39
|
-
readonly lineHeight:
|
|
40
|
-
readonly fontWeight: "
|
|
38
|
+
readonly fontSize: 24;
|
|
39
|
+
readonly lineHeight: 32;
|
|
40
|
+
readonly fontWeight: "500";
|
|
41
41
|
};
|
|
42
42
|
readonly level3: {
|
|
43
43
|
readonly fontSize: 24;
|
|
44
44
|
readonly lineHeight: 32;
|
|
45
|
-
readonly fontWeight: "
|
|
45
|
+
readonly fontWeight: "500";
|
|
46
46
|
};
|
|
47
47
|
readonly level4: {
|
|
48
48
|
readonly fontSize: 20;
|
|
49
49
|
readonly lineHeight: 28;
|
|
50
|
-
readonly fontWeight: "
|
|
50
|
+
readonly fontWeight: "500";
|
|
51
51
|
};
|
|
52
52
|
readonly level5: {
|
|
53
53
|
readonly fontSize: 18;
|
|
54
54
|
readonly lineHeight: 26;
|
|
55
|
-
readonly fontWeight: "
|
|
55
|
+
readonly fontWeight: "500";
|
|
56
56
|
};
|
|
57
57
|
};
|
|
58
58
|
readonly color: Readonly<{
|
package/dist/heading.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heading.js","sourceRoot":"","sources":["../src/heading.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAqB,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAwBtD,MAAM,YAAY,GAAsD;IACtE,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;CACV,CAAC;AAEF,MAAM,UAAU,yBAAyB,CAAC,UAA6B;IACrE,IAAI,UAAU,KAAK,IAAI,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,SAAS,CAAC,8BAA8B,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAChF,CAAC;IACD,IACE,UAAU,CAAC,aAAa,KAAK,SAAS;QACtC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,EACtD,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,sCAAsC,MAAM,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAChG,CAAC;AACH,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,2BAA2B,CACzC,UAA6B;IAE7B,yBAAyB,CAAC,UAAU,CAAC,CAAC;IACtC,OAAO,UAAU,CAAC,aAAa,IAAI,YAAY,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,KAAK,EAAE,CAAC,MAAM,CAAU;IACxB,QAAQ,EAAE,EAAE,KAAK,EAAE,QAAwB,EAAE;IAC7C,MAAM,EAAE,OAAO;IACf,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,OAAO;IACrC,wCAAwC;IACxC,YAAY,EAAE,OAAO,CAAC,EAAE;CAOzB,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,SAAS,EAAE,EAAE;IACb,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;IACxD,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IACtD,SAAS,EAAE;QACT,+EAA+E;QAC/E,mEAAmE;QACnE,gFAAgF;QAChF,oEAAoE;KACrE;CACkC,CAAC","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport type { ColorReference } from \"./color-references.js\";\nimport { heading, spacing, type HeadingLevel } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * 문서 제목 단계를 실제로 그리는 primitive다.\n *\n * `heading`(foundations)
|
|
1
|
+
{"version":3,"file":"heading.js","sourceRoot":"","sources":["../src/heading.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAqB,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAwBtD,MAAM,YAAY,GAAsD;IACtE,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;CACV,CAAC;AAEF,MAAM,UAAU,yBAAyB,CAAC,UAA6B;IACrE,IAAI,UAAU,KAAK,IAAI,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,SAAS,CAAC,8BAA8B,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAChF,CAAC;IACD,IACE,UAAU,CAAC,aAAa,KAAK,SAAS;QACtC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,EACtD,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,sCAAsC,MAAM,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAChG,CAAC;AACH,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,2BAA2B,CACzC,UAA6B;IAE7B,yBAAyB,CAAC,UAAU,CAAC,CAAC;IACtC,OAAO,UAAU,CAAC,aAAa,IAAI,YAAY,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,KAAK,EAAE,CAAC,MAAM,CAAU;IACxB,QAAQ,EAAE,EAAE,KAAK,EAAE,QAAwB,EAAE;IAC7C,MAAM,EAAE,OAAO;IACf,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,OAAO;IACrC,wCAAwC;IACxC,YAAY,EAAE,OAAO,CAAC,EAAE;CAOzB,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,SAAS,EAAE,EAAE;IACb,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;IACxD,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IACtD,SAAS,EAAE;QACT,+EAA+E;QAC/E,mEAAmE;QACnE,gFAAgF;QAChF,oEAAoE;KACrE;CACkC,CAAC","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport type { ColorReference } from \"./color-references.js\";\nimport { heading, spacing, type HeadingLevel } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * 문서 제목 단계를 실제로 그리는 primitive다.\n *\n * `heading`(foundations)의 단계를 문서 제목 단계(`h1`~`h6`)와 함께 꺼내 쓰게 한다.\n * 이 계약은 새 크기를 만들지 않는다. 가장 큰 단계는 `typography.heading` 24px이며,\n * level1 40px·level2 32px 큰 글자 단계는 2026-10-17 사용자 결정(\"큰 글자 정책은 없애\")으로\n * 폐지돼 두 이름은 level3과 같은 값이다(소비 코드 호환용, 새 코드는 level3~5).\n *\n * `Top`(화면 첫 제목)·`Section`(본문 묶음 제목)과 겹치지 않는다. 그 둘은 **자리**를\n * 아는 블록이고 자기 여백·보조 문장·보조 행동을 갖는다. `Heading`은 자리를 모르는\n * 글자 하나다 — 카드 안, 표 위, 빈 상태 안 어디에나 놓인다.\n */\nexport type HeadingDescriptor = Readonly<{\n /** 시각적 크기. 문서 구조상의 단계와 분리돼 있다. */\n level: HeadingLevel;\n /**\n * 실제로 낼 요소 단계. 생략하면 `level`의 숫자를 따른다. 시각적 크기와 문서 구조가\n * 어긋나는 경우(카드 제목이 크지만 h4인 경우)에 따로 지정한다.\n */\n semanticLevel?: 1 | 2 | 3 | 4 | 5 | 6;\n}>;\n\nconst levelNumbers: Readonly<Record<HeadingLevel, 1 | 2 | 3 | 4 | 5>> = {\n level1: 1,\n level2: 2,\n level3: 3,\n level4: 4,\n level5: 5,\n};\n\nexport function validateHeadingDescriptor(descriptor: HeadingDescriptor): void {\n if (descriptor === null || typeof descriptor !== \"object\") {\n throw new TypeError(\"Heading descriptor must be an object\");\n }\n if (!Object.prototype.hasOwnProperty.call(heading, descriptor.level)) {\n throw new TypeError(`Unsupported Heading level: ${String(descriptor.level)}`);\n }\n if (\n descriptor.semanticLevel !== undefined &&\n ![1, 2, 3, 4, 5, 6].includes(descriptor.semanticLevel)\n ) {\n throw new TypeError(`Unsupported Heading semanticLevel: ${String(descriptor.semanticLevel)}`);\n }\n}\n\n/** 시각적 단계에서 문서 단계를 뽑되, 명시된 값이 있으면 그것이 이긴다. */\nexport function resolveHeadingSemanticLevel(\n descriptor: HeadingDescriptor,\n): 1 | 2 | 3 | 4 | 5 | 6 {\n validateHeadingDescriptor(descriptor);\n return descriptor.semanticLevel ?? levelNumbers[descriptor.level];\n}\n\nexport const headingRecipe = {\n slots: [\"root\"] as const,\n defaults: { level: \"level3\" as HeadingLevel },\n levels: heading,\n color: semanticColors.content.primary,\n /** 제목 아래 간격은 제목이 아니라 그것을 담는 블록이 정한다. */\n marginBottom: spacing.xs,\n} as const satisfies {\n slots: readonly [\"root\"];\n defaults: { level: HeadingLevel };\n levels: typeof heading;\n color: ColorReference;\n marginBottom: number;\n};\n\nexport const headingBehavior = {\n controlled: [],\n inputs: [\"level\", \"semanticLevel\"],\n stateAxes: {},\n web: { roles: [\"heading\"], keyboard: [], focus: \"none\" },\n native: { roles: [\"header\"], states: [], actions: [] },\n scenarios: [\n \"visual-size-and-document-level-are-separate-axes-that-may-disagree-on-purpose\",\n \"the-element-is-a-real-heading-so-the-rotor-and-skip-links-find-it\",\n \"no-new-type-sizes-are-introduced-the-existing-heading-scale-is-what-is-exposed\",\n \"heading-owns-no-surrounding-layout-the-block-that-contains-it-does\",\n ],\n} as const satisfies BehaviorContract;\n"]}
|
package/dist/number-field.d.ts
CHANGED
package/dist/otp-field.d.ts
CHANGED
package/dist/password-field.d.ts
CHANGED
package/dist/result.d.ts
CHANGED
package/dist/result.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { semanticColors } from "./semantic-colors.js";
|
|
2
|
-
import {
|
|
2
|
+
import { spacing, typography } from "./foundations.js";
|
|
3
3
|
export const resultDefaults = {
|
|
4
4
|
status: "info",
|
|
5
5
|
};
|
|
@@ -91,7 +91,7 @@ export const resultRecipe = {
|
|
|
91
91
|
title: {
|
|
92
92
|
textVariant: "titleLarge",
|
|
93
93
|
color: semanticColors.content.primary,
|
|
94
|
-
fontWeight: fontWeight
|
|
94
|
+
fontWeight: typography.titleLarge.fontWeight,
|
|
95
95
|
},
|
|
96
96
|
description: {
|
|
97
97
|
textVariant: "body",
|
package/dist/result.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"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","sourcesContent":["import { semanticColors } from \"./semantic-colors.js\";\nimport { spacing, typography } from \"./foundations.js\";\n\n/**\n * Web-page HTTP status vocabulary (403/404/500) does not belong in a\n * platform-neutral contract. Products/adapters translate those into\n * `failure` with their own localized copy.\n */\nexport type ResultStatus = \"success\" | \"failure\" | \"info\";\n\nexport const resultDefaults = {\n status: \"info\",\n} as const satisfies Readonly<{ status: ResultStatus }>;\n\nexport type ResultActionDescriptor = Readonly<{\n label: string;\n accessibilityLabel?: string;\n onAction(): void;\n}>;\n\n/**\n * A flow terminus screen (success, failure, 404-equivalent). Unlike\n * `EmptyState`, which describes a place that can still be filled, Result\n * means this flow ends here — there is no pending content to wait for.\n *\n * `actions` holds at most one primary and one optional secondary action;\n * the first entry is the primary. A third action is refused, not silently\n * dropped, so a screen author notices instead of shipping a truncated row.\n */\nexport type ResultDescriptor = Readonly<{\n status: ResultStatus;\n title: string;\n description?: string;\n actions?: readonly ResultActionDescriptor[];\n}>;\n\nexport type ResolvedResultActionDescriptor = Readonly<{\n label: string;\n accessibilityLabel: string;\n onAction(): void;\n}>;\n\nexport type ResolvedResultDescriptor = Readonly<{\n status: ResultStatus;\n title: string;\n description: string | null;\n primaryAction: ResolvedResultActionDescriptor | null;\n secondaryAction: ResolvedResultActionDescriptor | null;\n}>;\n\nfunction assertCopy(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Result ${field} must not be empty`);\n }\n}\n\nexport function validateResultAction(\n action: ResultActionDescriptor,\n field: string,\n): void {\n assertCopy(action.label, `${field}.label`);\n if (action.accessibilityLabel !== undefined) {\n assertCopy(action.accessibilityLabel, `${field}.accessibilityLabel`);\n }\n if (typeof action.onAction !== \"function\") {\n throw new TypeError(`Result ${field}.onAction must be a function`);\n }\n}\n\nexport function validateResultDescriptor(descriptor: ResultDescriptor): void {\n if (\n descriptor.status !== \"success\" &&\n descriptor.status !== \"failure\" &&\n descriptor.status !== \"info\"\n ) {\n throw new TypeError(`Unsupported Result status: ${String(descriptor.status)}`);\n }\n assertCopy(descriptor.title, \"title\");\n if (descriptor.description !== undefined) {\n assertCopy(descriptor.description, \"description\");\n }\n const actions = descriptor.actions ?? [];\n if (actions.length > 2) {\n throw new RangeError(\n \"Result supports at most one primary and one secondary action\",\n );\n }\n actions.forEach((action, index) => {\n validateResultAction(action, index === 0 ? \"primaryAction\" : \"secondaryAction\");\n });\n}\n\nfunction resolveAction(\n action: ResultActionDescriptor | undefined,\n): ResolvedResultActionDescriptor | null {\n if (!action) return null;\n return {\n label: action.label,\n accessibilityLabel: action.accessibilityLabel ?? action.label,\n onAction: action.onAction,\n };\n}\n\nexport function resolveResultDescriptor(\n descriptor: ResultDescriptor,\n): ResolvedResultDescriptor {\n validateResultDescriptor(descriptor);\n const actions = descriptor.actions ?? [];\n return {\n status: descriptor.status,\n title: descriptor.title,\n description: descriptor.description ?? null,\n primaryAction: resolveAction(actions[0]),\n secondaryAction: resolveAction(actions[1]),\n };\n}\n\nexport const resultRecipe = {\n slots: [\n \"root\",\n \"icon\",\n \"title\",\n \"description\",\n \"primaryAction\",\n \"secondaryAction\",\n ] as const,\n defaults: resultDefaults,\n tones: {\n success: {\n icon: semanticColors.feedback.success.foreground,\n iconBackground: semanticColors.feedback.success.badgeBackground,\n },\n failure: {\n icon: semanticColors.feedback.danger.foreground,\n iconBackground: semanticColors.feedback.danger.badgeBackground,\n },\n info: {\n icon: semanticColors.feedback.info.foreground,\n iconBackground: semanticColors.feedback.info.badgeBackground,\n },\n },\n iconSize: \"xl\",\n /**\n * Status mark drawn when the host passes no icon. Web painted these from CSS `::before` literals while Native drew\n * an empty circle (2026-10-17 review F N1); the recipe owns them so both renderers draw the same mark.\n */\n glyphs: { success: \"✓\", failure: \"!\", info: \"i\" },\n paddingVertical: spacing.xxxl,\n paddingHorizontal: spacing.xl,\n gap: spacing.sm,\n title: {\n textVariant: \"titleLarge\",\n color: semanticColors.content.primary,\n fontWeight: typography.titleLarge.fontWeight,\n },\n description: {\n textVariant: \"body\",\n color: semanticColors.content.secondary,\n },\n actionsGap: spacing.sm,\n} as const;\n"]}
|
|
@@ -56,6 +56,42 @@ export declare function validateScreenExit(exit: Readonly<{
|
|
|
56
56
|
*/
|
|
57
57
|
export type ScreenContentInset = "default" | "none" | "body-edge";
|
|
58
58
|
export declare const screenContentInsets: readonly ["default", "none", "body-edge"];
|
|
59
|
+
/**
|
|
60
|
+
* 화면 진입 시 첫 입력 포커스(2026-10-18 사용자 요청: "input이 있는 화면에 진입하면 포커스되도록, 웹앱 둘 다").
|
|
61
|
+
* 앱마다 autoFocus를 따로 붙이던 일을 화면 골격이 맡는다. 입력 칸은 가장 가까운 화면·시트 범위에 자신을 등록하고,
|
|
62
|
+
* 범위가 진입이 끝난 뒤(Native: 전환·애니메이션 interaction 뒤, Web: mount 직후) 아래 규칙으로 한 번 고른다.
|
|
63
|
+
*
|
|
64
|
+
* - `first-input`: `initialFocusTarget`을 준 칸이 있으면 그 칸, 없으면 등록 순서상 첫 칸. 입력이 화면의 주 행동인 구성
|
|
65
|
+
* (검색, 작성·편집)의 기본값이다.
|
|
66
|
+
* - `single-input`: 등록된 텍스트 입력이 정확히 하나일 때만 그 칸. 시트·대화상자의 기본값이다 — 칸이 여럿이면 어느
|
|
67
|
+
* 것이 주인지 알 수 없어 기존 포커스 규칙(첫 포커스 가능 요소·덜 파괴적인 행동)을 그대로 둔다.
|
|
68
|
+
* - `none`: 고르지 않는다. 일반 화면과, 입력이 읽기의 보조인 대화·댓글 화면의 기본값이다 — 키보드가 올라오면 읽던
|
|
69
|
+
* 기록을 가린다. 같은 prop으로 켤 수 있다.
|
|
70
|
+
*
|
|
71
|
+
* 비활성·읽기 전용 칸은 고르지 않고 `initialFocusTarget={false}` 칸은 제외한다. 화면 범위는 mount당 한 번만 포커스한다:
|
|
72
|
+
* 스택에서 돌아온 화면(이미 mount된 화면)을 다시 포커스하면 키보드가 돌아온 맥락을 가리고, HJM은 내비게이션 포커스
|
|
73
|
+
* 신호를 알지 못한다. 시트·대화상자는 열릴 때마다 다시 고른다. 모션 감소는 애니메이션에만 적용하고 포커스는 그대로 한다.
|
|
74
|
+
*/
|
|
75
|
+
export type InitialFocusMode = "first-input" | "single-input" | "none";
|
|
76
|
+
export type ScreenInitialFocus = "first-input" | "none";
|
|
77
|
+
export type InitialFocusCandidate = Readonly<{
|
|
78
|
+
/** Registration order inside the scope (tree order). */
|
|
79
|
+
order: number;
|
|
80
|
+
/** `initialFocusTarget` on the field: true prefers it, false excludes it, undefined keeps it eligible. */
|
|
81
|
+
preference: boolean | undefined;
|
|
82
|
+
/** False for disabled or read-only fields at resolve time. */
|
|
83
|
+
focusable: boolean;
|
|
84
|
+
}>;
|
|
85
|
+
/** Defaults per composition; renderers read these instead of repeating literals. */
|
|
86
|
+
export declare const initialFocusDefaults: {
|
|
87
|
+
readonly screen: "none";
|
|
88
|
+
readonly search: "first-input";
|
|
89
|
+
readonly editor: "first-input";
|
|
90
|
+
readonly chat: "none";
|
|
91
|
+
readonly commentThread: "none";
|
|
92
|
+
readonly overlay: "single-input";
|
|
93
|
+
};
|
|
94
|
+
export declare function resolveInitialFocusTarget<T extends InitialFocusCandidate>(candidates: readonly T[], mode: InitialFocusMode): T | null;
|
|
59
95
|
export declare const screenPatternRecipe: {
|
|
60
96
|
readonly maxWidth: 720;
|
|
61
97
|
readonly padding: 16;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screen-patterns.d.ts","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,0FAA0F;AAC1F,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,CAAC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,GAC3B,QAAQ,CAAC;IAAE,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE1G,wBAAgB,yBAAyB,CAAC,KAAK,GAAE,kBAAsC;;;;;EAatF;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;;;;IAM7B;;;OAGG;;;CAGK,CAAC;AAEX;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,CAAC;AAC9C,eAAO,MAAM,gBAAgB;IAC3B,gHAAgH;;;;CAIxG,CAAC;AACX,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,UAAU,EAAE,OAAO,QAItG;AAED;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,CAAC;AAClE,eAAO,MAAM,mBAAmB,2CAAoF,CAAC;
|
|
1
|
+
{"version":3,"file":"screen-patterns.d.ts","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"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"}
|
package/dist/screen-patterns.js
CHANGED
|
@@ -47,6 +47,26 @@ export function validateScreenExit(exit, hasLeading) {
|
|
|
47
47
|
throw new TypeError("Screen exit owns the leading slot; do not also pass leading");
|
|
48
48
|
}
|
|
49
49
|
export const screenContentInsets = ["default", "none", "body-edge"];
|
|
50
|
+
/** Defaults per composition; renderers read these instead of repeating literals. */
|
|
51
|
+
export const initialFocusDefaults = {
|
|
52
|
+
screen: "none",
|
|
53
|
+
search: "first-input",
|
|
54
|
+
editor: "first-input",
|
|
55
|
+
chat: "none",
|
|
56
|
+
commentThread: "none",
|
|
57
|
+
overlay: "single-input",
|
|
58
|
+
};
|
|
59
|
+
export function resolveInitialFocusTarget(candidates, mode) {
|
|
60
|
+
if (mode === "none")
|
|
61
|
+
return null;
|
|
62
|
+
const ordered = [...candidates].sort((a, b) => a.order - b.order);
|
|
63
|
+
if (mode === "single-input") {
|
|
64
|
+
const only = ordered.length === 1 ? ordered[0] : null;
|
|
65
|
+
return only && only.focusable && only.preference !== false ? only : null;
|
|
66
|
+
}
|
|
67
|
+
const eligible = ordered.filter(candidate => candidate.focusable && candidate.preference !== false);
|
|
68
|
+
return eligible.find(candidate => candidate.preference === true) ?? eligible[0] ?? null;
|
|
69
|
+
}
|
|
50
70
|
// Reuse reading-width and spacing tokens; do not derive geometry from a product's device or artwork.
|
|
51
71
|
export const screenPatternRecipe = {
|
|
52
72
|
maxWidth: layout.readingMaxWidth,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screen-patterns.js","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"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","sourcesContent":["import { glyph, layout, radius, spacing } from \"./foundations.js\";\nimport { avatarRecipe } from \"./component-recipes.js\";\nimport type { MenuItemDescriptor } from \"./behaviors.js\";\n\n/** Initial load replaces the body; refresh/save errors belong beside retained content. */\nexport type ScreenContentState =\n | Readonly<{ kind: \"ready\" }>\n | Readonly<{ kind: \"loading\" | \"empty\" | \"error\" | \"restricted\"; title: string; description?: string }>;\n\nexport function resolveScreenContentState(state: ScreenContentState = { kind: \"ready\" }) {\n if (![\"ready\", \"loading\", \"empty\", \"error\", \"restricted\"].includes(state.kind)) {\n throw new TypeError(\"Unknown screen content state\");\n }\n if (state.kind !== \"ready\" && !state.title.trim()) {\n throw new TypeError(\"Screen state requires a localized title\");\n }\n return {\n kind: state.kind,\n replacesContent: state.kind !== \"ready\",\n busy: state.kind === \"loading\",\n announcement: state.kind === \"error\" ? \"assertive\" : \"polite\",\n } as const;\n}\n\n/**\n * Native header band heights. Titled centered headers match the iOS navigation bar (44 control + 4 + 4);\n * icon-only centered headers are compact (2026-10-14 야잘알 tab spacing, 2026-10-17 \"상단 여백 줄여\").\n * Body top padding shrinks only under a compact header without a notice.\n */\nexport const screenHeaderRecipe = {\n minHeight: 52,\n paddingVertical: spacing.xxs,\n compactMinHeight: 48,\n compactPaddingVertical: 2,\n compactBodyTop: spacing.xxs,\n /**\n * Heading levels for the route title, shared by Web and Native (2026-10-17 comparison: Native drew the\n * title as regular-weight Text — 20 start / 18 centered — while Web used bold Heading levels).\n */\n titleLevel: \"level3\",\n centeredTitleLevel: \"level5\",\n} as const;\n\n/**\n * Leaving a screen (back, close, \"설정 나가기\") is an icon-only button fixed at the header's leading edge\n * (2026-10-17 user rule: exits had drifted into body text buttons with mixed glyphs). The label is read\n * by assistive tech only. `back` mirrors in RTL; `close` is for modal-style routes.\n */\nexport type ScreenExitKind = \"back\" | \"close\";\nexport const screenExitRecipe = {\n /** Chevron arm length and stroke; close cross arm length. Drawn, not font glyphs, so every platform matches. */\n chevronSize: 10,\n closeSize: 14,\n stroke: 2,\n} as const;\nexport function validateScreenExit(exit: Readonly<{ kind: string; label: string }>, hasLeading: boolean) {\n if (exit.kind !== \"back\" && exit.kind !== \"close\") throw new TypeError(\"Screen exit kind must be back or close\");\n if (!exit.label.trim()) throw new TypeError(\"Screen exit needs a localized accessible label\");\n if (hasLeading) throw new TypeError(\"Screen exit owns the leading slot; do not also pass leading\");\n}\n\n/**\n * Who owns the screen gutters. `default`: the shared shell pads header, notice, body and footer. `none`: the host\n * route already insets everything (≈45 product call sites rely on this meaning, so it cannot change).\n * `body-edge`: header, notice and footer keep the shell padding; only the body's inline padding is 0 so full-width\n * list rows (ConversationRow, ListRow-based items) supply their own inset — review S5#9 measured DM avatars ~32pt\n * from the edge because body padding and row padding stacked (2026-10-17 user decision).\n */\nexport type ScreenContentInset = \"default\" | \"none\" | \"body-edge\";\nexport const screenContentInsets = [\"default\", \"none\", \"body-edge\"] as const satisfies readonly ScreenContentInset[];\n\n// Reuse reading-width and spacing tokens; do not derive geometry from a product's device or artwork.\nexport const screenPatternRecipe = {\n maxWidth: layout.readingMaxWidth,\n padding: spacing.md,\n sectionGap: spacing.xl,\n itemGap: spacing.sm,\n stateGap: spacing.md,\n // Compact screens must keep back/title/save on one row; scale this floor for large text instead of reserving a desktop-width title.\n headerMinWidth: layout.readingMaxWidth / 6,\n // Reserve the opposite edge for authorship and cap the composer so history remains visible.\n messageMaxWidth: \"84%\",\n // Social thumbnails stay compact; selection order and removal replace a second toolbar.\n attachmentSize: 80,\n attachmentRemoveSize: 24,\n reactionHoldMs: 450,\n reactionMoveTolerance: 10,\n reactionMenuWidth: 320,\n replySwipeDistance: 60,\n composerMinLines: 1,\n composerMaxLines: 5,\n} as const;\n\n/** Controlled composer: HJM never clears drafts or interprets a fulfilled callback as server delivery. */\nexport type MessageComposerDescriptor = Readonly<{\n value: string;\n label: string;\n sendLabel: string;\n /** Short visual copy may differ from the complete accessible input name. */\n placeholder?: string;\n description?: string;\n error?: string;\n invalid?: boolean;\n /** Newline remains the compatible default; desktop chat may explicitly opt into send. */\n submitMode?: \"newline\" | \"send\";\n disabled?: boolean;\n pending?: boolean;\n attachmentCount?: number;\n}>;\n/** Safari may report the final IME Enter as keyCode 229 after isComposing becomes false. */\nexport function shouldSubmitMessageKey(event: Readonly<{ key: string; shiftKey?: boolean; isComposing?: boolean; keyCode?: number }>, mode: \"newline\" | \"send\" = \"newline\"): boolean {\n return mode === \"send\" && event.key === \"Enter\" && !event.shiftKey && !event.isComposing && event.keyCode !== 229;\n}\nexport function canSubmitMessage({ value, disabled = false, pending = false, attachmentCount = 0 }: MessageComposerDescriptor): boolean {\n if (!Number.isSafeInteger(attachmentCount) || attachmentCount < 0) throw new RangeError(\"Attachment count must be a nonnegative integer\");\n return !disabled && !pending && (value.trim().length > 0 || attachmentCount > 0);\n}\n\n/**\n * Shared timeline geometry. 2026-10-16 user decision: the common chat standard follows the measured\n * KakaoTalk iOS chat room (@3x capture: bubble ~35pt tall, radius ~14, run gap ~6, group gap ~12,\n * avatar ~34, avatar gap 8). Brand colors and artwork are not copied; surfaces stay semantic tokens.\n */\nexport const chatMessageRecipe = {\n // Must be a size the Avatar renders on both platforms: 36 was no Web Avatar step (32/40/48/64), so Web drew a 32\n // avatar in a 36 slot while Native drew 36 (2026-10-17 measured audit). `small` is the step nearest the ~34 reference.\n avatarSize: avatarRecipe.sizes.small,\n avatarGap: spacing.xs,\n authorGap: spacing.xxs,\n metaGap: spacing.xxs,\n bubbleRadius: 14,\n bubblePaddingInline: 10,\n bubblePaddingBlock: 6,\n // Bubbles of one run read as a stack; a new author or minute starts a visibly separate group.\n runGap: 6,\n groupGap: spacing.sm,\n // Flat-topped tip continuing the bubble's top edge; the tipped corner tightens to meet it.\n tailWidth: 7,\n tailHeight: 10,\n tailCornerRadius: 4,\n jumpToLatestGap: spacing.xs,\n // Timeline side gutter (measured 10pt); header and composer keep the screen padding.\n timelineInset: 10,\n // Photos render without the bubble surface (measured reference: ~210pt wide, bubble radius).\n mediaWidth: 210,\n mediaGap: 2,\n} as const;\n\n/** Column count for a photo set: pairs read as 2-up, other counts as 3-up; one photo spans the width. */\nexport function resolveChatMediaColumns(count: number): 1 | 2 | 3 {\n if (!Number.isSafeInteger(count) || count < 1) throw new RangeError(\"Chat media requires at least one item\");\n return count === 1 ? 1 : count === 2 || count === 4 ? 2 : 3;\n}\n\n/**\n * Long-press menu geometry measured from the Instagram iOS DM hold menu (2026-10-16 @3x capture):\n * full-width reaction strip ~60pt with ~30pt emoji, the pressed bubble itself as preview, and a ~200pt\n * action card aligned to the bubble's side with ~56pt rows. 12pt between the three blocks.\n */\n/** Emoji row above a comment composer (Instagram comment sheet, 2026-10-16): evenly spread 44pt targets. */\nexport const composerQuickInsertRecipe = { itemSize: 44, emojiSize: 26, gap: spacing.xs } as const;\n\nexport const holdMenuRecipe = {\n inset: 10,\n gap: spacing.sm,\n stripHeight: 60,\n stripItemSize: 48,\n emojiSize: 28,\n actionWidth: 200,\n actionRowHeight: 56,\n actionRadius: 18,\n iconSize: 24,\n} as const;\n\n/** Position inside a run of consecutive messages from one author within one displayed minute. */\nexport type ChatMessageGroupPosition = \"single\" | \"first\" | \"middle\" | \"last\";\n\n/**\n * Runs break when the author or the displayed timestamp changes, so each minute shows its time once\n * and a new minute re-introduces the author (KakaoTalk behavior). Products pass the same localized\n * timestamp string they render; HJM never parses dates.\n */\nexport function resolveChatMessageGroups(items: readonly Readonly<{ id: string; authorId: string; timestamp: string }>[]): ReadonlyMap<string, ChatMessageGroupPosition> {\n const positions = new Map<string, ChatMessageGroupPosition>();\n items.forEach((item, index) => {\n if (!item.id.trim() || positions.has(item.id)) throw new TypeError(\"Chat messages require unique, nonempty ids\");\n const same = (other: (typeof items)[number] | undefined) => !!other && other.authorId === item.authorId && other.timestamp === item.timestamp;\n const before = same(items[index - 1]), after = same(items[index + 1]);\n positions.set(item.id, before ? (after ? \"middle\" : \"last\") : after ? \"first\" : \"single\");\n });\n return positions;\n}\n\n/** What one bubble shows for its run position; both renderers read this instead of re-deriving rules. */\nexport function resolveChatMessageGroupDisplay(position: ChatMessageGroupPosition = \"single\") {\n if (![\"single\", \"first\", \"middle\", \"last\"].includes(position)) throw new TypeError(\"Unknown chat message group position\");\n const starts = position === \"single\" || position === \"first\";\n const ends = position === \"single\" || position === \"last\";\n return { showAuthor: starts, showAvatar: starts, showTail: starts, showTimestamp: ends, spacingBefore: starts ? chatMessageRecipe.groupGap : chatMessageRecipe.runGap } as const;\n}\n\n/** Bubble alignment expresses authorship, not delivery; delivery text always comes from the product receipt. */\nexport type ChatMessageDescriptor = Readonly<{\n direction: \"incoming\" | \"outgoing\";\n author: string;\n timestamp: string;\n deliveryLabel?: string;\n /** Inward drag reveals time (incoming right, outgoing left) while delivery state remains visible; default shows time beside the last bubble of a run. */\n timestampPresentation?: \"always\" | \"swipe\";\n /** Authorship tip on the run's first bubble. Default on since the 2026-10-16 timeline standard. */\n bubbleTail?: boolean;\n /** From resolveChatMessageGroups; omitted means a standalone message. */\n groupPosition?: ChatMessageGroupPosition;\n}>;\n\n/** A denied permission goes to settings; rendering never requests OS access automatically. */\nexport type PermissionScreenStatus = \"prompt\" | \"denied\" | \"granted\" | \"unavailable\";\nexport function resolvePermissionAction(status:PermissionScreenStatus) {\n switch(status){case \"prompt\":return \"request\";case \"denied\":return \"settings\";case \"granted\":return \"continue\";case \"unavailable\":return null;default:throw new TypeError(\"Unknown permission state\");}\n}\nexport function resolveOnboardingStep(total:number,index:number){\n if(!Number.isInteger(total)||total<1||!Number.isInteger(index)||index<0||index>=total)throw new RangeError(\"Onboarding requires a valid step index\");\n return {first:index===0,last:index===total-1};\n}\n\n/**\n * Shared comment-row geometry. 2026-10-16 user decision: comments follow the measured Instagram iOS\n * comment sheet (@3x capture: avatar 32 / reply 24, avatar gap 8, like count under the heart,\n * \"Reply\" as quiet text below the body, replies indented to the text column). Artwork is not copied.\n */\nexport const commentThreadRecipe = {\n avatarSize: 32,\n replyAvatarSize: 24,\n avatarGap: spacing.xs,\n lineGap: 2,\n rowGap: spacing.sm,\n // Replies align with the parent's text column instead of an arbitrary section inset.\n replyIndent: 32 + spacing.xs,\n // The heart keeps the 44pt touch target even though the measured reference draws a smaller glyph.\n likeColumnWidth: 44,\n // Optional photo/sticker under the body; the measured reference keeps it thumbnail-sized.\n mediaMaxWidth: 160,\n mediaRadius: radius.md,\n // The trailing vertical-dots menu glyph; the button keeps the shared 44pt IconButton target.\n menuGlyphSize: glyph.sm,\n /**\n * Trailing actions (like, product actions, more) centre their 44 targets on the identity's first line instead of\n * top-aligning the target box (2026-10-17 measured audit: avatar 168 / author line 162 / glyphs 174). The cluster\n * shifts by (first-line height − visible target) / 2 — negative while the line is shorter than the target — and the\n * last control takes IconButton `edge=\"end\"` so its glyph slot ends on the row inset like the avatar starts on it.\n * The first line is the author caption, or the body line when the author runs inline with `bodyText`.\n */\n actionAnchor: { authorTextVariant: \"caption\", inlineTextVariant: \"body\" },\n} as const;\n\n/**\n * Items of the HJM-owned per-comment \"more\" menu (2026-10-17: message/comment structure belongs to HJM, review S4#7).\n * Same vocabulary as Menu/ContextMenu; the product supplies only ids, localized labels, tone and availability.\n */\nexport type CommentMenuItemDescriptor<Key extends string = string> = Pick<MenuItemDescriptor<Key>, \"id\" | \"label\" | \"disabled\" | \"tone\">;\n/** Danger actions (delete, block) always sit last so a destructive row is never the first target; order is otherwise kept. */\nexport function resolveCommentMenuItems<Key extends string>(items: readonly CommentMenuItemDescriptor<Key>[]): readonly CommentMenuItemDescriptor<Key>[] {\n const ids = new Set<string>();\n for (const item of items) {\n if (!item.id.trim() || ids.has(item.id) || !item.label.trim()) throw new TypeError(\"Comment menu items require unique ids and localized labels\");\n ids.add(item.id);\n }\n return [...items.filter(item => item.tone !== \"danger\"), ...items.filter(item => item.tone === \"danger\")];\n}\n\n/** Replies are one level deep; reject orphaned/duplicate records instead of silently losing them. */\nexport function validateCommentThread(items:readonly {id:string;parentId:string|null}[]){\n const roots=new Set(items.filter(item=>item.parentId===null).map(item=>item.id));const ids=new Set<string>();\n for(const item of items){if(!item.id.trim()||ids.has(item.id))throw new TypeError(\"Comment ids must be unique and nonempty\");ids.add(item.id);if(item.parentId!==null&&(!roots.has(item.parentId)||item.parentId===item.id))throw new TypeError(\"Replies require an existing root comment\");}\n}\n\n/** Source bytes, permission requests, ordering and upload limits remain product-owned. */\nexport type MessageAttachmentDescriptor = Readonly<{ id: string; removeLabel: string; /** Local preparation can lock removal without locking text entry. */ disabled?: boolean }>;\nexport function validateMessageAttachments(attachments: readonly MessageAttachmentDescriptor[]): void {\n const ids = new Set<string>();\n for (const item of attachments) {\n if (!item.id.trim() || ids.has(item.id) || !item.removeLabel.trim()) throw new TypeError(\"Attachments require unique ids and localized removal labels\");\n ids.add(item.id);\n }\n}\n\n/** Require horizontal intent so a timeline scroll cannot accidentally start a reply. */\nexport function isReplySwipe(dx: number, dy: number): boolean {\n return Math.abs(dx) >= screenPatternRecipe.replySwipeDistance && Math.abs(dx) > Math.abs(dy) * 2;\n}\n\n/** The UI never infers permission or camera availability; product adapters own both. */\nexport type PhotoSource = \"library\" | \"camera\";\nexport type PhotoSourceLabels = Readonly<{ title:string; library:string; camera:string; cancel:string }>;\n\nexport type SavedItem = Readonly<{ id: string; title: string }>;\nexport type SavedCollection = Readonly<{ id: string; title: string; itemIds: readonly string[] }>;\nexport type SavedItemsLabels = Readonly<{ allItems: string; privateNotice: string; back: string; empty: string; createCollection: string }>;\n\n/** Unsave removes an item from every collection view without rewriting product-owned membership. */\nexport function resolveSavedItems<T extends SavedItem>(items:readonly T[],collections:readonly SavedCollection[],collectionId:string|null|undefined,selectedItemId:string|null|undefined){\n const ids=new Set<string>(),groups=new Set<string>();\n for(const item of items){if(!item.id.trim()||ids.has(item.id)||!item.title.trim())throw new TypeError(\"Saved items require unique ids and nonempty titles\");ids.add(item.id);}\n for(const group of collections){if(!group.id.trim()||groups.has(group.id)||!group.title.trim())throw new TypeError(\"Saved collections require unique ids and nonempty titles\");groups.add(group.id);}\n const collection=collections.find(item=>item.id===collectionId);\n // A deleted collection returns to the overview instead of showing a mislabeled all-posts view.\n const home=collectionId===undefined||(collectionId!==null&&!collection);\n const visible=home||collectionId===null?items:items.filter(item=>collection?.itemIds.includes(item.id));\n return {home,collection,visible,selected:home?undefined:visible.find(item=>item.id===selectedItemId)};\n}\n\n/**\n * SearchScreen phases (2026-10-06 search redesign, docs/screen-patterns.md#searchscreen-두-단계-검색).\n * Without a committed query (one-step search, the pre-1.13 behavior) any nonblank query is already\n * `results`. With one, typing a different query is `typing`: suggestions replace the results until the\n * user commits (Enter, a suggestion, a recent or suggested query). Comparing trimmed values keeps a\n * trailing space from flipping committed results back into suggestions.\n */\nexport type SearchScreenPhase = \"idle\" | \"typing\" | \"results\";\nexport function resolveSearchScreenPhase(query: string, committedQuery?: string): SearchScreenPhase {\n const typed = query.trim();\n if (!typed) return \"idle\";\n if (committedQuery === undefined) return \"results\";\n return typed === committedQuery.trim() ? \"results\" : \"typing\";\n}\n\n/**\n * The value a commit records, or `null` when there is nothing to commit. Blank commits are dropped\n * so a stray Enter never writes an empty recent search or fetches \"everything\".\n */\nexport function resolveSearchCommit(value: string): string | null {\n const next = value.trim();\n return next ? next : null;\n}\n\n/**\n * Why a committed search shows nothing. `filters` keeps the filter rail and offers \"clear all\";\n * `query` hides the rail (no filter can fix it) and offers suggested queries instead. Reference\n * services hide the rail on every zero result, which removes the only way to undo a filter.\n * `null` count means the result is still loading, so no cause is reported yet.\n */\nexport type SearchEmptyCause = \"none\" | \"filters\" | \"query\";\nexport function resolveSearchEmptyCause(count: number | null, appliedFilterCount: number): SearchEmptyCause {\n // Loading/nonempty results must not bypass the same count contract used by empty results.\n if ((count !== null && (count < 0 || !Number.isSafeInteger(count))) || !Number.isSafeInteger(appliedFilterCount) || appliedFilterCount < 0) {\n throw new RangeError(\"Search counts must be nonnegative integers\");\n }\n if (count === null || count > 0) return \"none\";\n return appliedFilterCount > 0 ? \"filters\" : \"query\";\n}\n\n/**\n * Index to focus after removing item `removedIndex` from a list that now has `remaining` items:\n * the item that slid into its place, else the new last item, else -1 (move to the fallback target).\n * Removing a focused chip or row would otherwise drop focus to <body>.\n */\nexport function resolveFocusAfterRemoval(removedIndex: number, remaining: number): number {\n if (!Number.isInteger(removedIndex) || removedIndex < 0 || !Number.isInteger(remaining) || remaining < 0) {\n throw new RangeError(\"Removal indexes must be nonnegative integers\");\n }\n return remaining === 0 ? -1 : Math.min(removedIndex, remaining - 1);\n}\n\n/** Recipe for the SearchScreen phase sections; geometry stays on shared spacing tokens. */\nexport const searchScreenRecipe = {\n /** Debounced typing delay; explicit submit remains a separate host action. */\n debounceMs: 300,\n /** Recent searches shown before the user types; reference services show 5–10 and let the rest scroll away. */\n recentVisible: 5,\n /** Suggestions under the \"search for ‘q’\" row. */\n suggestionVisible: 6,\n /** Skeleton rows while the first page of results loads. */\n loadingRows: 4,\n /** Between idle sections (recent, suggested). */\n sectionGap: spacing.xl,\n /** Section title to its list, and results header to applied filters. */\n headerGap: spacing.sm,\n /** Between chips in applied filters and suggested queries. */\n chipGap: spacing.xs,\n} as const;\n\n/** Keep finite negative delays at zero; reject values whose timer behavior varies by host. */\nexport function resolveSearchDebounceMs(value: number = searchScreenRecipe.debounceMs): number {\n if (!Number.isFinite(value)) throw new RangeError(\"Search debounceMs must be finite\");\n return Math.max(0, value);\n}\n\n/** Horizontal intent preserves vertical scrolling and uses one reveal distance across renderers. */\nexport function timestampRevealOffset(dx: number, dy: number): number {\n // A 12px dead zone and 2:1 direction bias avoid turning taps/list scrolling into a time gesture.\n return dx > 12 && dx > Math.abs(dy) * 2 ? Math.min(dx, 88) : 0;\n}\n\n/** Notification previews need an actor/type badge and a separate subject thumbnail.\n * Keep this geometry shared so inboxes do not recreate the same row per product. */\nexport const notificationRowRecipe = {\n gap: 12, contentGap: 4, avatarSize: 44, avatarBadgeSize: 20,\n thumbnailSize: 44, unreadSize: 6, previewLines: 2, paddingBlock: 12, paddingInline: 16,\n} as const;\n\n/** Shared profile identity geometry; product owns the avatar content. */\nexport const profileSummaryRecipe = Object.freeze({ avatarSize: 72 });\n\n/** Launcher artwork stays above its caption; product overlays may extend beyond the icon frame. */\nexport const iconTileRecipe = Object.freeze({ iconSize: 64, labelMaxWidth: 72, multilineLabelMaxWidth: 84, multilineLabelMinHeight: 44, ghostOpacity: 0.55, gap: 6, iconRadius: \"22%\" });\n"]}
|
|
1
|
+
{"version":3,"file":"screen-patterns.js","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"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","sourcesContent":["import { glyph, layout, radius, spacing } from \"./foundations.js\";\nimport { avatarRecipe } from \"./component-recipes.js\";\nimport type { MenuItemDescriptor } from \"./behaviors.js\";\n\n/** Initial load replaces the body; refresh/save errors belong beside retained content. */\nexport type ScreenContentState =\n | Readonly<{ kind: \"ready\" }>\n | Readonly<{ kind: \"loading\" | \"empty\" | \"error\" | \"restricted\"; title: string; description?: string }>;\n\nexport function resolveScreenContentState(state: ScreenContentState = { kind: \"ready\" }) {\n if (![\"ready\", \"loading\", \"empty\", \"error\", \"restricted\"].includes(state.kind)) {\n throw new TypeError(\"Unknown screen content state\");\n }\n if (state.kind !== \"ready\" && !state.title.trim()) {\n throw new TypeError(\"Screen state requires a localized title\");\n }\n return {\n kind: state.kind,\n replacesContent: state.kind !== \"ready\",\n busy: state.kind === \"loading\",\n announcement: state.kind === \"error\" ? \"assertive\" : \"polite\",\n } as const;\n}\n\n/**\n * Native header band heights. Titled centered headers match the iOS navigation bar (44 control + 4 + 4);\n * icon-only centered headers are compact (2026-10-14 야잘알 tab spacing, 2026-10-17 \"상단 여백 줄여\").\n * Body top padding shrinks only under a compact header without a notice.\n */\nexport const screenHeaderRecipe = {\n minHeight: 52,\n paddingVertical: spacing.xxs,\n compactMinHeight: 48,\n compactPaddingVertical: 2,\n compactBodyTop: spacing.xxs,\n /**\n * Heading levels for the route title, shared by Web and Native (2026-10-17 comparison: Native drew the\n * title as regular-weight Text — 20 start / 18 centered — while Web used bold Heading levels).\n */\n titleLevel: \"level3\",\n centeredTitleLevel: \"level5\",\n} as const;\n\n/**\n * Leaving a screen (back, close, \"설정 나가기\") is an icon-only button fixed at the header's leading edge\n * (2026-10-17 user rule: exits had drifted into body text buttons with mixed glyphs). The label is read\n * by assistive tech only. `back` mirrors in RTL; `close` is for modal-style routes.\n */\nexport type ScreenExitKind = \"back\" | \"close\";\nexport const screenExitRecipe = {\n /** Chevron arm length and stroke; close cross arm length. Drawn, not font glyphs, so every platform matches. */\n chevronSize: 10,\n closeSize: 14,\n stroke: 2,\n} as const;\nexport function validateScreenExit(exit: Readonly<{ kind: string; label: string }>, hasLeading: boolean) {\n if (exit.kind !== \"back\" && exit.kind !== \"close\") throw new TypeError(\"Screen exit kind must be back or close\");\n if (!exit.label.trim()) throw new TypeError(\"Screen exit needs a localized accessible label\");\n if (hasLeading) throw new TypeError(\"Screen exit owns the leading slot; do not also pass leading\");\n}\n\n/**\n * Who owns the screen gutters. `default`: the shared shell pads header, notice, body and footer. `none`: the host\n * route already insets everything (≈45 product call sites rely on this meaning, so it cannot change).\n * `body-edge`: header, notice and footer keep the shell padding; only the body's inline padding is 0 so full-width\n * list rows (ConversationRow, ListRow-based items) supply their own inset — review S5#9 measured DM avatars ~32pt\n * from the edge because body padding and row padding stacked (2026-10-17 user decision).\n */\nexport type ScreenContentInset = \"default\" | \"none\" | \"body-edge\";\nexport const screenContentInsets = [\"default\", \"none\", \"body-edge\"] as const satisfies readonly ScreenContentInset[];\n\n/**\n * 화면 진입 시 첫 입력 포커스(2026-10-18 사용자 요청: \"input이 있는 화면에 진입하면 포커스되도록, 웹앱 둘 다\").\n * 앱마다 autoFocus를 따로 붙이던 일을 화면 골격이 맡는다. 입력 칸은 가장 가까운 화면·시트 범위에 자신을 등록하고,\n * 범위가 진입이 끝난 뒤(Native: 전환·애니메이션 interaction 뒤, Web: mount 직후) 아래 규칙으로 한 번 고른다.\n *\n * - `first-input`: `initialFocusTarget`을 준 칸이 있으면 그 칸, 없으면 등록 순서상 첫 칸. 입력이 화면의 주 행동인 구성\n * (검색, 작성·편집)의 기본값이다.\n * - `single-input`: 등록된 텍스트 입력이 정확히 하나일 때만 그 칸. 시트·대화상자의 기본값이다 — 칸이 여럿이면 어느\n * 것이 주인지 알 수 없어 기존 포커스 규칙(첫 포커스 가능 요소·덜 파괴적인 행동)을 그대로 둔다.\n * - `none`: 고르지 않는다. 일반 화면과, 입력이 읽기의 보조인 대화·댓글 화면의 기본값이다 — 키보드가 올라오면 읽던\n * 기록을 가린다. 같은 prop으로 켤 수 있다.\n *\n * 비활성·읽기 전용 칸은 고르지 않고 `initialFocusTarget={false}` 칸은 제외한다. 화면 범위는 mount당 한 번만 포커스한다:\n * 스택에서 돌아온 화면(이미 mount된 화면)을 다시 포커스하면 키보드가 돌아온 맥락을 가리고, HJM은 내비게이션 포커스\n * 신호를 알지 못한다. 시트·대화상자는 열릴 때마다 다시 고른다. 모션 감소는 애니메이션에만 적용하고 포커스는 그대로 한다.\n */\nexport type InitialFocusMode = \"first-input\" | \"single-input\" | \"none\";\nexport type ScreenInitialFocus = \"first-input\" | \"none\";\nexport type InitialFocusCandidate = Readonly<{\n /** Registration order inside the scope (tree order). */\n order: number;\n /** `initialFocusTarget` on the field: true prefers it, false excludes it, undefined keeps it eligible. */\n preference: boolean | undefined;\n /** False for disabled or read-only fields at resolve time. */\n focusable: boolean;\n}>;\n\n/** Defaults per composition; renderers read these instead of repeating literals. */\nexport const initialFocusDefaults = {\n screen: \"none\",\n search: \"first-input\",\n editor: \"first-input\",\n chat: \"none\",\n commentThread: \"none\",\n overlay: \"single-input\",\n} as const satisfies Record<string, InitialFocusMode>;\n\nexport function resolveInitialFocusTarget<T extends InitialFocusCandidate>(candidates: readonly T[], mode: InitialFocusMode): T | null {\n if (mode === \"none\") return null;\n const ordered = [...candidates].sort((a, b) => a.order - b.order);\n if (mode === \"single-input\") {\n const only = ordered.length === 1 ? ordered[0]! : null;\n return only && only.focusable && only.preference !== false ? only : null;\n }\n const eligible = ordered.filter(candidate => candidate.focusable && candidate.preference !== false);\n return eligible.find(candidate => candidate.preference === true) ?? eligible[0] ?? null;\n}\n\n// Reuse reading-width and spacing tokens; do not derive geometry from a product's device or artwork.\nexport const screenPatternRecipe = {\n maxWidth: layout.readingMaxWidth,\n padding: spacing.md,\n sectionGap: spacing.xl,\n itemGap: spacing.sm,\n stateGap: spacing.md,\n // Compact screens must keep back/title/save on one row; scale this floor for large text instead of reserving a desktop-width title.\n headerMinWidth: layout.readingMaxWidth / 6,\n // Reserve the opposite edge for authorship and cap the composer so history remains visible.\n messageMaxWidth: \"84%\",\n // Social thumbnails stay compact; selection order and removal replace a second toolbar.\n attachmentSize: 80,\n attachmentRemoveSize: 24,\n reactionHoldMs: 450,\n reactionMoveTolerance: 10,\n reactionMenuWidth: 320,\n replySwipeDistance: 60,\n composerMinLines: 1,\n composerMaxLines: 5,\n} as const;\n\n/** Controlled composer: HJM never clears drafts or interprets a fulfilled callback as server delivery. */\nexport type MessageComposerDescriptor = Readonly<{\n value: string;\n label: string;\n sendLabel: string;\n /** Short visual copy may differ from the complete accessible input name. */\n placeholder?: string;\n description?: string;\n error?: string;\n invalid?: boolean;\n /** Newline remains the compatible default; desktop chat may explicitly opt into send. */\n submitMode?: \"newline\" | \"send\";\n disabled?: boolean;\n pending?: boolean;\n attachmentCount?: number;\n}>;\n/** Safari may report the final IME Enter as keyCode 229 after isComposing becomes false. */\nexport function shouldSubmitMessageKey(event: Readonly<{ key: string; shiftKey?: boolean; isComposing?: boolean; keyCode?: number }>, mode: \"newline\" | \"send\" = \"newline\"): boolean {\n return mode === \"send\" && event.key === \"Enter\" && !event.shiftKey && !event.isComposing && event.keyCode !== 229;\n}\nexport function canSubmitMessage({ value, disabled = false, pending = false, attachmentCount = 0 }: MessageComposerDescriptor): boolean {\n if (!Number.isSafeInteger(attachmentCount) || attachmentCount < 0) throw new RangeError(\"Attachment count must be a nonnegative integer\");\n return !disabled && !pending && (value.trim().length > 0 || attachmentCount > 0);\n}\n\n/**\n * Shared timeline geometry. 2026-10-16 user decision: the common chat standard follows the measured\n * KakaoTalk iOS chat room (@3x capture: bubble ~35pt tall, radius ~14, run gap ~6, group gap ~12,\n * avatar ~34, avatar gap 8). Brand colors and artwork are not copied; surfaces stay semantic tokens.\n */\nexport const chatMessageRecipe = {\n // Must be a size the Avatar renders on both platforms: 36 was no Web Avatar step (32/40/48/64), so Web drew a 32\n // avatar in a 36 slot while Native drew 36 (2026-10-17 measured audit). `small` is the step nearest the ~34 reference.\n avatarSize: avatarRecipe.sizes.small,\n avatarGap: spacing.xs,\n authorGap: spacing.xxs,\n metaGap: spacing.xxs,\n bubbleRadius: 14,\n bubblePaddingInline: 10,\n bubblePaddingBlock: 6,\n // Bubbles of one run read as a stack; a new author or minute starts a visibly separate group.\n runGap: 6,\n groupGap: spacing.sm,\n // Flat-topped tip continuing the bubble's top edge; the tipped corner tightens to meet it.\n tailWidth: 7,\n tailHeight: 10,\n tailCornerRadius: 4,\n jumpToLatestGap: spacing.xs,\n // Timeline side gutter (measured 10pt); header and composer keep the screen padding.\n timelineInset: 10,\n // Photos render without the bubble surface (measured reference: ~210pt wide, bubble radius).\n mediaWidth: 210,\n mediaGap: 2,\n} as const;\n\n/** Column count for a photo set: pairs read as 2-up, other counts as 3-up; one photo spans the width. */\nexport function resolveChatMediaColumns(count: number): 1 | 2 | 3 {\n if (!Number.isSafeInteger(count) || count < 1) throw new RangeError(\"Chat media requires at least one item\");\n return count === 1 ? 1 : count === 2 || count === 4 ? 2 : 3;\n}\n\n/**\n * Long-press menu geometry measured from the Instagram iOS DM hold menu (2026-10-16 @3x capture):\n * full-width reaction strip ~60pt with ~30pt emoji, the pressed bubble itself as preview, and a ~200pt\n * action card aligned to the bubble's side with ~56pt rows. 12pt between the three blocks.\n */\n/** Emoji row above a comment composer (Instagram comment sheet, 2026-10-16): evenly spread 44pt targets. */\nexport const composerQuickInsertRecipe = { itemSize: 44, emojiSize: 26, gap: spacing.xs } as const;\n\nexport const holdMenuRecipe = {\n inset: 10,\n gap: spacing.sm,\n stripHeight: 60,\n stripItemSize: 48,\n emojiSize: 28,\n actionWidth: 200,\n actionRowHeight: 56,\n actionRadius: 18,\n iconSize: 24,\n} as const;\n\n/** Position inside a run of consecutive messages from one author within one displayed minute. */\nexport type ChatMessageGroupPosition = \"single\" | \"first\" | \"middle\" | \"last\";\n\n/**\n * Runs break when the author or the displayed timestamp changes, so each minute shows its time once\n * and a new minute re-introduces the author (KakaoTalk behavior). Products pass the same localized\n * timestamp string they render; HJM never parses dates.\n */\nexport function resolveChatMessageGroups(items: readonly Readonly<{ id: string; authorId: string; timestamp: string }>[]): ReadonlyMap<string, ChatMessageGroupPosition> {\n const positions = new Map<string, ChatMessageGroupPosition>();\n items.forEach((item, index) => {\n if (!item.id.trim() || positions.has(item.id)) throw new TypeError(\"Chat messages require unique, nonempty ids\");\n const same = (other: (typeof items)[number] | undefined) => !!other && other.authorId === item.authorId && other.timestamp === item.timestamp;\n const before = same(items[index - 1]), after = same(items[index + 1]);\n positions.set(item.id, before ? (after ? \"middle\" : \"last\") : after ? \"first\" : \"single\");\n });\n return positions;\n}\n\n/** What one bubble shows for its run position; both renderers read this instead of re-deriving rules. */\nexport function resolveChatMessageGroupDisplay(position: ChatMessageGroupPosition = \"single\") {\n if (![\"single\", \"first\", \"middle\", \"last\"].includes(position)) throw new TypeError(\"Unknown chat message group position\");\n const starts = position === \"single\" || position === \"first\";\n const ends = position === \"single\" || position === \"last\";\n return { showAuthor: starts, showAvatar: starts, showTail: starts, showTimestamp: ends, spacingBefore: starts ? chatMessageRecipe.groupGap : chatMessageRecipe.runGap } as const;\n}\n\n/** Bubble alignment expresses authorship, not delivery; delivery text always comes from the product receipt. */\nexport type ChatMessageDescriptor = Readonly<{\n direction: \"incoming\" | \"outgoing\";\n author: string;\n timestamp: string;\n deliveryLabel?: string;\n /** Inward drag reveals time (incoming right, outgoing left) while delivery state remains visible; default shows time beside the last bubble of a run. */\n timestampPresentation?: \"always\" | \"swipe\";\n /** Authorship tip on the run's first bubble. Default on since the 2026-10-16 timeline standard. */\n bubbleTail?: boolean;\n /** From resolveChatMessageGroups; omitted means a standalone message. */\n groupPosition?: ChatMessageGroupPosition;\n}>;\n\n/** A denied permission goes to settings; rendering never requests OS access automatically. */\nexport type PermissionScreenStatus = \"prompt\" | \"denied\" | \"granted\" | \"unavailable\";\nexport function resolvePermissionAction(status:PermissionScreenStatus) {\n switch(status){case \"prompt\":return \"request\";case \"denied\":return \"settings\";case \"granted\":return \"continue\";case \"unavailable\":return null;default:throw new TypeError(\"Unknown permission state\");}\n}\nexport function resolveOnboardingStep(total:number,index:number){\n if(!Number.isInteger(total)||total<1||!Number.isInteger(index)||index<0||index>=total)throw new RangeError(\"Onboarding requires a valid step index\");\n return {first:index===0,last:index===total-1};\n}\n\n/**\n * Shared comment-row geometry. 2026-10-16 user decision: comments follow the measured Instagram iOS\n * comment sheet (@3x capture: avatar 32 / reply 24, avatar gap 8, like count under the heart,\n * \"Reply\" as quiet text below the body, replies indented to the text column). Artwork is not copied.\n */\nexport const commentThreadRecipe = {\n avatarSize: 32,\n replyAvatarSize: 24,\n avatarGap: spacing.xs,\n lineGap: 2,\n rowGap: spacing.sm,\n // Replies align with the parent's text column instead of an arbitrary section inset.\n replyIndent: 32 + spacing.xs,\n // The heart keeps the 44pt touch target even though the measured reference draws a smaller glyph.\n likeColumnWidth: 44,\n // Optional photo/sticker under the body; the measured reference keeps it thumbnail-sized.\n mediaMaxWidth: 160,\n mediaRadius: radius.md,\n // The trailing vertical-dots menu glyph; the button keeps the shared 44pt IconButton target.\n menuGlyphSize: glyph.sm,\n /**\n * Trailing actions (like, product actions, more) centre their 44 targets on the identity's first line instead of\n * top-aligning the target box (2026-10-17 measured audit: avatar 168 / author line 162 / glyphs 174). The cluster\n * shifts by (first-line height − visible target) / 2 — negative while the line is shorter than the target — and the\n * last control takes IconButton `edge=\"end\"` so its glyph slot ends on the row inset like the avatar starts on it.\n * The first line is the author caption, or the body line when the author runs inline with `bodyText`.\n */\n actionAnchor: { authorTextVariant: \"caption\", inlineTextVariant: \"body\" },\n} as const;\n\n/**\n * Items of the HJM-owned per-comment \"more\" menu (2026-10-17: message/comment structure belongs to HJM, review S4#7).\n * Same vocabulary as Menu/ContextMenu; the product supplies only ids, localized labels, tone and availability.\n */\nexport type CommentMenuItemDescriptor<Key extends string = string> = Pick<MenuItemDescriptor<Key>, \"id\" | \"label\" | \"disabled\" | \"tone\">;\n/** Danger actions (delete, block) always sit last so a destructive row is never the first target; order is otherwise kept. */\nexport function resolveCommentMenuItems<Key extends string>(items: readonly CommentMenuItemDescriptor<Key>[]): readonly CommentMenuItemDescriptor<Key>[] {\n const ids = new Set<string>();\n for (const item of items) {\n if (!item.id.trim() || ids.has(item.id) || !item.label.trim()) throw new TypeError(\"Comment menu items require unique ids and localized labels\");\n ids.add(item.id);\n }\n return [...items.filter(item => item.tone !== \"danger\"), ...items.filter(item => item.tone === \"danger\")];\n}\n\n/** Replies are one level deep; reject orphaned/duplicate records instead of silently losing them. */\nexport function validateCommentThread(items:readonly {id:string;parentId:string|null}[]){\n const roots=new Set(items.filter(item=>item.parentId===null).map(item=>item.id));const ids=new Set<string>();\n for(const item of items){if(!item.id.trim()||ids.has(item.id))throw new TypeError(\"Comment ids must be unique and nonempty\");ids.add(item.id);if(item.parentId!==null&&(!roots.has(item.parentId)||item.parentId===item.id))throw new TypeError(\"Replies require an existing root comment\");}\n}\n\n/** Source bytes, permission requests, ordering and upload limits remain product-owned. */\nexport type MessageAttachmentDescriptor = Readonly<{ id: string; removeLabel: string; /** Local preparation can lock removal without locking text entry. */ disabled?: boolean }>;\nexport function validateMessageAttachments(attachments: readonly MessageAttachmentDescriptor[]): void {\n const ids = new Set<string>();\n for (const item of attachments) {\n if (!item.id.trim() || ids.has(item.id) || !item.removeLabel.trim()) throw new TypeError(\"Attachments require unique ids and localized removal labels\");\n ids.add(item.id);\n }\n}\n\n/** Require horizontal intent so a timeline scroll cannot accidentally start a reply. */\nexport function isReplySwipe(dx: number, dy: number): boolean {\n return Math.abs(dx) >= screenPatternRecipe.replySwipeDistance && Math.abs(dx) > Math.abs(dy) * 2;\n}\n\n/** The UI never infers permission or camera availability; product adapters own both. */\nexport type PhotoSource = \"library\" | \"camera\";\nexport type PhotoSourceLabels = Readonly<{ title:string; library:string; camera:string; cancel:string }>;\n\nexport type SavedItem = Readonly<{ id: string; title: string }>;\nexport type SavedCollection = Readonly<{ id: string; title: string; itemIds: readonly string[] }>;\nexport type SavedItemsLabels = Readonly<{ allItems: string; privateNotice: string; back: string; empty: string; createCollection: string }>;\n\n/** Unsave removes an item from every collection view without rewriting product-owned membership. */\nexport function resolveSavedItems<T extends SavedItem>(items:readonly T[],collections:readonly SavedCollection[],collectionId:string|null|undefined,selectedItemId:string|null|undefined){\n const ids=new Set<string>(),groups=new Set<string>();\n for(const item of items){if(!item.id.trim()||ids.has(item.id)||!item.title.trim())throw new TypeError(\"Saved items require unique ids and nonempty titles\");ids.add(item.id);}\n for(const group of collections){if(!group.id.trim()||groups.has(group.id)||!group.title.trim())throw new TypeError(\"Saved collections require unique ids and nonempty titles\");groups.add(group.id);}\n const collection=collections.find(item=>item.id===collectionId);\n // A deleted collection returns to the overview instead of showing a mislabeled all-posts view.\n const home=collectionId===undefined||(collectionId!==null&&!collection);\n const visible=home||collectionId===null?items:items.filter(item=>collection?.itemIds.includes(item.id));\n return {home,collection,visible,selected:home?undefined:visible.find(item=>item.id===selectedItemId)};\n}\n\n/**\n * SearchScreen phases (2026-10-06 search redesign, docs/screen-patterns.md#searchscreen-두-단계-검색).\n * Without a committed query (one-step search, the pre-1.13 behavior) any nonblank query is already\n * `results`. With one, typing a different query is `typing`: suggestions replace the results until the\n * user commits (Enter, a suggestion, a recent or suggested query). Comparing trimmed values keeps a\n * trailing space from flipping committed results back into suggestions.\n */\nexport type SearchScreenPhase = \"idle\" | \"typing\" | \"results\";\nexport function resolveSearchScreenPhase(query: string, committedQuery?: string): SearchScreenPhase {\n const typed = query.trim();\n if (!typed) return \"idle\";\n if (committedQuery === undefined) return \"results\";\n return typed === committedQuery.trim() ? \"results\" : \"typing\";\n}\n\n/**\n * The value a commit records, or `null` when there is nothing to commit. Blank commits are dropped\n * so a stray Enter never writes an empty recent search or fetches \"everything\".\n */\nexport function resolveSearchCommit(value: string): string | null {\n const next = value.trim();\n return next ? next : null;\n}\n\n/**\n * Why a committed search shows nothing. `filters` keeps the filter rail and offers \"clear all\";\n * `query` hides the rail (no filter can fix it) and offers suggested queries instead. Reference\n * services hide the rail on every zero result, which removes the only way to undo a filter.\n * `null` count means the result is still loading, so no cause is reported yet.\n */\nexport type SearchEmptyCause = \"none\" | \"filters\" | \"query\";\nexport function resolveSearchEmptyCause(count: number | null, appliedFilterCount: number): SearchEmptyCause {\n // Loading/nonempty results must not bypass the same count contract used by empty results.\n if ((count !== null && (count < 0 || !Number.isSafeInteger(count))) || !Number.isSafeInteger(appliedFilterCount) || appliedFilterCount < 0) {\n throw new RangeError(\"Search counts must be nonnegative integers\");\n }\n if (count === null || count > 0) return \"none\";\n return appliedFilterCount > 0 ? \"filters\" : \"query\";\n}\n\n/**\n * Index to focus after removing item `removedIndex` from a list that now has `remaining` items:\n * the item that slid into its place, else the new last item, else -1 (move to the fallback target).\n * Removing a focused chip or row would otherwise drop focus to <body>.\n */\nexport function resolveFocusAfterRemoval(removedIndex: number, remaining: number): number {\n if (!Number.isInteger(removedIndex) || removedIndex < 0 || !Number.isInteger(remaining) || remaining < 0) {\n throw new RangeError(\"Removal indexes must be nonnegative integers\");\n }\n return remaining === 0 ? -1 : Math.min(removedIndex, remaining - 1);\n}\n\n/** Recipe for the SearchScreen phase sections; geometry stays on shared spacing tokens. */\nexport const searchScreenRecipe = {\n /** Debounced typing delay; explicit submit remains a separate host action. */\n debounceMs: 300,\n /** Recent searches shown before the user types; reference services show 5–10 and let the rest scroll away. */\n recentVisible: 5,\n /** Suggestions under the \"search for ‘q’\" row. */\n suggestionVisible: 6,\n /** Skeleton rows while the first page of results loads. */\n loadingRows: 4,\n /** Between idle sections (recent, suggested). */\n sectionGap: spacing.xl,\n /** Section title to its list, and results header to applied filters. */\n headerGap: spacing.sm,\n /** Between chips in applied filters and suggested queries. */\n chipGap: spacing.xs,\n} as const;\n\n/** Keep finite negative delays at zero; reject values whose timer behavior varies by host. */\nexport function resolveSearchDebounceMs(value: number = searchScreenRecipe.debounceMs): number {\n if (!Number.isFinite(value)) throw new RangeError(\"Search debounceMs must be finite\");\n return Math.max(0, value);\n}\n\n/** Horizontal intent preserves vertical scrolling and uses one reveal distance across renderers. */\nexport function timestampRevealOffset(dx: number, dy: number): number {\n // A 12px dead zone and 2:1 direction bias avoid turning taps/list scrolling into a time gesture.\n return dx > 12 && dx > Math.abs(dy) * 2 ? Math.min(dx, 88) : 0;\n}\n\n/** Notification previews need an actor/type badge and a separate subject thumbnail.\n * Keep this geometry shared so inboxes do not recreate the same row per product. */\nexport const notificationRowRecipe = {\n gap: 12, contentGap: 4, avatarSize: 44, avatarBadgeSize: 20,\n thumbnailSize: 44, unreadSize: 6, previewLines: 2, paddingBlock: 12, paddingInline: 16,\n} as const;\n\n/** Shared profile identity geometry; product owns the avatar content. */\nexport const profileSummaryRecipe = Object.freeze({ avatarSize: 72 });\n\n/** Launcher artwork stays above its caption; product overlays may extend beyond the icon frame. */\nexport const iconTileRecipe = Object.freeze({ iconSize: 64, labelMaxWidth: 72, multilineLabelMaxWidth: 84, multilineLabelMinHeight: 44, ghostOpacity: 0.55, gap: 6, iconRadius: \"22%\" });\n"]}
|
|
@@ -29,6 +29,16 @@ export declare const semanticColors: {
|
|
|
29
29
|
key: "surfaceAccent";
|
|
30
30
|
alpha?: number;
|
|
31
31
|
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Loading placeholder fill (Skeleton, ListRow `loading`). A translucent text tint instead of an opaque
|
|
34
|
+
* neutral: it darkens/lightens whatever it sits on, so it stays visible on canvas, Card surfaces and
|
|
35
|
+
* profile palettes whose `surfaceAlt` is as light as `bg` (2026-10-17 Skeleton review, skeleton.md 근거).
|
|
36
|
+
*/
|
|
37
|
+
readonly placeholder: Readonly<{
|
|
38
|
+
source: "theme";
|
|
39
|
+
key: "text";
|
|
40
|
+
alpha?: number;
|
|
41
|
+
}>;
|
|
32
42
|
};
|
|
33
43
|
readonly content: {
|
|
34
44
|
readonly primary: Readonly<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic-colors.d.ts","sourceRoot":"","sources":["../src/semantic-colors.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,cAAc
|
|
1
|
+
{"version":3,"file":"semantic-colors.d.ts","sourceRoot":"","sources":["../src/semantic-colors.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;QAOvB;;;;WAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAgBH,iDAAiD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA4DjD;;;WAGG;;;;;;;CAGG,CAAC"}
|
package/dist/semantic-colors.js
CHANGED
|
@@ -10,6 +10,12 @@ export const semanticColors = {
|
|
|
10
10
|
sunken: themeColor("surfaceAlt"),
|
|
11
11
|
raised: themeColor("bg"),
|
|
12
12
|
brand: themeColor("surfaceAccent"),
|
|
13
|
+
/**
|
|
14
|
+
* Loading placeholder fill (Skeleton, ListRow `loading`). A translucent text tint instead of an opaque
|
|
15
|
+
* neutral: it darkens/lightens whatever it sits on, so it stays visible on canvas, Card surfaces and
|
|
16
|
+
* profile palettes whose `surfaceAlt` is as light as `bg` (2026-10-17 Skeleton review, skeleton.md 근거).
|
|
17
|
+
*/
|
|
18
|
+
placeholder: themeColor("text", 0.12),
|
|
13
19
|
},
|
|
14
20
|
content: {
|
|
15
21
|
primary: themeColor("text"),
|