@hjmds/design-contracts 1.12.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avatar-fallback.d.ts +11 -0
- package/dist/avatar-fallback.d.ts.map +1 -1
- package/dist/avatar-fallback.js +21 -0
- package/dist/avatar-fallback.js.map +1 -1
- package/dist/base-recipes.d.ts +17 -0
- package/dist/base-recipes.d.ts.map +1 -1
- package/dist/base-recipes.js +17 -0
- package/dist/base-recipes.js.map +1 -1
- package/dist/catalog.d.ts +25 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/command-palette.d.ts +14 -9
- package/dist/command-palette.d.ts.map +1 -1
- package/dist/command-palette.js +8 -9
- package/dist/command-palette.js.map +1 -1
- package/dist/component-recipes.d.ts +17 -0
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +5 -0
- package/dist/component-recipes.js.map +1 -1
- package/dist/provider-button.d.ts.map +1 -1
- package/dist/provider-button.js +3 -0
- package/dist/provider-button.js.map +1 -1
- package/dist/reactions.d.ts +10 -0
- package/dist/reactions.d.ts.map +1 -1
- package/dist/reactions.js +7 -0
- package/dist/reactions.js.map +1 -1
- package/dist/screen-patterns.d.ts +147 -0
- package/dist/screen-patterns.d.ts.map +1 -0
- package/dist/screen-patterns.js +149 -0
- package/dist/screen-patterns.js.map +1 -0
- package/dist/slider.d.ts +8 -0
- package/dist/slider.d.ts.map +1 -1
- package/dist/slider.js +6 -1
- package/dist/slider.js.map +1 -1
- package/dist/upload-item.d.ts +5 -0
- package/dist/upload-item.d.ts.map +1 -1
- package/dist/upload-item.js +5 -0
- package/dist/upload-item.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/action-session.md +3 -3
- package/docs/agreement.md +5 -0
- package/docs/avatar-fallback.md +7 -0
- package/docs/bottom-navigation.md +6 -0
- package/docs/brand-boundary.md +1 -1
- package/docs/button-label.md +6 -0
- package/docs/clipboard.md +3 -0
- package/docs/command-palette.md +45 -2
- package/docs/consumer-policy.md +5 -1
- package/docs/data-table.md +6 -4
- package/docs/dialog.md +8 -2
- package/docs/form.md +51 -0
- package/docs/generated/component-maturity.md +1 -1
- package/docs/generated/renderer-evidence.json +3 -3
- package/docs/generated/renderer-evidence.md +1 -1
- package/docs/generated/showcase-manifest.json +1 -1
- package/docs/link.md +8 -0
- package/docs/migration-native-legacy-removal.md +45 -1
- package/docs/optional-adapters.md +1 -1
- package/docs/password-field.md +5 -0
- package/docs/product-composition-adoption.md +40 -0
- package/docs/progress.md +19 -1
- package/docs/provider-button.md +13 -0
- package/docs/result.md +3 -0
- package/docs/screen-chrome.md +10 -0
- package/docs/screen-patterns.md +376 -0
- package/docs/sheet.md +12 -0
- package/docs/splitter.md +8 -2
- package/docs/theming.md +36 -29
- package/docs/toggle-group.md +7 -0
- package/docs/tour.md +7 -1
- package/docs/tree.md +5 -2
- package/docs/upload-item.md +7 -0
- package/docs/usage/README.md +236 -0
- package/docs/usage/STANDARD.md +108 -0
- package/docs/usage/components/accordion.md +107 -0
- package/docs/usage/components/activity-heatmap.md +104 -0
- package/docs/usage/components/affix.md +86 -0
- package/docs/usage/components/agreement.md +129 -0
- package/docs/usage/components/alert-dialog.md +130 -0
- package/docs/usage/components/anchor.md +96 -0
- package/docs/usage/components/aspect-ratio.md +89 -0
- package/docs/usage/components/asset.md +126 -0
- package/docs/usage/components/auth-provider-button.md +116 -0
- package/docs/usage/components/auth-screen-layout.md +129 -0
- package/docs/usage/components/avatar.md +114 -0
- package/docs/usage/components/badge.md +84 -0
- package/docs/usage/components/bottom-cta.md +125 -0
- package/docs/usage/components/bottom-info.md +99 -0
- package/docs/usage/components/bottom-navigation.md +136 -0
- package/docs/usage/components/breadcrumb.md +81 -0
- package/docs/usage/components/button.md +118 -0
- package/docs/usage/components/calendar.md +122 -0
- package/docs/usage/components/card.md +110 -0
- package/docs/usage/components/carousel.md +113 -0
- package/docs/usage/components/celebration.md +96 -0
- package/docs/usage/components/chat-message.md +122 -0
- package/docs/usage/components/chat-screen.md +112 -0
- package/docs/usage/components/checkbox-group.md +104 -0
- package/docs/usage/components/checkbox.md +103 -0
- package/docs/usage/components/chip.md +104 -0
- package/docs/usage/components/code-block.md +111 -0
- package/docs/usage/components/collapsible.md +112 -0
- package/docs/usage/components/color-picker.md +86 -0
- package/docs/usage/components/combobox.md +137 -0
- package/docs/usage/components/command-palette.md +125 -0
- package/docs/usage/components/comment-thread-screen.md +125 -0
- package/docs/usage/components/container.md +98 -0
- package/docs/usage/components/content-transition.md +101 -0
- package/docs/usage/components/context-menu.md +136 -0
- package/docs/usage/components/counter-badge.md +107 -0
- package/docs/usage/components/data-table.md +122 -0
- package/docs/usage/components/date-picker.md +142 -0
- package/docs/usage/components/date-range-picker.md +111 -0
- package/docs/usage/components/description-list.md +103 -0
- package/docs/usage/components/design-system-provider.md +124 -0
- package/docs/usage/components/dialog.md +176 -0
- package/docs/usage/components/divider.md +89 -0
- package/docs/usage/components/editor-screen.md +126 -0
- package/docs/usage/components/effect-surface.md +120 -0
- package/docs/usage/components/empty-state.md +114 -0
- package/docs/usage/components/field.md +129 -0
- package/docs/usage/components/file-picker.md +114 -0
- package/docs/usage/components/floating-action-button.md +138 -0
- package/docs/usage/components/form.md +162 -0
- package/docs/usage/components/grid.md +99 -0
- package/docs/usage/components/heading.md +87 -0
- package/docs/usage/components/icon-button.md +126 -0
- package/docs/usage/components/icon.md +105 -0
- package/docs/usage/components/image.md +122 -0
- package/docs/usage/components/keyboard-avoiding.md +93 -0
- package/docs/usage/components/keyboard-dock.md +110 -0
- package/docs/usage/components/keyboard-form-scroll-view.md +95 -0
- package/docs/usage/components/keyboard-motion-provider.md +86 -0
- package/docs/usage/components/layout.md +117 -0
- package/docs/usage/components/link.md +121 -0
- package/docs/usage/components/list-detail-screen.md +103 -0
- package/docs/usage/components/list-row.md +124 -0
- package/docs/usage/components/list.md +119 -0
- package/docs/usage/components/load-more.md +115 -0
- package/docs/usage/components/masonry.md +109 -0
- package/docs/usage/components/media-selection-screen.md +119 -0
- package/docs/usage/components/mentions.md +119 -0
- package/docs/usage/components/menu.md +129 -0
- package/docs/usage/components/menubar.md +93 -0
- package/docs/usage/components/message-composer.md +124 -0
- package/docs/usage/components/moderation-screen.md +113 -0
- package/docs/usage/components/notice.md +106 -0
- package/docs/usage/components/notification-inbox-screen.md +97 -0
- package/docs/usage/components/notification-item.md +98 -0
- package/docs/usage/components/number-field.md +131 -0
- package/docs/usage/components/onboarding-screen.md +106 -0
- package/docs/usage/components/otp-field.md +101 -0
- package/docs/usage/components/pagination.md +82 -0
- package/docs/usage/components/password-field.md +137 -0
- package/docs/usage/components/permission-screen.md +107 -0
- package/docs/usage/components/photo-source-sheet.md +119 -0
- package/docs/usage/components/popover.md +108 -0
- package/docs/usage/components/profile-screen.md +89 -0
- package/docs/usage/components/progress.md +122 -0
- package/docs/usage/components/qr-code.md +122 -0
- package/docs/usage/components/radio-group.md +124 -0
- package/docs/usage/components/radio.md +104 -0
- package/docs/usage/components/result.md +116 -0
- package/docs/usage/components/saved-items-screen.md +126 -0
- package/docs/usage/components/screen-layout.md +119 -0
- package/docs/usage/components/search-field.md +120 -0
- package/docs/usage/components/search-screen.md +215 -0
- package/docs/usage/components/section.md +111 -0
- package/docs/usage/components/segmented-control.md +138 -0
- package/docs/usage/components/select.md +142 -0
- package/docs/usage/components/settings-screen.md +126 -0
- package/docs/usage/components/shared-transition-element.md +111 -0
- package/docs/usage/components/shared-transition-screen.md +86 -0
- package/docs/usage/components/sheet.md +151 -0
- package/docs/usage/components/side-panel.md +104 -0
- package/docs/usage/components/sidebar.md +107 -0
- package/docs/usage/components/skeleton.md +105 -0
- package/docs/usage/components/skip-nav.md +76 -0
- package/docs/usage/components/slider.md +121 -0
- package/docs/usage/components/sortable-collection.md +127 -0
- package/docs/usage/components/spinner.md +86 -0
- package/docs/usage/components/splitter.md +103 -0
- package/docs/usage/components/stack.md +93 -0
- package/docs/usage/components/statistic.md +123 -0
- package/docs/usage/components/steps.md +110 -0
- package/docs/usage/components/surface.md +91 -0
- package/docs/usage/components/swipe-actions.md +124 -0
- package/docs/usage/components/switch.md +120 -0
- package/docs/usage/components/tabs.md +134 -0
- package/docs/usage/components/tag.md +84 -0
- package/docs/usage/components/tags-input.md +111 -0
- package/docs/usage/components/text-area.md +112 -0
- package/docs/usage/components/text-format.md +75 -0
- package/docs/usage/components/text-transition.md +104 -0
- package/docs/usage/components/text.md +101 -0
- package/docs/usage/components/thinking-orb.md +105 -0
- package/docs/usage/components/timeline.md +105 -0
- package/docs/usage/components/toast.md +145 -0
- package/docs/usage/components/toggle-group.md +95 -0
- package/docs/usage/components/tooltip.md +103 -0
- package/docs/usage/components/top-bar.md +124 -0
- package/docs/usage/components/top.md +89 -0
- package/docs/usage/components/tour.md +118 -0
- package/docs/usage/components/transfer-list.md +115 -0
- package/docs/usage/components/tree.md +91 -0
- package/docs/usage/components/upload-item.md +99 -0
- package/docs/usage/components/virtual-list.md +105 -0
- package/docs/usage/components/visually-hidden.md +72 -0
- package/docs/usage/components/watermark.md +78 -0
- package/docs/usage/compositions/action-recovery-optimistic.md +180 -0
- package/docs/usage/compositions/action-recovery-save.md +235 -0
- package/docs/usage/compositions/action-recovery-undo.md +193 -0
- package/docs/usage/compositions/common-message.md +132 -0
- package/docs/usage/compositions/common-notification.md +101 -0
- package/docs/usage/compositions/compound-controls.md +186 -0
- package/docs/usage/compositions/data-layouts.md +157 -0
- package/docs/usage/compositions/disclosure.md +144 -0
- package/docs/usage/compositions/environment-matrix.md +139 -0
- package/docs/usage/compositions/expo-interactions.md +149 -0
- package/docs/usage/compositions/family-drawer.md +201 -0
- package/docs/usage/compositions/floating-action-button.md +197 -0
- package/docs/usage/compositions/input-sheet.md +148 -0
- package/docs/usage/compositions/interaction-adapters.md +190 -0
- package/docs/usage/compositions/interaction-flow-apply.md +205 -0
- package/docs/usage/compositions/interaction-flow-draft.md +188 -0
- package/docs/usage/compositions/interaction-flow-search.md +171 -0
- package/docs/usage/compositions/native-renderers.md +106 -0
- package/docs/usage/compositions/navigation-bar-collection.md +164 -0
- package/docs/usage/compositions/optional-adapters.md +169 -0
- package/docs/usage/compositions/optional-motion.md +109 -0
- package/docs/usage/compositions/photo-source.md +104 -0
- package/docs/usage/compositions/purpose-input-comment.md +110 -0
- package/docs/usage/compositions/purpose-input-message.md +119 -0
- package/docs/usage/compositions/reference-first.md +96 -0
- package/docs/usage/compositions/reference-review.md +107 -0
- package/docs/usage/compositions/reference-settings.md +107 -0
- package/docs/usage/compositions/selection-scope.md +174 -0
- package/docs/usage/compositions/stea-event-ticket.md +166 -0
- package/docs/usage/compositions/stea-flip-card.md +162 -0
- package/docs/usage/compositions/stea-order-progress.md +184 -0
- package/docs/usage/compositions/stea-otp-verify.md +215 -0
- package/docs/usage/compositions/stea-pixel-empty.md +140 -0
- package/docs/usage/compositions/stea-schedule-card.md +169 -0
- package/docs/usage/compositions/stea-stat-summary.md +154 -0
- package/docs/usage/compositions/time-selection.md +174 -0
- package/docs/usage/compositions/toast-layout.md +128 -0
- package/docs/usage/compositions/visual-foundations.md +185 -0
- package/docs/usage/compositions/web-additions.md +146 -0
- package/docs/usage/compositions/web-navigation.md +143 -0
- package/docs/usage/screens/common-chat.md +127 -0
- package/docs/usage/screens/common-comments.md +108 -0
- package/docs/usage/screens/common-inbox.md +110 -0
- package/docs/usage/screens/common-login.md +98 -0
- package/docs/usage/screens/common-profile.md +221 -0
- package/docs/usage/screens/common-saved.md +127 -0
- package/docs/usage/screens/common-search.md +274 -0
- package/docs/usage/screens/common-settings.md +126 -0
- package/docs/usage/screens/common-shell.md +108 -0
- package/docs/usage/screens/dashboard.md +245 -0
- package/docs/usage/screens/discovery-gallery.md +306 -0
- package/docs/usage/screens/flow-collection.md +96 -0
- package/docs/usage/screens/flow-editor.md +120 -0
- package/docs/usage/screens/flow-media.md +111 -0
- package/docs/usage/screens/flow-moderation.md +120 -0
- package/docs/usage/screens/flow-onboarding.md +193 -0
- package/docs/usage/screens/flow-permission.md +103 -0
- package/docs/usage/screens/landing.md +347 -0
- package/docs/usage/screens/mockup-studio.md +190 -0
- package/docs/usage/screens/notification-settings.md +206 -0
- package/docs/usage/screens/reference-comparison.md +159 -0
- package/docs/usage/templates/component.md +61 -0
- package/docs/usage/templates/composition.md +47 -0
- package/docs/usage/templates/screen.md +56 -0
- package/docs/usage/templates/token.md +32 -0
- package/docs/usage/tokens/color.md +142 -0
- package/docs/usage/tokens/elevation-opacity.md +86 -0
- package/docs/usage/tokens/layers.md +98 -0
- package/docs/usage/tokens/layout.md +114 -0
- package/docs/usage/tokens/motion.md +88 -0
- package/docs/usage/tokens/radius.md +53 -0
- package/docs/usage/tokens/size.md +74 -0
- package/docs/usage/tokens/spacing.md +73 -0
- package/docs/usage/tokens/stroke.md +50 -0
- package/docs/usage/tokens/theme-studio.md +70 -0
- package/docs/usage/tokens/typography-studio.md +70 -0
- package/docs/usage/tokens/typography.md +89 -0
- package/package.json +7 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-button.js","sourceRoot":"","sources":["../src/provider-button.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport { buttonRecipe } from \"./base-recipes.js\";\nimport { control, radius, spacing, typography } from \"./foundations.js\";\n\n/**\n * 소셜 로그인 버튼이다. Button의 tone 하나로 풀 수 없어서 별도 계약으로 둔다.\n *\n * HJM의 tone은 **의미**(primary/secondary/danger)이고 팔레트가 테마를 따라 움직인다.\n * 제공자 버튼의 색은 의미가 아니라 **남의 브랜드 자산**이다 — Google·Kakao·Naver·Apple이\n * 각자 로그인 버튼의 배경·글자·테두리·최소 크기를 브랜드 가이드라인으로 규정하고, 심사에서\n * 그대로 쓰기를 요구한다. 그래서 이 색들은 semantic token이 아니고 테마가 다시 칠하지\n * 않는다. 제품이 `.hjm-button.bt-provider-google` 같은 CSS로 덮어 오던 자리를 계약으로\n * 올리되, 값의 출처가 HJM이 아니라는 사실을 타입으로 드러낸다.\n *\n * **HJM이 소유하는 것**: 높이·radius·터치 타깃·포커스 링·로고와 라벨의 배치·큰 글자 대응.\n * **제공자가 소유하는 것**: 색과 로고 모양.\n * **제품이 소유하는 것**: 로고 자산(재배포하지 않으므로 슬롯으로 받는다)과 현지화된 문구\n * (\"Google로 계속하기\"의 정확한 표현은 제공자 가이드라인과 언어에 따라 다르다).\n */\nexport type AuthProviderId = \"google\" | \"kakao\" | \"naver\" | \"apple\";\n\nexport type AuthProviderSurface = Readonly<{\n /** 제공자 가이드라인이 지정한 배경색. HJM 팔레트가 아니다. */\n background: string;\n /** 같은 가이드라인의 글자/로고 색. */\n content: string;\n /** 배경이 캔버스와 구분되지 않을 때만 가이드라인이 요구하는 테두리. */\n border: string | null;\n}>;\n\nexport type AuthProviderPalette = Readonly<{\n light: AuthProviderSurface;\n /**\n * 제공자가 다크 변형을 따로 규정한 경우에만 다르다. 브랜드색 자체가 정체성인\n * Kakao·Naver는 다크에서도 같은 색을 쓰라고 명시하므로 light와 동일하다.\n */\n dark: AuthProviderSurface;\n}>;\n\n/**\n * 값의 출처는 각 제공자의 공개 브랜드 가이드라인이다(2026-09 확인).\n * 로고 이미지는 포함하지 않는다 — 배포 조건이 제공자마다 다르고, HJM이 재배포할 권리가\n * 없다. 가이드라인이 바뀌면 이 표를 고치는 것이 유일한 변경 지점이다.\n */\nexport const authProviderPalettes = {\n // Google Identity: 흰 버튼 #FFFFFF / 글자 #1F1F1F / 테두리 #747775,\n // 어두운 변형 #131314 / #E3E3E3 / #8E918F.\n google: {\n light: { background: \"#FFFFFF\", content: \"#1F1F1F\", border: \"#747775\" },\n dark: { background: \"#131314\", content: \"#E3E3E3\", border: \"#8E918F\" },\n },\n // Kakao 로그인: 배경 #FEE500 고정, 글자 #191919.\n // 예전 값은 `rgba(0, 0, 0, 0.85)`였다. 흔히 인용되는 \"검정 85%\"인데, 노란 배경 위에서\n // 실제로 합성하면 (38, 34, 0)이라 카카오가 배포하는 버튼 이미지의 글자색과 다르다.\n // 공식 자산(kakao_login.zip의 ko/kakao_login_large_wide.png)에서 픽셀을 뽑아 대조했다.\n kakao: {\n light: { background: \"#FEE500\", content: \"#191919\", border: null },\n dark: { background: \"#FEE500\", content: \"#191919\", border: null },\n },\n // 네이버 로그인: 배경 #03A94D 고정, 글자 흰색.\n // 예전 값 #03C75A는 구 BI다. 네이버 로그인 BI 가이드가 \"배경 컬러가 더 뚜렷하게\n // 바뀌었어요. 반드시 지정된 녹색을 사용해 주세요. 컬러 #03A94D, RGB 3/169/77\"로 못박고,\n // 배포 중인 버튼 자산(NAVER_login_KR.zip)의 픽셀도 (3, 169, 77)이다(2026-09-19 확인).\n naver: {\n light: { background: \"#03A94D\", content: \"#FFFFFF\", border: null },\n dark: { background: \"#03A94D\", content: \"#FFFFFF\", border: null },\n },\n // Sign in with Apple: 검정 버튼이 기본, 밝은 배경 위에서는 흰 버튼 + 검은 테두리.\n apple: {\n light: { background: \"#000000\", content: \"#FFFFFF\", border: null },\n dark: { background: \"#FFFFFF\", content: \"#000000\", border: \"#000000\" },\n },\n} as const satisfies Readonly<Record<AuthProviderId, AuthProviderPalette>>;\n\nexport type AuthProviderButtonDescriptor = Readonly<{\n provider: AuthProviderId;\n /** 제공자 가이드라인과 언어에 맞춘 문구. renderer가 만들지 않는다. */\n label: string;\n /** 진행 중 표시. 라벨을 지우지 않고 버튼 폭도 유지한다. */\n busy?: boolean;\n disabled?: boolean;\n}>;\n\nexport function validateAuthProviderButtonDescriptor(\n descriptor: AuthProviderButtonDescriptor,\n): void {\n if (descriptor === null || typeof descriptor !== \"object\") {\n throw new TypeError(\"AuthProviderButton descriptor must be an object\");\n }\n if (!Object.prototype.hasOwnProperty.call(authProviderPalettes, descriptor.provider)) {\n throw new TypeError(`Unsupported auth provider: ${String(descriptor.provider)}`);\n }\n if (typeof descriptor.label !== \"string\" || descriptor.label.trim().length === 0) {\n throw new TypeError(\"AuthProviderButton label must not be empty\");\n }\n}\n\n/** 해석된 테마에 맞는 제공자 표면. 테마는 *변형 선택*만 하고 색을 만들지 않는다. */\nexport function resolveAuthProviderSurface(\n provider: AuthProviderId,\n theme: \"light\" | \"dark\",\n): AuthProviderSurface {\n return authProviderPalettes[provider][theme];\n}\n\n/**\n * 크기는 HJM 것이다 — 한 화면에 네 개가 세로로 쌓이므로 높이·radius·간격이 서로 다르면\n * 목록이 무너진다. `buttonRecipe`의 medium 높이와 같은 값을 쓰고, 최소 터치 타깃도 공유한다.\n */\nexport const authProviderButtonRecipe = {\n slots: [\"root\", \"logo\", \"label\", \"spinner\"] as const,\n minHeight: buttonRecipe.sizes.medium.height,\n minTouchTarget: control.minTouchTarget,\n radius: radius.md,\n paddingHorizontal: spacing.md,\n gap: spacing.sm,\n logoSize: 20,\n label: typography.body,\n borderWidth: 1,\n /** 제공자 색 위에서도 보이도록 포커스 링은 바깥에 그린다. */\n focusOutlineOffset: 2,\n} as const;\n\nexport const authProviderButtonBehavior = {\n controlled: [],\n inputs: [\"provider\", \"label\", \"busy\", \"disabled\"],\n events: [\"onPress\"],\n stateAxes: {\n availability: [\"enabled\", \"disabled\", \"busy\"],\n interaction: [\"idle\", \"hover\", \"focusVisible\", \"pressed\"],\n },\n web: { roles: [\"button\"], keyboard: [\"Enter\", \"Space\"], focus: \"native\" },\n native: { roles: [\"button\"], states: [\"disabled\", \"busy\"], actions: [\"press\"] },\n scenarios: [\n \"provider-colors-come-from-the-provider-guideline-and-the-theme-never-recolors-them\",\n \"the-theme-only-picks-between-the-providers-own-light-and-dark-variants\",\n \"the-logo-asset-is-a-product-supplied-slot-never-bundled-by-the-design-system\",\n \"the-label-is-product-copy-because-the-required-wording-differs-per-provider-and-language\",\n \"height-radius-and-touch-target-stay-hjm-so-a-stack-of-providers-lines-up\",\n \"busy-keeps-the-label-and-the-button-width-instead-of-collapsing-to-a-spinner\",\n \"the-focus-ring-is-drawn-outside-the-brand-fill-so-it-survives-every-provider-color\",\n ],\n} as const satisfies BehaviorContract;\n"]}
|
|
1
|
+
{"version":3,"file":"provider-button.js","sourceRoot":"","sources":["../src/provider-button.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport { buttonRecipe } from \"./base-recipes.js\";\nimport { control, radius, spacing, typography } from \"./foundations.js\";\n\n/**\n * 소셜 로그인 버튼이다. Button의 tone 하나로 풀 수 없어서 별도 계약으로 둔다.\n *\n * HJM의 tone은 **의미**(primary/secondary/danger)이고 팔레트가 테마를 따라 움직인다.\n * 제공자 버튼의 색은 의미가 아니라 **남의 브랜드 자산**이다 — Google·Kakao·Naver·Apple이\n * 각자 로그인 버튼의 배경·글자·테두리·최소 크기를 브랜드 가이드라인으로 규정하고, 심사에서\n * 그대로 쓰기를 요구한다. 그래서 이 색들은 semantic token이 아니고 테마가 다시 칠하지\n * 않는다. 제품이 `.hjm-button.bt-provider-google` 같은 CSS로 덮어 오던 자리를 계약으로\n * 올리되, 값의 출처가 HJM이 아니라는 사실을 타입으로 드러낸다.\n *\n * **HJM이 소유하는 것**: 높이·radius·터치 타깃·포커스 링·로고와 라벨의 배치·큰 글자 대응.\n * **제공자가 소유하는 것**: 색과 로고 모양.\n * **제품이 소유하는 것**: 로고 자산(재배포하지 않으므로 슬롯으로 받는다)과 현지화된 문구\n * (\"Google로 계속하기\"의 정확한 표현은 제공자 가이드라인과 언어에 따라 다르다).\n */\nexport type AuthProviderId = \"google\" | \"kakao\" | \"naver\" | \"apple\";\n\nexport type AuthProviderSurface = Readonly<{\n /** 제공자 가이드라인이 지정한 배경색. HJM 팔레트가 아니다. */\n background: string;\n /** 같은 가이드라인의 글자/로고 색. */\n content: string;\n /** 배경이 캔버스와 구분되지 않을 때만 가이드라인이 요구하는 테두리. */\n border: string | null;\n}>;\n\nexport type AuthProviderPalette = Readonly<{\n light: AuthProviderSurface;\n /**\n * 제공자가 다크 변형을 따로 규정한 경우에만 다르다. 브랜드색 자체가 정체성인\n * Kakao·Naver는 다크에서도 같은 색을 쓰라고 명시하므로 light와 동일하다.\n */\n dark: AuthProviderSurface;\n}>;\n\n/**\n * 값의 출처는 각 제공자의 공개 브랜드 가이드라인이다(2026-09 확인).\n * 로고 이미지는 포함하지 않는다 — 배포 조건이 제공자마다 다르고, HJM이 재배포할 권리가\n * 없다. 가이드라인이 바뀌면 이 표를 고치는 것이 유일한 변경 지점이다.\n */\nexport const authProviderPalettes = {\n // Google Identity: 흰 버튼 #FFFFFF / 글자 #1F1F1F / 테두리 #747775,\n // 어두운 변형 #131314 / #E3E3E3 / #8E918F.\n google: {\n light: { background: \"#FFFFFF\", content: \"#1F1F1F\", border: \"#747775\" },\n dark: { background: \"#131314\", content: \"#E3E3E3\", border: \"#8E918F\" },\n },\n // Kakao 로그인: 배경 #FEE500 고정, 글자 #191919.\n // 예전 값은 `rgba(0, 0, 0, 0.85)`였다. 흔히 인용되는 \"검정 85%\"인데, 노란 배경 위에서\n // 실제로 합성하면 (38, 34, 0)이라 카카오가 배포하는 버튼 이미지의 글자색과 다르다.\n // 공식 자산(kakao_login.zip의 ko/kakao_login_large_wide.png)에서 픽셀을 뽑아 대조했다.\n kakao: {\n light: { background: \"#FEE500\", content: \"#191919\", border: null },\n dark: { background: \"#FEE500\", content: \"#191919\", border: null },\n },\n // 네이버 로그인: 배경 #03A94D 고정, 글자 흰색.\n // 예전 값 #03C75A는 구 BI다. 네이버 로그인 BI 가이드가 \"배경 컬러가 더 뚜렷하게\n // 바뀌었어요. 반드시 지정된 녹색을 사용해 주세요. 컬러 #03A94D, RGB 3/169/77\"로 못박고,\n // 배포 중인 버튼 자산(NAVER_login_KR.zip)의 픽셀도 (3, 169, 77)이다(2026-09-19 확인).\n naver: {\n light: { background: \"#03A94D\", content: \"#FFFFFF\", border: null },\n dark: { background: \"#03A94D\", content: \"#FFFFFF\", border: null },\n },\n // Sign in with Apple: 검정 버튼이 기본, 밝은 배경 위에서는 흰 버튼 + 검은 테두리.\n apple: {\n light: { background: \"#000000\", content: \"#FFFFFF\", border: null },\n dark: { background: \"#FFFFFF\", content: \"#000000\", border: \"#000000\" },\n },\n} as const satisfies Readonly<Record<AuthProviderId, AuthProviderPalette>>;\n\nexport type AuthProviderButtonDescriptor = Readonly<{\n provider: AuthProviderId;\n /** 제공자 가이드라인과 언어에 맞춘 문구. renderer가 만들지 않는다. */\n label: string;\n /** 진행 중 표시. 라벨을 지우지 않고 버튼 폭도 유지한다. */\n busy?: boolean;\n disabled?: boolean;\n}>;\n\nexport function validateAuthProviderButtonDescriptor(\n descriptor: AuthProviderButtonDescriptor,\n): void {\n if (descriptor === null || typeof descriptor !== \"object\") {\n throw new TypeError(\"AuthProviderButton descriptor must be an object\");\n }\n if (!Object.prototype.hasOwnProperty.call(authProviderPalettes, descriptor.provider)) {\n throw new TypeError(`Unsupported auth provider: ${String(descriptor.provider)}`);\n }\n if (typeof descriptor.label !== \"string\" || descriptor.label.trim().length === 0) {\n throw new TypeError(\"AuthProviderButton label must not be empty\");\n }\n}\n\n/** 해석된 테마에 맞는 제공자 표면. 테마는 *변형 선택*만 하고 색을 만들지 않는다. */\nexport function resolveAuthProviderSurface(\n provider: AuthProviderId,\n theme: \"light\" | \"dark\",\n): AuthProviderSurface {\n return authProviderPalettes[provider][theme];\n}\n\n/**\n * 크기는 HJM 것이다 — 한 화면에 네 개가 세로로 쌓이므로 높이·radius·간격이 서로 다르면\n * 목록이 무너진다. `buttonRecipe`의 medium 높이와 같은 값을 쓰고, 최소 터치 타깃도 공유한다.\n */\nexport const authProviderButtonRecipe = {\n slots: [\"root\", \"logo\", \"label\", \"spinner\"] as const,\n minHeight: buttonRecipe.sizes.medium.height,\n minTouchTarget: control.minTouchTarget,\n radius: radius.md,\n paddingHorizontal: spacing.md,\n gap: spacing.sm,\n logoSize: 20,\n // 제공자 가이드가 라벨·색 표현을 소유한다(2026-10-06 사용자 위임 결정). 네이버 녹색·흰 글자\n // 대비 3.09:1은 제공자 색 예외로 기록하고(docs/provider-button.md), titleLarge(20 heavy)로 키워\n // 3:1 큰 글자 기준을 맞추는 대안은 가이드 밖의 라벨 표현이라 버렸다.\n label: typography.body,\n borderWidth: 1,\n /** 제공자 색 위에서도 보이도록 포커스 링은 바깥에 그린다. */\n focusOutlineOffset: 2,\n} as const;\n\nexport const authProviderButtonBehavior = {\n controlled: [],\n inputs: [\"provider\", \"label\", \"busy\", \"disabled\"],\n events: [\"onPress\"],\n stateAxes: {\n availability: [\"enabled\", \"disabled\", \"busy\"],\n interaction: [\"idle\", \"hover\", \"focusVisible\", \"pressed\"],\n },\n web: { roles: [\"button\"], keyboard: [\"Enter\", \"Space\"], focus: \"native\" },\n native: { roles: [\"button\"], states: [\"disabled\", \"busy\"], actions: [\"press\"] },\n scenarios: [\n \"provider-colors-come-from-the-provider-guideline-and-the-theme-never-recolors-them\",\n \"the-theme-only-picks-between-the-providers-own-light-and-dark-variants\",\n \"the-logo-asset-is-a-product-supplied-slot-never-bundled-by-the-design-system\",\n \"the-label-is-product-copy-because-the-required-wording-differs-per-provider-and-language\",\n \"height-radius-and-touch-target-stay-hjm-so-a-stack-of-providers-lines-up\",\n \"busy-keeps-the-label-and-the-button-width-instead-of-collapsing-to-a-spinner\",\n \"the-focus-ring-is-drawn-outside-the-brand-fill-so-it-survives-every-provider-color\",\n ],\n} as const satisfies BehaviorContract;\n"]}
|
package/dist/reactions.d.ts
CHANGED
|
@@ -7,4 +7,14 @@ export type ReactionOption = Readonly<{
|
|
|
7
7
|
}>;
|
|
8
8
|
export declare function validateReactions(options: readonly ReactionOption[], value: string | null): void;
|
|
9
9
|
export declare function nextReaction(options: readonly ReactionOption[], current: string | null, id: string): string | null;
|
|
10
|
+
/**
|
|
11
|
+
* The expanded catalog is product-localized; quick and additional IDs stay unique.
|
|
12
|
+
* The + button needs a localized name and something to reveal; the message stays short
|
|
13
|
+
* because this isolated entry has a byte cap (check-bundle-budget.mjs).
|
|
14
|
+
*/
|
|
15
|
+
export type ReactionMoreOptions = Readonly<{
|
|
16
|
+
label: string;
|
|
17
|
+
options: readonly ReactionOption[];
|
|
18
|
+
}>;
|
|
19
|
+
export declare function resolveReactionOptions(options: readonly ReactionOption[], more?: ReactionMoreOptions): readonly ReactionOption[];
|
|
10
20
|
//# sourceMappingURL=reactions.d.ts.map
|
package/dist/reactions.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactions.d.ts","sourceRoot":"","sources":["../src/reactions.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACxH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAOhG;AACD,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKlH"}
|
|
1
|
+
{"version":3,"file":"reactions.d.ts","sourceRoot":"","sources":["../src/reactions.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACxH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAOhG;AACD,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKlH;AAED;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAA;CAAE,CAAC,CAAC;AAClG,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,SAAS,cAAc,EAAE,CAKhI"}
|
package/dist/reactions.js
CHANGED
|
@@ -17,4 +17,11 @@ export function nextReaction(options, current, id) {
|
|
|
17
17
|
throw new TypeError("Unknown reaction");
|
|
18
18
|
return option.disabled ? current : current === id ? null : id;
|
|
19
19
|
}
|
|
20
|
+
export function resolveReactionOptions(options, more) {
|
|
21
|
+
const all = more ? [...options, ...more.options] : options;
|
|
22
|
+
if (more && !(more.label.trim() && more.options.length))
|
|
23
|
+
throw new TypeError("More reactions need a label and options");
|
|
24
|
+
validateReactions(all, null);
|
|
25
|
+
return all;
|
|
26
|
+
}
|
|
20
27
|
//# sourceMappingURL=reactions.js.map
|
package/dist/reactions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactions.js","sourceRoot":"","sources":["../src/reactions.ts"],"names":[],"mappings":"AACA,MAAM,UAAU,iBAAiB,CAAC,OAAkC,EAAE,KAAoB;IACxF,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,kCAAkC,CAAC,CAAC;IAClJ,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE;YAAE,MAAM,IAAI,SAAS,CAAC,4DAA4D,CAAC,CAAC;QACzJ,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,8CAA8C,CAAC,CAAC;IACpK,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,8BAA8B,CAAC,CAAC;AAC1H,CAAC;AACD,MAAM,UAAU,YAAY,CAAC,OAAkC,EAAE,OAAsB,EAAE,EAAU;IACjG,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,kBAAkB,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;AAChE,CAAC","sourcesContent":["export type ReactionOption = Readonly<{ id: string; emoji: string; label: string; count?: number; disabled?: boolean }>;\nexport function validateReactions(options: readonly ReactionOption[], value: string | null): void {\n if (!options.length || new Set(options.map(option => option.id)).size !== options.length) throw new TypeError(\"Reactions require unique options\");\n for (const option of options) {\n if (!option.id.trim() || !option.emoji.trim() || !option.label.trim()) throw new TypeError(\"Reactions require identifiers, artwork and localized names\");\n if (option.count !== undefined && (!Number.isSafeInteger(option.count) || option.count < 0)) throw new RangeError(\"Reaction count must be a nonnegative integer\");\n }\n if (value !== null && !options.some(option => option.id === value)) throw new TypeError(\"Selected reaction is missing\");\n}\nexport function nextReaction(options: readonly ReactionOption[], current: string | null, id: string): string | null {\n validateReactions(options, current);\n const option = options.find(item => item.id === id);\n if (!option) throw new TypeError(\"Unknown reaction\");\n return option.disabled ? current : current === id ? null : id;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"reactions.js","sourceRoot":"","sources":["../src/reactions.ts"],"names":[],"mappings":"AACA,MAAM,UAAU,iBAAiB,CAAC,OAAkC,EAAE,KAAoB;IACxF,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,kCAAkC,CAAC,CAAC;IAClJ,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE;YAAE,MAAM,IAAI,SAAS,CAAC,4DAA4D,CAAC,CAAC;QACzJ,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,8CAA8C,CAAC,CAAC;IACpK,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,8BAA8B,CAAC,CAAC;AAC1H,CAAC;AACD,MAAM,UAAU,YAAY,CAAC,OAAkC,EAAE,OAAsB,EAAE,EAAU;IACjG,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,kBAAkB,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;AAChE,CAAC;AAQD,MAAM,UAAU,sBAAsB,CAAC,OAAkC,EAAE,IAA0B;IACnG,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAC3D,IAAI,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;IACxH,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC7B,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["export type ReactionOption = Readonly<{ id: string; emoji: string; label: string; count?: number; disabled?: boolean }>;\nexport function validateReactions(options: readonly ReactionOption[], value: string | null): void {\n if (!options.length || new Set(options.map(option => option.id)).size !== options.length) throw new TypeError(\"Reactions require unique options\");\n for (const option of options) {\n if (!option.id.trim() || !option.emoji.trim() || !option.label.trim()) throw new TypeError(\"Reactions require identifiers, artwork and localized names\");\n if (option.count !== undefined && (!Number.isSafeInteger(option.count) || option.count < 0)) throw new RangeError(\"Reaction count must be a nonnegative integer\");\n }\n if (value !== null && !options.some(option => option.id === value)) throw new TypeError(\"Selected reaction is missing\");\n}\nexport function nextReaction(options: readonly ReactionOption[], current: string | null, id: string): string | null {\n validateReactions(options, current);\n const option = options.find(item => item.id === id);\n if (!option) throw new TypeError(\"Unknown reaction\");\n return option.disabled ? current : current === id ? null : id;\n}\n\n/**\n * The expanded catalog is product-localized; quick and additional IDs stay unique.\n * The + button needs a localized name and something to reveal; the message stays short\n * because this isolated entry has a byte cap (check-bundle-budget.mjs).\n */\nexport type ReactionMoreOptions = Readonly<{ label: string; options: readonly ReactionOption[] }>;\nexport function resolveReactionOptions(options: readonly ReactionOption[], more?: ReactionMoreOptions): readonly ReactionOption[] {\n const all = more ? [...options, ...more.options] : options;\n if (more && !(more.label.trim() && more.options.length)) throw new TypeError(\"More reactions need a label and options\");\n validateReactions(all, null);\n return all;\n}\n"]}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/** Initial load replaces the body; refresh/save errors belong beside retained content. */
|
|
2
|
+
export type ScreenContentState = Readonly<{
|
|
3
|
+
kind: "ready";
|
|
4
|
+
}> | Readonly<{
|
|
5
|
+
kind: "loading" | "empty" | "error" | "restricted";
|
|
6
|
+
title: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function resolveScreenContentState(state?: ScreenContentState): {
|
|
10
|
+
readonly kind: "error" | "empty" | "loading" | "ready" | "restricted";
|
|
11
|
+
readonly replacesContent: boolean;
|
|
12
|
+
readonly busy: boolean;
|
|
13
|
+
readonly announcement: "polite" | "assertive";
|
|
14
|
+
};
|
|
15
|
+
export declare const screenPatternRecipe: {
|
|
16
|
+
readonly maxWidth: 720;
|
|
17
|
+
readonly padding: 16;
|
|
18
|
+
readonly sectionGap: 24;
|
|
19
|
+
readonly itemGap: 12;
|
|
20
|
+
readonly stateGap: 16;
|
|
21
|
+
readonly headerMinWidth: number;
|
|
22
|
+
readonly messageMaxWidth: "84%";
|
|
23
|
+
readonly attachmentSize: 80;
|
|
24
|
+
readonly attachmentRemoveSize: 24;
|
|
25
|
+
readonly reactionHoldMs: 450;
|
|
26
|
+
readonly reactionMoveTolerance: 10;
|
|
27
|
+
readonly reactionMenuWidth: 320;
|
|
28
|
+
readonly replySwipeDistance: 60;
|
|
29
|
+
readonly composerMinLines: 1;
|
|
30
|
+
readonly composerMaxLines: 5;
|
|
31
|
+
};
|
|
32
|
+
/** Controlled composer: HJM never clears drafts or interprets a fulfilled callback as server delivery. */
|
|
33
|
+
export type MessageComposerDescriptor = Readonly<{
|
|
34
|
+
value: string;
|
|
35
|
+
label: string;
|
|
36
|
+
sendLabel: string;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
pending?: boolean;
|
|
39
|
+
attachmentCount?: number;
|
|
40
|
+
}>;
|
|
41
|
+
export declare function canSubmitMessage({ value, disabled, pending, attachmentCount }: MessageComposerDescriptor): boolean;
|
|
42
|
+
/** Bubble alignment expresses authorship, not delivery; delivery text always comes from the product receipt. */
|
|
43
|
+
export type ChatMessageDescriptor = Readonly<{
|
|
44
|
+
direction: "incoming" | "outgoing";
|
|
45
|
+
author: string;
|
|
46
|
+
timestamp: string;
|
|
47
|
+
deliveryLabel?: string;
|
|
48
|
+
}>;
|
|
49
|
+
/** A denied permission goes to settings; rendering never requests OS access automatically. */
|
|
50
|
+
export type PermissionScreenStatus = "prompt" | "denied" | "granted" | "unavailable";
|
|
51
|
+
export declare function resolvePermissionAction(status: PermissionScreenStatus): "settings" | "request" | "continue" | null;
|
|
52
|
+
export declare function resolveOnboardingStep(total: number, index: number): {
|
|
53
|
+
first: boolean;
|
|
54
|
+
last: boolean;
|
|
55
|
+
};
|
|
56
|
+
/** Replies are one level deep; reject orphaned/duplicate records instead of silently losing them. */
|
|
57
|
+
export declare function validateCommentThread(items: readonly {
|
|
58
|
+
id: string;
|
|
59
|
+
parentId: string | null;
|
|
60
|
+
}[]): void;
|
|
61
|
+
/** Source bytes, permission requests, ordering and upload limits remain product-owned. */
|
|
62
|
+
export type MessageAttachmentDescriptor = Readonly<{
|
|
63
|
+
id: string;
|
|
64
|
+
removeLabel: string;
|
|
65
|
+
}>;
|
|
66
|
+
export declare function validateMessageAttachments(attachments: readonly MessageAttachmentDescriptor[]): void;
|
|
67
|
+
/** Require horizontal intent so a timeline scroll cannot accidentally start a reply. */
|
|
68
|
+
export declare function isReplySwipe(dx: number, dy: number): boolean;
|
|
69
|
+
/** The UI never infers permission or camera availability; product adapters own both. */
|
|
70
|
+
export type PhotoSource = "library" | "camera";
|
|
71
|
+
export type PhotoSourceLabels = Readonly<{
|
|
72
|
+
title: string;
|
|
73
|
+
library: string;
|
|
74
|
+
camera: string;
|
|
75
|
+
cancel: string;
|
|
76
|
+
}>;
|
|
77
|
+
export type SavedItem = Readonly<{
|
|
78
|
+
id: string;
|
|
79
|
+
title: string;
|
|
80
|
+
}>;
|
|
81
|
+
export type SavedCollection = Readonly<{
|
|
82
|
+
id: string;
|
|
83
|
+
title: string;
|
|
84
|
+
itemIds: readonly string[];
|
|
85
|
+
}>;
|
|
86
|
+
export type SavedItemsLabels = Readonly<{
|
|
87
|
+
allItems: string;
|
|
88
|
+
privateNotice: string;
|
|
89
|
+
back: string;
|
|
90
|
+
empty: string;
|
|
91
|
+
createCollection: string;
|
|
92
|
+
}>;
|
|
93
|
+
/** Unsave removes an item from every collection view without rewriting product-owned membership. */
|
|
94
|
+
export declare function resolveSavedItems<T extends SavedItem>(items: readonly T[], collections: readonly SavedCollection[], collectionId: string | null | undefined, selectedItemId: string | null | undefined): {
|
|
95
|
+
home: boolean;
|
|
96
|
+
collection: Readonly<{
|
|
97
|
+
id: string;
|
|
98
|
+
title: string;
|
|
99
|
+
itemIds: readonly string[];
|
|
100
|
+
}> | undefined;
|
|
101
|
+
visible: readonly T[];
|
|
102
|
+
selected: T | undefined;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* SearchScreen phases (2026-10-06 search redesign, docs/screen-patterns.md#searchscreen-두-단계-검색).
|
|
106
|
+
* Without a committed query (one-step search, the pre-1.13 behavior) any nonblank query is already
|
|
107
|
+
* `results`. With one, typing a different query is `typing`: suggestions replace the results until the
|
|
108
|
+
* user commits (Enter, a suggestion, a recent or suggested query). Comparing trimmed values keeps a
|
|
109
|
+
* trailing space from flipping committed results back into suggestions.
|
|
110
|
+
*/
|
|
111
|
+
export type SearchScreenPhase = "idle" | "typing" | "results";
|
|
112
|
+
export declare function resolveSearchScreenPhase(query: string, committedQuery?: string): SearchScreenPhase;
|
|
113
|
+
/**
|
|
114
|
+
* The value a commit records, or `null` when there is nothing to commit. Blank commits are dropped
|
|
115
|
+
* so a stray Enter never writes an empty recent search or fetches "everything".
|
|
116
|
+
*/
|
|
117
|
+
export declare function resolveSearchCommit(value: string): string | null;
|
|
118
|
+
/**
|
|
119
|
+
* Why a committed search shows nothing. `filters` keeps the filter rail and offers "clear all";
|
|
120
|
+
* `query` hides the rail (no filter can fix it) and offers suggested queries instead. Reference
|
|
121
|
+
* services hide the rail on every zero result, which removes the only way to undo a filter.
|
|
122
|
+
* `null` count means the result is still loading, so no cause is reported yet.
|
|
123
|
+
*/
|
|
124
|
+
export type SearchEmptyCause = "none" | "filters" | "query";
|
|
125
|
+
export declare function resolveSearchEmptyCause(count: number | null, appliedFilterCount: number): SearchEmptyCause;
|
|
126
|
+
/**
|
|
127
|
+
* Index to focus after removing item `removedIndex` from a list that now has `remaining` items:
|
|
128
|
+
* the item that slid into its place, else the new last item, else -1 (move to the fallback target).
|
|
129
|
+
* Removing a focused chip or row would otherwise drop focus to <body>.
|
|
130
|
+
*/
|
|
131
|
+
export declare function resolveFocusAfterRemoval(removedIndex: number, remaining: number): number;
|
|
132
|
+
/** Recipe for the SearchScreen phase sections; geometry stays on shared spacing tokens. */
|
|
133
|
+
export declare const searchScreenRecipe: {
|
|
134
|
+
/** Recent searches shown before the user types; reference services show 5–10 and let the rest scroll away. */
|
|
135
|
+
readonly recentVisible: 5;
|
|
136
|
+
/** Suggestions under the "search for ‘q’" row. */
|
|
137
|
+
readonly suggestionVisible: 6;
|
|
138
|
+
/** Skeleton rows while the first page of results loads. */
|
|
139
|
+
readonly loadingRows: 4;
|
|
140
|
+
/** Between idle sections (recent, suggested). */
|
|
141
|
+
readonly sectionGap: 24;
|
|
142
|
+
/** Section title to its list, and results header to applied filters. */
|
|
143
|
+
readonly headerGap: 12;
|
|
144
|
+
/** Between chips in applied filters and suggested queries. */
|
|
145
|
+
readonly chipGap: 8;
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=screen-patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screen-patterns.d.ts","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { layout, spacing } from "./foundations.js";
|
|
2
|
+
export function resolveScreenContentState(state = { kind: "ready" }) {
|
|
3
|
+
if (!["ready", "loading", "empty", "error", "restricted"].includes(state.kind)) {
|
|
4
|
+
throw new TypeError("Unknown screen content state");
|
|
5
|
+
}
|
|
6
|
+
if (state.kind !== "ready" && !state.title.trim()) {
|
|
7
|
+
throw new TypeError("Screen state requires a localized title");
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
kind: state.kind,
|
|
11
|
+
replacesContent: state.kind !== "ready",
|
|
12
|
+
busy: state.kind === "loading",
|
|
13
|
+
announcement: state.kind === "error" ? "assertive" : "polite",
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
// Reuse reading-width and spacing tokens; do not derive geometry from a product's device or artwork.
|
|
17
|
+
export const screenPatternRecipe = {
|
|
18
|
+
maxWidth: layout.readingMaxWidth,
|
|
19
|
+
padding: spacing.md,
|
|
20
|
+
sectionGap: spacing.xl,
|
|
21
|
+
itemGap: spacing.sm,
|
|
22
|
+
stateGap: spacing.md,
|
|
23
|
+
// Compact screens must keep back/title/save on one row; scale this floor for large text instead of reserving a desktop-width title.
|
|
24
|
+
headerMinWidth: layout.readingMaxWidth / 6,
|
|
25
|
+
// Reserve the opposite edge for authorship and cap the composer so history remains visible.
|
|
26
|
+
messageMaxWidth: "84%",
|
|
27
|
+
// Social thumbnails stay compact; selection order and removal replace a second toolbar.
|
|
28
|
+
attachmentSize: 80,
|
|
29
|
+
attachmentRemoveSize: 24,
|
|
30
|
+
reactionHoldMs: 450,
|
|
31
|
+
reactionMoveTolerance: 10,
|
|
32
|
+
reactionMenuWidth: 320,
|
|
33
|
+
replySwipeDistance: 60,
|
|
34
|
+
composerMinLines: 1,
|
|
35
|
+
composerMaxLines: 5,
|
|
36
|
+
};
|
|
37
|
+
export function canSubmitMessage({ value, disabled = false, pending = false, attachmentCount = 0 }) {
|
|
38
|
+
if (!Number.isSafeInteger(attachmentCount) || attachmentCount < 0)
|
|
39
|
+
throw new RangeError("Attachment count must be a nonnegative integer");
|
|
40
|
+
return !disabled && !pending && (value.trim().length > 0 || attachmentCount > 0);
|
|
41
|
+
}
|
|
42
|
+
export function resolvePermissionAction(status) {
|
|
43
|
+
switch (status) {
|
|
44
|
+
case "prompt": return "request";
|
|
45
|
+
case "denied": return "settings";
|
|
46
|
+
case "granted": return "continue";
|
|
47
|
+
case "unavailable": return null;
|
|
48
|
+
default: throw new TypeError("Unknown permission state");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function resolveOnboardingStep(total, index) {
|
|
52
|
+
if (!Number.isInteger(total) || total < 1 || !Number.isInteger(index) || index < 0 || index >= total)
|
|
53
|
+
throw new RangeError("Onboarding requires a valid step index");
|
|
54
|
+
return { first: index === 0, last: index === total - 1 };
|
|
55
|
+
}
|
|
56
|
+
/** Replies are one level deep; reject orphaned/duplicate records instead of silently losing them. */
|
|
57
|
+
export function validateCommentThread(items) {
|
|
58
|
+
const roots = new Set(items.filter(item => item.parentId === null).map(item => item.id));
|
|
59
|
+
const ids = new Set();
|
|
60
|
+
for (const item of items) {
|
|
61
|
+
if (!item.id.trim() || ids.has(item.id))
|
|
62
|
+
throw new TypeError("Comment ids must be unique and nonempty");
|
|
63
|
+
ids.add(item.id);
|
|
64
|
+
if (item.parentId !== null && (!roots.has(item.parentId) || item.parentId === item.id))
|
|
65
|
+
throw new TypeError("Replies require an existing root comment");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export function validateMessageAttachments(attachments) {
|
|
69
|
+
const ids = new Set();
|
|
70
|
+
for (const item of attachments) {
|
|
71
|
+
if (!item.id.trim() || ids.has(item.id) || !item.removeLabel.trim())
|
|
72
|
+
throw new TypeError("Attachments require unique ids and localized removal labels");
|
|
73
|
+
ids.add(item.id);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Require horizontal intent so a timeline scroll cannot accidentally start a reply. */
|
|
77
|
+
export function isReplySwipe(dx, dy) {
|
|
78
|
+
return Math.abs(dx) >= screenPatternRecipe.replySwipeDistance && Math.abs(dx) > Math.abs(dy) * 2;
|
|
79
|
+
}
|
|
80
|
+
/** Unsave removes an item from every collection view without rewriting product-owned membership. */
|
|
81
|
+
export function resolveSavedItems(items, collections, collectionId, selectedItemId) {
|
|
82
|
+
const ids = new Set(), groups = new Set();
|
|
83
|
+
for (const item of items) {
|
|
84
|
+
if (!item.id.trim() || ids.has(item.id) || !item.title.trim())
|
|
85
|
+
throw new TypeError("Saved items require unique ids and nonempty titles");
|
|
86
|
+
ids.add(item.id);
|
|
87
|
+
}
|
|
88
|
+
for (const group of collections) {
|
|
89
|
+
if (!group.id.trim() || groups.has(group.id) || !group.title.trim())
|
|
90
|
+
throw new TypeError("Saved collections require unique ids and nonempty titles");
|
|
91
|
+
groups.add(group.id);
|
|
92
|
+
}
|
|
93
|
+
const collection = collections.find(item => item.id === collectionId);
|
|
94
|
+
// A deleted collection returns to the overview instead of showing a mislabeled all-posts view.
|
|
95
|
+
const home = collectionId === undefined || (collectionId !== null && !collection);
|
|
96
|
+
const visible = home || collectionId === null ? items : items.filter(item => collection?.itemIds.includes(item.id));
|
|
97
|
+
return { home, collection, visible, selected: home ? undefined : visible.find(item => item.id === selectedItemId) };
|
|
98
|
+
}
|
|
99
|
+
export function resolveSearchScreenPhase(query, committedQuery) {
|
|
100
|
+
const typed = query.trim();
|
|
101
|
+
if (!typed)
|
|
102
|
+
return "idle";
|
|
103
|
+
if (committedQuery === undefined)
|
|
104
|
+
return "results";
|
|
105
|
+
return typed === committedQuery.trim() ? "results" : "typing";
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The value a commit records, or `null` when there is nothing to commit. Blank commits are dropped
|
|
109
|
+
* so a stray Enter never writes an empty recent search or fetches "everything".
|
|
110
|
+
*/
|
|
111
|
+
export function resolveSearchCommit(value) {
|
|
112
|
+
const next = value.trim();
|
|
113
|
+
return next ? next : null;
|
|
114
|
+
}
|
|
115
|
+
export function resolveSearchEmptyCause(count, appliedFilterCount) {
|
|
116
|
+
if (count === null || count > 0)
|
|
117
|
+
return "none";
|
|
118
|
+
if (count < 0 || !Number.isSafeInteger(count) || !Number.isSafeInteger(appliedFilterCount) || appliedFilterCount < 0) {
|
|
119
|
+
throw new RangeError("Search counts must be nonnegative integers");
|
|
120
|
+
}
|
|
121
|
+
return appliedFilterCount > 0 ? "filters" : "query";
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Index to focus after removing item `removedIndex` from a list that now has `remaining` items:
|
|
125
|
+
* the item that slid into its place, else the new last item, else -1 (move to the fallback target).
|
|
126
|
+
* Removing a focused chip or row would otherwise drop focus to <body>.
|
|
127
|
+
*/
|
|
128
|
+
export function resolveFocusAfterRemoval(removedIndex, remaining) {
|
|
129
|
+
if (!Number.isInteger(removedIndex) || removedIndex < 0 || !Number.isInteger(remaining) || remaining < 0) {
|
|
130
|
+
throw new RangeError("Removal indexes must be nonnegative integers");
|
|
131
|
+
}
|
|
132
|
+
return remaining === 0 ? -1 : Math.min(removedIndex, remaining - 1);
|
|
133
|
+
}
|
|
134
|
+
/** Recipe for the SearchScreen phase sections; geometry stays on shared spacing tokens. */
|
|
135
|
+
export const searchScreenRecipe = {
|
|
136
|
+
/** Recent searches shown before the user types; reference services show 5–10 and let the rest scroll away. */
|
|
137
|
+
recentVisible: 5,
|
|
138
|
+
/** Suggestions under the "search for ‘q’" row. */
|
|
139
|
+
suggestionVisible: 6,
|
|
140
|
+
/** Skeleton rows while the first page of results loads. */
|
|
141
|
+
loadingRows: 4,
|
|
142
|
+
/** Between idle sections (recent, suggested). */
|
|
143
|
+
sectionGap: spacing.xl,
|
|
144
|
+
/** Section title to its list, and results header to applied filters. */
|
|
145
|
+
headerGap: spacing.sm,
|
|
146
|
+
/** Between chips in applied filters and suggested queries. */
|
|
147
|
+
chipGap: spacing.xs,
|
|
148
|
+
};
|
|
149
|
+
//# sourceMappingURL=screen-patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screen-patterns.js","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAOnD,MAAM,UAAU,yBAAyB,CAAC,QAA4B,EAAE,IAAI,EAAE,OAAO,EAAE;IACrF,IAAI,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/E,MAAM,IAAI,SAAS,CAAC,8BAA8B,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAClD,MAAM,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;IACjE,CAAC;IACD,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,eAAe,EAAE,KAAK,CAAC,IAAI,KAAK,OAAO;QACvC,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,SAAS;QAC9B,YAAY,EAAE,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ;KACrD,CAAC;AACb,CAAC;AAED,qGAAqG;AACrG,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC,eAAe;IAChC,OAAO,EAAE,OAAO,CAAC,EAAE;IACnB,UAAU,EAAE,OAAO,CAAC,EAAE;IACtB,OAAO,EAAE,OAAO,CAAC,EAAE;IACnB,QAAQ,EAAE,OAAO,CAAC,EAAE;IACpB,oIAAoI;IACpI,cAAc,EAAE,MAAM,CAAC,eAAe,GAAG,CAAC;IAC1C,4FAA4F;IAC5F,eAAe,EAAE,KAAK;IACtB,wFAAwF;IACxF,cAAc,EAAE,EAAE;IAClB,oBAAoB,EAAE,EAAE;IACxB,cAAc,EAAE,GAAG;IACnB,qBAAqB,EAAE,EAAE;IACzB,iBAAiB,EAAE,GAAG;IACtB,kBAAkB,EAAE,EAAE;IACtB,gBAAgB,EAAE,CAAC;IACnB,gBAAgB,EAAE,CAAC;CACX,CAAC;AAWX,MAAM,UAAU,gBAAgB,CAAC,EAAE,KAAK,EAAE,QAAQ,GAAG,KAAK,EAAE,OAAO,GAAG,KAAK,EAAE,eAAe,GAAG,CAAC,EAA6B;IAC3H,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,eAAe,CAAC,IAAI,eAAe,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,gDAAgD,CAAC,CAAC;IAC1I,OAAO,CAAC,QAAQ,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,eAAe,GAAG,CAAC,CAAC,CAAC;AACnF,CAAC;AAYD,MAAM,UAAU,uBAAuB,CAAC,MAA6B;IACnE,QAAO,MAAM,EAAC,CAAC;QAAA,KAAK,QAAQ,CAAC,CAAA,OAAO,SAAS,CAAC;QAAA,KAAK,QAAQ,CAAC,CAAA,OAAO,UAAU,CAAC;QAAA,KAAK,SAAS,CAAC,CAAA,OAAO,UAAU,CAAC;QAAA,KAAK,aAAa,CAAC,CAAA,OAAO,IAAI,CAAC;QAAA,OAAO,CAAC,CAAA,MAAM,IAAI,SAAS,CAAC,0BAA0B,CAAC,CAAC;IAAA,CAAC;AACzM,CAAC;AACD,MAAM,UAAU,qBAAqB,CAAC,KAAY,EAAC,KAAY;IAC9D,IAAG,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAE,KAAK,GAAC,CAAC,IAAE,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAE,KAAK,GAAC,CAAC,IAAE,KAAK,IAAE,KAAK;QAAC,MAAM,IAAI,UAAU,CAAC,wCAAwC,CAAC,CAAC;IACrJ,OAAO,EAAC,KAAK,EAAC,KAAK,KAAG,CAAC,EAAC,IAAI,EAAC,KAAK,KAAG,KAAK,GAAC,CAAC,EAAC,CAAC;AAC/C,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,qBAAqB,CAAC,KAAiD;IACtF,MAAM,KAAK,GAAC,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAA,EAAE,CAAA,IAAI,CAAC,QAAQ,KAAG,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAA,EAAE,CAAA,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAAA,MAAM,GAAG,GAAC,IAAI,GAAG,EAAU,CAAC;IAC7G,KAAI,MAAM,IAAI,IAAI,KAAK,EAAC,CAAC;QAAA,IAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAAC,MAAM,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;QAAA,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAAA,IAAG,IAAI,CAAC,QAAQ,KAAG,IAAI,IAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAE,IAAI,CAAC,QAAQ,KAAG,IAAI,CAAC,EAAE,CAAC;YAAC,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;IAAA,CAAC;AAC9R,CAAC;AAID,MAAM,UAAU,0BAA0B,CAAC,WAAmD;IAC5F,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;YAAE,MAAM,IAAI,SAAS,CAAC,6DAA6D,CAAC,CAAC;QACxJ,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnB,CAAC;AACH,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,YAAY,CAAC,EAAU,EAAE,EAAU;IACjD,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,mBAAmB,CAAC,kBAAkB,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;AACnG,CAAC;AAUD,oGAAoG;AACpG,MAAM,UAAU,iBAAiB,CAAsB,KAAkB,EAAC,WAAsC,EAAC,YAAkC,EAAC,cAAoC;IACtL,MAAM,GAAG,GAAC,IAAI,GAAG,EAAU,EAAC,MAAM,GAAC,IAAI,GAAG,EAAU,CAAC;IACrD,KAAI,MAAM,IAAI,IAAI,KAAK,EAAC,CAAC;QAAA,IAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YAAC,MAAM,IAAI,SAAS,CAAC,oDAAoD,CAAC,CAAC;QAAA,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAAA,CAAC;IAC9K,KAAI,MAAM,KAAK,IAAI,WAAW,EAAC,CAAC;QAAA,IAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,IAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE;YAAC,MAAM,IAAI,SAAS,CAAC,0DAA0D,CAAC,CAAC;QAAA,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAAA,CAAC;IACrM,MAAM,UAAU,GAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAA,EAAE,CAAA,IAAI,CAAC,EAAE,KAAG,YAAY,CAAC,CAAC;IAChE,+FAA+F;IAC/F,MAAM,IAAI,GAAC,YAAY,KAAG,SAAS,IAAE,CAAC,YAAY,KAAG,IAAI,IAAE,CAAC,UAAU,CAAC,CAAC;IACxE,MAAM,OAAO,GAAC,IAAI,IAAE,YAAY,KAAG,IAAI,CAAA,CAAC,CAAA,KAAK,CAAA,CAAC,CAAA,KAAK,CAAC,MAAM,CAAC,IAAI,CAAA,EAAE,CAAA,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACxG,OAAO,EAAC,IAAI,EAAC,UAAU,EAAC,OAAO,EAAC,QAAQ,EAAC,IAAI,CAAA,CAAC,CAAA,SAAS,CAAA,CAAC,CAAA,OAAO,CAAC,IAAI,CAAC,IAAI,CAAA,EAAE,CAAA,IAAI,CAAC,EAAE,KAAG,cAAc,CAAC,EAAC,CAAC;AACxG,CAAC;AAUD,MAAM,UAAU,wBAAwB,CAAC,KAAa,EAAE,cAAuB;IAC7E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,CAAC,KAAK;QAAE,OAAO,MAAM,CAAC;IAC1B,IAAI,cAAc,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACnD,OAAO,KAAK,KAAK,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;AAChE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC1B,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5B,CAAC;AASD,MAAM,UAAU,uBAAuB,CAAC,KAAoB,EAAE,kBAA0B;IACtF,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAC/C,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,kBAAkB,CAAC,IAAI,kBAAkB,GAAG,CAAC,EAAE,CAAC;QACrH,MAAM,IAAI,UAAU,CAAC,4CAA4C,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,kBAAkB,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,YAAoB,EAAE,SAAiB;IAC9E,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,YAAY,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QACzG,MAAM,IAAI,UAAU,CAAC,8CAA8C,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,8GAA8G;IAC9G,aAAa,EAAE,CAAC;IAChB,kDAAkD;IAClD,iBAAiB,EAAE,CAAC;IACpB,2DAA2D;IAC3D,WAAW,EAAE,CAAC;IACd,iDAAiD;IACjD,UAAU,EAAE,OAAO,CAAC,EAAE;IACtB,wEAAwE;IACxE,SAAS,EAAE,OAAO,CAAC,EAAE;IACrB,8DAA8D;IAC9D,OAAO,EAAE,OAAO,CAAC,EAAE;CACX,CAAC","sourcesContent":["import { layout, spacing } from \"./foundations.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// 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 disabled?: boolean;\n pending?: boolean;\n attachmentCount?: number;\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/** 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}>;\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/** 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 }>;\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 if (count === null || count > 0) return \"none\";\n if (count < 0 || !Number.isSafeInteger(count) || !Number.isSafeInteger(appliedFilterCount) || appliedFilterCount < 0) {\n throw new RangeError(\"Search counts must be nonnegative integers\");\n }\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 /** 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"]}
|
package/dist/slider.d.ts
CHANGED
|
@@ -112,6 +112,14 @@ export declare const sliderRecipe: {
|
|
|
112
112
|
readonly draggedOpacity: 0.64;
|
|
113
113
|
};
|
|
114
114
|
readonly radius: "full";
|
|
115
|
+
/**
|
|
116
|
+
* Header row [label ……… value] and its distance to the track. Added 2026-10-06: Web drew a 16px
|
|
117
|
+
* label–value gap and Native none, so a long label ran into the value on Native.
|
|
118
|
+
*/
|
|
119
|
+
readonly header: {
|
|
120
|
+
readonly gap: 16;
|
|
121
|
+
readonly trackGap: 8;
|
|
122
|
+
};
|
|
115
123
|
};
|
|
116
124
|
export declare const sliderBehavior: {
|
|
117
125
|
readonly controlled: readonly ["value", "defaultValue", "onValueChange"];
|
package/dist/slider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slider.d.ts","sourceRoot":"","sources":["../src/slider.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,GACnE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAQ7B,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAU3E;AAED,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,gBAAgB,GAC3B,wBAAwB,CAG1B;AAED,+FAA+F;AAC/F,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,gBAAgB,GAAG,MAAM,CAG9E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,gBAAgB,EAC5B,SAAS,EAAE,MAAM,GAChB,MAAM,CAMR;AAED,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,CAAC;AAE5C;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,gBAAgB,EAC5B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,eAAe,GACzB,MAAM,CAWR;AAED,MAAM,MAAM,gBAAgB,GACxB,WAAW,GACX,WAAW,GACX,gBAAgB,GAChB,gBAAgB,GAChB,OAAO,GACP,MAAM,CAAC;AAEX,eAAO,MAAM,sBAAsB;;CAEsB,CAAC;AAE1D;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,gBAAgB,EAC5B,MAAM,EAAE,gBAAgB,GACvB,MAAM,CAcR;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;AAElC,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"slider.d.ts","sourceRoot":"","sources":["../src/slider.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,GACnE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAQ7B,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAU3E;AAED,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,gBAAgB,GAC3B,wBAAwB,CAG1B;AAED,+FAA+F;AAC/F,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,gBAAgB,GAAG,MAAM,CAG9E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,gBAAgB,EAC5B,SAAS,EAAE,MAAM,GAChB,MAAM,CAMR;AAED,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,CAAC;AAE5C;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,gBAAgB,EAC5B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,eAAe,GACzB,MAAM,CAWR;AAED,MAAM,MAAM,gBAAgB,GACxB,WAAW,GACX,WAAW,GACX,gBAAgB,GAChB,gBAAgB,GAChB,OAAO,GACP,MAAM,CAAC;AAEX,eAAO,MAAM,sBAAsB;;CAEsB,CAAC;AAE1D;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,gBAAgB,EAC5B,MAAM,EAAE,gBAAgB,GACvB,MAAM,CAcR;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;AAElC,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA2BvB;;;OAGG;;;;;CA+BJ,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;CAyCU,CAAC"}
|
package/dist/slider.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { focusIndicatorContract } from "./component-contracts.js";
|
|
2
|
-
import { control, opacity } from "./foundations.js";
|
|
2
|
+
import { control, opacity, spacing } from "./foundations.js";
|
|
3
3
|
import { assertFiniteNumber, numericRangeDefaults, snapToStep, stepNumericValue, validateNumericRangeConfig, } from "./number-field.js";
|
|
4
4
|
import { semanticColors } from "./semantic-colors.js";
|
|
5
5
|
function assertNonEmptyCopy(value, field) {
|
|
@@ -104,6 +104,11 @@ export const sliderRecipe = {
|
|
|
104
104
|
draggedOpacity: opacity.dragged,
|
|
105
105
|
},
|
|
106
106
|
radius: "full",
|
|
107
|
+
/**
|
|
108
|
+
* Header row [label ……… value] and its distance to the track. Added 2026-10-06: Web drew a 16px
|
|
109
|
+
* label–value gap and Native none, so a long label ran into the value on Native.
|
|
110
|
+
*/
|
|
111
|
+
header: { gap: spacing.md, trackGap: spacing.xs },
|
|
107
112
|
};
|
|
108
113
|
export const sliderBehavior = {
|
|
109
114
|
controlled: ["value", "defaultValue", "onValueChange"],
|
package/dist/slider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slider.js","sourceRoot":"","sources":["../src/slider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAoB,MAAM,kBAAkB,CAAC;AACtE,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,UAAU,EACV,gBAAgB,EAChB,0BAA0B,GAE3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAyBtD,SAAS,kBAAkB,CAAC,KAAa,EAAE,KAAa;IACtD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,SAAS,CAAC,UAAU,KAAK,oBAAoB,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,UAA4B;IACnE,kBAAkB,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC9C,0BAA0B,CAAC,UAAU,CAAC,CAAC;IACvC,kBAAkB,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAI,UAAU,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,IAAI,UAAU,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC;QAC3E,MAAM,IAAI,UAAU,CAAC,yCAAyC,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,UAAU,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACvC,kBAAkB,CAAC,UAAU,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,UAA4B;IAE5B,wBAAwB,CAAC,UAAU,CAAC,CAAC;IACrC,OAAO,EAAE,GAAG,UAAU,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,IAAI,oBAAoB,CAAC,IAAI,EAAE,CAAC;AAC/E,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,yBAAyB,CAAC,UAA4B;IACpE,wBAAwB,CAAC,UAAU,CAAC,CAAC;IACrC,OAAO,CAAC,UAAU,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;AACjF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAA4B,EAC5B,SAAiB;IAEjB,wBAAwB,CAAC,UAAU,CAAC,CAAC;IACrC,kBAAkB,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAC3C,IAAI,SAAS,IAAI,UAAU,CAAC,GAAG;QAAE,OAAO,UAAU,CAAC,GAAG,CAAC;IACvD,IAAI,SAAS,IAAI,UAAU,CAAC,GAAG;QAAE,OAAO,UAAU,CAAC,GAAG,CAAC;IACvD,OAAO,UAAU,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;AAC3C,CAAC;AAID;;;GAGG;AACH,MAAM,UAAU,4BAA4B,CAC1C,UAA4B,EAC5B,MAAc,EACd,MAAc,EACd,SAA0B;IAE1B,wBAAwB,CAAC,UAAU,CAAC,CAAC;IACrC,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrC,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrC,IAAI,MAAM,IAAI,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,sCAAsC,CAAC,CAAC;IAC9E,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IACnE,MAAM,eAAe,GAAG,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,gBAAgB,CAAC;IACtF,OAAO,kBAAkB,CACvB,UAAU,EACV,UAAU,CAAC,GAAG,GAAG,eAAe,GAAG,CAAC,UAAU,CAAC,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,CACrE,CAAC;AACJ,CAAC;AAUD,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,cAAc,EAAE,EAAE;CACqC,CAAC;AAE1D;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,UAA4B,EAC5B,MAAwB;IAExB,wBAAwB,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,UAAU,CAAC,GAAG,CAAC;IAC9C,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,UAAU,CAAC,GAAG,CAAC;IAC7C,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,IAAI,oBAAoB,CAAC,IAAI,CAAC;IAC1D,MAAM,MAAM,GAAuB,EAAE,GAAG,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IACtF,IAAI,MAAM,KAAK,WAAW,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;QACrD,OAAO,gBAAgB,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,KAAK,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,OAAO,kBAAkB,CACvB,UAAU,EACV,UAAU,CAAC,KAAK,GAAG,SAAS,GAAG,IAAI,GAAG,sBAAsB,CAAC,cAAc,CAC5E,CAAC;AACJ,CAAC;AAID,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,CAAU;IAChF,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;IAC5B,KAAK,EAAE;QACL,MAAM,EAAE;YACN,WAAW,EAAE,CAAC;YACd,aAAa,EAAE,EAAE;YACjB,SAAS,EAAE,OAAO,CAAC,cAAc;YACjC,YAAY,EAAE,MAAM;YACpB,iBAAiB,EAAE,OAAO;SAC3B;KACF;IACD,MAAM,EAAE;QACN,WAAW,EAAE,cAAc,CAAC,OAAO,CAAC,KAAK;QACzC,aAAa,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM;QAC5C,mBAAmB,EAAE,cAAc,CAAC,OAAO,CAAC,SAAS;QACrD,KAAK,EAAE,cAAc,CAAC,MAAM;QAC5B,WAAW,EAAE,cAAc,CAAC,MAAM,CAAC,KAAK;QACxC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,IAAI;QAClC,UAAU,EAAE,cAAc,CAAC,OAAO,CAAC,SAAS;KAC7C;IACD,MAAM,EAAE;QACN,KAAK,EAAE,sBAAsB;QAC7B,eAAe,EAAE,OAAO,CAAC,QAAQ;QACjC,cAAc,EAAE,OAAO,CAAC,OAAO;KAChC;IACD,MAAM,EAAE,MAAM;CA6Bf,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,UAAU,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,eAAe,CAAC;IACtD,MAAM,EAAE,CAAC,kBAAkB,CAAC;IAC5B,QAAQ,EAAE,sBAAsB;IAChC,SAAS,EAAE;QACT,WAAW,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,CAAC;QACpE,YAAY,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;KACtC;IACD,GAAG,EAAE;QACH,KAAK,EAAE,CAAC,QAAQ,CAAC;QACjB,QAAQ,EAAE;YACR,KAAK;YACL,WAAW;YACX,YAAY;YACZ,SAAS;YACT,WAAW;YACX,MAAM;YACN,KAAK;YACL,QAAQ;YACR,UAAU;SACX;QACD,KAAK,EAAE,QAAQ;KAChB;IACD,MAAM,EAAE;QACN,KAAK,EAAE,CAAC,YAAY,CAAC;QACrB,MAAM,EAAE,CAAC,UAAU,CAAC;QACpB,OAAO,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC;KACpC;IACD,SAAS,EAAE;QACT,gDAAgD;QAChD,kCAAkC;QAClC,uCAAuC;QACvC,kCAAkC;QAClC,mEAAmE;QACnE,2DAA2D;QAC3D,2DAA2D;QAC3D,kEAAkE;QAClE,wCAAwC;QACxC,wCAAwC;QACxC,6EAA6E;KAC9E;CACkC,CAAC","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { BehaviorContract } from \"./behaviors.js\";\nimport { focusIndicatorContract } from \"./component-contracts.js\";\nimport { control, opacity, type TextVariant } from \"./foundations.js\";\nimport {\n assertFiniteNumber,\n numericRangeDefaults,\n snapToStep,\n stepNumericValue,\n validateNumericRangeConfig,\n type NumericRangeConfig,\n} from \"./number-field.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * Slider always has a value — there is no `empty` axis to model, unlike a\n * NumberField that can start unset. Range selection (two handles) is out of\n * scope until a real product needs it; adding it speculatively would mean\n * guessing at a value-pair contract nobody has validated.\n */\nexport type SliderDescriptor = Readonly<{\n /** Visible or accessibility label; announced before the value. */\n label: string;\n value: number;\n min: number;\n max: number;\n step?: number;\n /**\n * Product-formatted copy overriding the raw numeric announcement, e.g.\n * \"75점\" or \".357\". Slider never formats numbers itself.\n */\n valueText?: string;\n}>;\n\nexport type ResolvedSliderDescriptor = Omit<SliderDescriptor, \"step\"> &\n Readonly<{ step: number }>;\n\nfunction assertNonEmptyCopy(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Slider ${field} must not be empty`);\n }\n}\n\nexport function validateSliderDescriptor(descriptor: SliderDescriptor): void {\n assertNonEmptyCopy(descriptor.label, \"label\");\n validateNumericRangeConfig(descriptor);\n assertFiniteNumber(descriptor.value, \"value\");\n if (descriptor.value < descriptor.min || descriptor.value > descriptor.max) {\n throw new RangeError(\"Slider value must be within min and max\");\n }\n if (descriptor.valueText !== undefined) {\n assertNonEmptyCopy(descriptor.valueText, \"valueText\");\n }\n}\n\nexport function resolveSliderDescriptor(\n descriptor: SliderDescriptor,\n): ResolvedSliderDescriptor {\n validateSliderDescriptor(descriptor);\n return { ...descriptor, step: descriptor.step ?? numericRangeDefaults.step };\n}\n\n/** 0..1 position of `value` between `min` and `max`, for track fill — not a display string. */\nexport function resolveSliderFillFraction(descriptor: SliderDescriptor): number {\n validateSliderDescriptor(descriptor);\n return (descriptor.value - descriptor.min) / (descriptor.max - descriptor.min);\n}\n\n/**\n * Snaps user-originated input with NumberField's min-origin range math while\n * preserving Slider's exact endpoints. Public controlled values may be\n * off-grid; they remain untouched until a user interaction proposes a value.\n */\nexport function resolveSliderValue(\n descriptor: SliderDescriptor,\n candidate: number,\n): number {\n validateSliderDescriptor(descriptor);\n assertFiniteNumber(candidate, \"candidate\");\n if (candidate <= descriptor.min) return descriptor.min;\n if (candidate >= descriptor.max) return descriptor.max;\n return snapToStep(candidate, descriptor);\n}\n\nexport type SliderDirection = \"ltr\" | \"rtl\";\n\n/**\n * Maps a pointer/touch offset to a min-origin step. RTL mirrors the physical\n * track while preserving the logical meaning of increment/decrement.\n */\nexport function resolveSliderValueFromOffset(\n descriptor: SliderDescriptor,\n offset: number,\n extent: number,\n direction: SliderDirection,\n): number {\n validateSliderDescriptor(descriptor);\n assertFiniteNumber(offset, \"offset\");\n assertFiniteNumber(extent, \"extent\");\n if (extent <= 0) throw new RangeError(\"Slider extent must be greater than 0\");\n const physicalFraction = Math.min(1, Math.max(0, offset / extent));\n const logicalFraction = direction === \"rtl\" ? 1 - physicalFraction : physicalFraction;\n return resolveSliderValue(\n descriptor,\n descriptor.min + logicalFraction * (descriptor.max - descriptor.min),\n );\n}\n\nexport type SliderStepIntent =\n | \"increment\"\n | \"decrement\"\n | \"increment-page\"\n | \"decrement-page\"\n | \"first\"\n | \"last\";\n\nexport const sliderBehaviorDefaults = {\n pageMultiplier: 10,\n} as const satisfies Readonly<{ pageMultiplier: number }>;\n\n/**\n * One function for every keyboard/RN step intent so Web arrow/Home/End/\n * PageUp/PageDown and RN increment/decrement actions resolve through the same\n * snap-and-clamp judgment as the stepper itself.\n */\nexport function getSliderStepTarget(\n descriptor: SliderDescriptor,\n intent: SliderStepIntent,\n): number {\n validateSliderDescriptor(descriptor);\n if (intent === \"first\") return descriptor.min;\n if (intent === \"last\") return descriptor.max;\n const step = descriptor.step ?? numericRangeDefaults.step;\n const config: NumericRangeConfig = { min: descriptor.min, max: descriptor.max, step };\n if (intent === \"increment\" || intent === \"decrement\") {\n return stepNumericValue(descriptor.value, config, intent);\n }\n const direction = intent === \"increment-page\" ? 1 : -1;\n return resolveSliderValue(\n descriptor,\n descriptor.value + direction * step * sliderBehaviorDefaults.pageMultiplier,\n );\n}\n\nexport type SliderSize = \"medium\";\n\nexport const sliderRecipe = {\n slots: [\"root\", \"track\", \"filledTrack\", \"thumb\", \"label\", \"valueLabel\"] as const,\n defaults: { size: \"medium\" },\n sizes: {\n medium: {\n trackHeight: 4,\n thumbDiameter: 20,\n hitTarget: control.minTouchTarget,\n labelVariant: \"body\",\n valueLabelVariant: \"label\",\n },\n },\n colors: {\n trackFilled: semanticColors.content.brand,\n trackUnfilled: semanticColors.surface.sunken,\n trackUnfilledBorder: semanticColors.content.secondary,\n thumb: semanticColors.canvas,\n thumbBorder: semanticColors.border.focus,\n label: semanticColors.content.body,\n valueLabel: semanticColors.content.secondary,\n },\n states: {\n focus: focusIndicatorContract,\n disabledOpacity: opacity.disabled,\n draggedOpacity: opacity.dragged,\n },\n radius: \"full\",\n} as const satisfies {\n slots: readonly [\"root\", \"track\", \"filledTrack\", \"thumb\", \"label\", \"valueLabel\"];\n defaults: { size: SliderSize };\n sizes: Record<\n SliderSize,\n {\n trackHeight: number;\n thumbDiameter: number;\n hitTarget: number;\n labelVariant: TextVariant;\n valueLabelVariant: TextVariant;\n }\n >;\n colors: {\n trackFilled: ColorReference;\n trackUnfilled: ColorReference;\n trackUnfilledBorder: ColorReference;\n thumb: ColorReference;\n thumbBorder: ColorReference;\n label: ColorReference;\n valueLabel: ColorReference;\n };\n states: {\n focus: typeof focusIndicatorContract;\n disabledOpacity: number;\n draggedOpacity: number;\n };\n radius: \"full\";\n};\n\nexport const sliderBehavior = {\n controlled: [\"value\", \"defaultValue\", \"onValueChange\"],\n events: [\"onValueChangeEnd\"],\n defaults: sliderBehaviorDefaults,\n stateAxes: {\n interaction: [\"idle\", \"hover\", \"focusVisible\", \"pressed\", \"dragged\"],\n availability: [\"enabled\", \"disabled\"],\n },\n web: {\n roles: [\"slider\"],\n keyboard: [\n \"Tab\",\n \"ArrowLeft\",\n \"ArrowRight\",\n \"ArrowUp\",\n \"ArrowDown\",\n \"Home\",\n \"End\",\n \"PageUp\",\n \"PageDown\",\n ],\n focus: \"native\",\n },\n native: {\n roles: [\"adjustable\"],\n states: [\"disabled\"],\n actions: [\"increment\", \"decrement\"],\n },\n scenarios: [\n \"label-then-value-then-range-announcement-order\",\n \"arrow-keys-step-by-one-increment\",\n \"page-keys-step-by-the-page-multiplier\",\n \"home-and-end-jump-to-min-and-max\",\n \"rtl-mirrors-the-track-without-reversing-logical-increment-actions\",\n \"continuous-change-and-interaction-end-are-separate-events\",\n \"non-divisible-ranges-preserve-exact-min-and-max-endpoints\",\n \"native-disable-during-drag-finishes-once-and-ignores-later-moves\",\n \"value-is-always-present-no-empty-state\",\n \"fill-color-is-never-the-only-value-cue\",\n \"range-two-handle-selection-is-out-of-scope-until-a-real-product-need-exists\",\n ],\n} as const satisfies BehaviorContract;\n"]}
|
|
1
|
+
{"version":3,"file":"slider.js","sourceRoot":"","sources":["../src/slider.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { BehaviorContract } from \"./behaviors.js\";\nimport { focusIndicatorContract } from \"./component-contracts.js\";\nimport { control, opacity, spacing, type TextVariant } from \"./foundations.js\";\nimport {\n assertFiniteNumber,\n numericRangeDefaults,\n snapToStep,\n stepNumericValue,\n validateNumericRangeConfig,\n type NumericRangeConfig,\n} from \"./number-field.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * Slider always has a value — there is no `empty` axis to model, unlike a\n * NumberField that can start unset. Range selection (two handles) is out of\n * scope until a real product needs it; adding it speculatively would mean\n * guessing at a value-pair contract nobody has validated.\n */\nexport type SliderDescriptor = Readonly<{\n /** Visible or accessibility label; announced before the value. */\n label: string;\n value: number;\n min: number;\n max: number;\n step?: number;\n /**\n * Product-formatted copy overriding the raw numeric announcement, e.g.\n * \"75점\" or \".357\". Slider never formats numbers itself.\n */\n valueText?: string;\n}>;\n\nexport type ResolvedSliderDescriptor = Omit<SliderDescriptor, \"step\"> &\n Readonly<{ step: number }>;\n\nfunction assertNonEmptyCopy(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Slider ${field} must not be empty`);\n }\n}\n\nexport function validateSliderDescriptor(descriptor: SliderDescriptor): void {\n assertNonEmptyCopy(descriptor.label, \"label\");\n validateNumericRangeConfig(descriptor);\n assertFiniteNumber(descriptor.value, \"value\");\n if (descriptor.value < descriptor.min || descriptor.value > descriptor.max) {\n throw new RangeError(\"Slider value must be within min and max\");\n }\n if (descriptor.valueText !== undefined) {\n assertNonEmptyCopy(descriptor.valueText, \"valueText\");\n }\n}\n\nexport function resolveSliderDescriptor(\n descriptor: SliderDescriptor,\n): ResolvedSliderDescriptor {\n validateSliderDescriptor(descriptor);\n return { ...descriptor, step: descriptor.step ?? numericRangeDefaults.step };\n}\n\n/** 0..1 position of `value` between `min` and `max`, for track fill — not a display string. */\nexport function resolveSliderFillFraction(descriptor: SliderDescriptor): number {\n validateSliderDescriptor(descriptor);\n return (descriptor.value - descriptor.min) / (descriptor.max - descriptor.min);\n}\n\n/**\n * Snaps user-originated input with NumberField's min-origin range math while\n * preserving Slider's exact endpoints. Public controlled values may be\n * off-grid; they remain untouched until a user interaction proposes a value.\n */\nexport function resolveSliderValue(\n descriptor: SliderDescriptor,\n candidate: number,\n): number {\n validateSliderDescriptor(descriptor);\n assertFiniteNumber(candidate, \"candidate\");\n if (candidate <= descriptor.min) return descriptor.min;\n if (candidate >= descriptor.max) return descriptor.max;\n return snapToStep(candidate, descriptor);\n}\n\nexport type SliderDirection = \"ltr\" | \"rtl\";\n\n/**\n * Maps a pointer/touch offset to a min-origin step. RTL mirrors the physical\n * track while preserving the logical meaning of increment/decrement.\n */\nexport function resolveSliderValueFromOffset(\n descriptor: SliderDescriptor,\n offset: number,\n extent: number,\n direction: SliderDirection,\n): number {\n validateSliderDescriptor(descriptor);\n assertFiniteNumber(offset, \"offset\");\n assertFiniteNumber(extent, \"extent\");\n if (extent <= 0) throw new RangeError(\"Slider extent must be greater than 0\");\n const physicalFraction = Math.min(1, Math.max(0, offset / extent));\n const logicalFraction = direction === \"rtl\" ? 1 - physicalFraction : physicalFraction;\n return resolveSliderValue(\n descriptor,\n descriptor.min + logicalFraction * (descriptor.max - descriptor.min),\n );\n}\n\nexport type SliderStepIntent =\n | \"increment\"\n | \"decrement\"\n | \"increment-page\"\n | \"decrement-page\"\n | \"first\"\n | \"last\";\n\nexport const sliderBehaviorDefaults = {\n pageMultiplier: 10,\n} as const satisfies Readonly<{ pageMultiplier: number }>;\n\n/**\n * One function for every keyboard/RN step intent so Web arrow/Home/End/\n * PageUp/PageDown and RN increment/decrement actions resolve through the same\n * snap-and-clamp judgment as the stepper itself.\n */\nexport function getSliderStepTarget(\n descriptor: SliderDescriptor,\n intent: SliderStepIntent,\n): number {\n validateSliderDescriptor(descriptor);\n if (intent === \"first\") return descriptor.min;\n if (intent === \"last\") return descriptor.max;\n const step = descriptor.step ?? numericRangeDefaults.step;\n const config: NumericRangeConfig = { min: descriptor.min, max: descriptor.max, step };\n if (intent === \"increment\" || intent === \"decrement\") {\n return stepNumericValue(descriptor.value, config, intent);\n }\n const direction = intent === \"increment-page\" ? 1 : -1;\n return resolveSliderValue(\n descriptor,\n descriptor.value + direction * step * sliderBehaviorDefaults.pageMultiplier,\n );\n}\n\nexport type SliderSize = \"medium\";\n\nexport const sliderRecipe = {\n slots: [\"root\", \"track\", \"filledTrack\", \"thumb\", \"label\", \"valueLabel\"] as const,\n defaults: { size: \"medium\" },\n sizes: {\n medium: {\n trackHeight: 4,\n thumbDiameter: 20,\n hitTarget: control.minTouchTarget,\n labelVariant: \"body\",\n valueLabelVariant: \"label\",\n },\n },\n colors: {\n trackFilled: semanticColors.content.brand,\n trackUnfilled: semanticColors.surface.sunken,\n trackUnfilledBorder: semanticColors.content.secondary,\n thumb: semanticColors.canvas,\n thumbBorder: semanticColors.border.focus,\n label: semanticColors.content.body,\n valueLabel: semanticColors.content.secondary,\n },\n states: {\n focus: focusIndicatorContract,\n disabledOpacity: opacity.disabled,\n draggedOpacity: opacity.dragged,\n },\n radius: \"full\",\n /**\n * Header row [label ……… value] and its distance to the track. Added 2026-10-06: Web drew a 16px\n * label–value gap and Native none, so a long label ran into the value on Native.\n */\n header: { gap: spacing.md, trackGap: spacing.xs },\n} as const satisfies {\n slots: readonly [\"root\", \"track\", \"filledTrack\", \"thumb\", \"label\", \"valueLabel\"];\n defaults: { size: SliderSize };\n sizes: Record<\n SliderSize,\n {\n trackHeight: number;\n thumbDiameter: number;\n hitTarget: number;\n labelVariant: TextVariant;\n valueLabelVariant: TextVariant;\n }\n >;\n colors: {\n trackFilled: ColorReference;\n trackUnfilled: ColorReference;\n trackUnfilledBorder: ColorReference;\n thumb: ColorReference;\n thumbBorder: ColorReference;\n label: ColorReference;\n valueLabel: ColorReference;\n };\n states: {\n focus: typeof focusIndicatorContract;\n disabledOpacity: number;\n draggedOpacity: number;\n };\n header: { gap: number; trackGap: number };\n radius: \"full\";\n};\n\nexport const sliderBehavior = {\n controlled: [\"value\", \"defaultValue\", \"onValueChange\"],\n events: [\"onValueChangeEnd\"],\n defaults: sliderBehaviorDefaults,\n stateAxes: {\n interaction: [\"idle\", \"hover\", \"focusVisible\", \"pressed\", \"dragged\"],\n availability: [\"enabled\", \"disabled\"],\n },\n web: {\n roles: [\"slider\"],\n keyboard: [\n \"Tab\",\n \"ArrowLeft\",\n \"ArrowRight\",\n \"ArrowUp\",\n \"ArrowDown\",\n \"Home\",\n \"End\",\n \"PageUp\",\n \"PageDown\",\n ],\n focus: \"native\",\n },\n native: {\n roles: [\"adjustable\"],\n states: [\"disabled\"],\n actions: [\"increment\", \"decrement\"],\n },\n scenarios: [\n \"label-then-value-then-range-announcement-order\",\n \"arrow-keys-step-by-one-increment\",\n \"page-keys-step-by-the-page-multiplier\",\n \"home-and-end-jump-to-min-and-max\",\n \"rtl-mirrors-the-track-without-reversing-logical-increment-actions\",\n \"continuous-change-and-interaction-end-are-separate-events\",\n \"non-divisible-ranges-preserve-exact-min-and-max-endpoints\",\n \"native-disable-during-drag-finishes-once-and-ignores-later-moves\",\n \"value-is-always-present-no-empty-state\",\n \"fill-color-is-never-the-only-value-cue\",\n \"range-two-handle-selection-is-out-of-scope-until-a-real-product-need-exists\",\n ],\n} as const satisfies BehaviorContract;\n"]}
|
package/dist/upload-item.d.ts
CHANGED
|
@@ -73,6 +73,11 @@ export declare const uploadItemRecipe: {
|
|
|
73
73
|
readonly row: {
|
|
74
74
|
readonly minHeight: 68;
|
|
75
75
|
readonly paddingHorizontal: 16;
|
|
76
|
+
/**
|
|
77
|
+
* Added 2026-10-06: Native hard-coded 8 and Web used 12 with a 64px floor, so the same row
|
|
78
|
+
* measured 68 on Native and 64-ish+ on Web. One value now drives both renderers.
|
|
79
|
+
*/
|
|
80
|
+
readonly paddingVertical: 8;
|
|
76
81
|
readonly gap: 12;
|
|
77
82
|
readonly radius: "md";
|
|
78
83
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload-item.d.ts","sourceRoot":"","sources":["../src/upload-item.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAE7E,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAErE,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,MAAM,EAAE,WAAW,CAAC;IACpB,qFAAqF;IACrF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAErE,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,MAAM,EAAE,OAAO,CAAC;IAChB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB,sBAAsB,GACtB,wBAAwB,GACxB,sBAAsB,GACtB,oBAAoB,CAAC;AAEzB,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,eAAe,CAAC;CACxB,CAAC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAQH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAoBpE;AAED,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,oBAAoB,GAAG,IAAI,CAKnF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAIvE;AAED,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,oBAAoB,EAAE,GAAG,IAAI,CASnF;AAED,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,GAAG,IAAI,CAI5F;AAED,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,yFAAyF;IACzF,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,oBAAoB,EAChC,MAAM,EAAE,gBAAgB,GACvB,sBAAsB,CAmBxB;AAED,eAAO,MAAM,gBAAgB
|
|
1
|
+
{"version":3,"file":"upload-item.d.ts","sourceRoot":"","sources":["../src/upload-item.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAE7E,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAErE,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,MAAM,EAAE,WAAW,CAAC;IACpB,qFAAqF;IACrF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAErE,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,MAAM,EAAE,OAAO,CAAC;IAChB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB,sBAAsB,GACtB,wBAAwB,GACxB,sBAAsB,GACtB,oBAAoB,CAAC;AAEzB,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,eAAe,CAAC;CACxB,CAAC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAQH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAoBpE;AAED,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,oBAAoB,GAAG,IAAI,CAKnF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAIvE;AAED,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,oBAAoB,EAAE,GAAG,IAAI,CASnF;AAED,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,GAAG,IAAI,CAI5F;AAED,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,yFAAyF;IACzF,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,oBAAoB,EAChC,MAAM,EAAE,gBAAgB,GACvB,sBAAsB,CAmBxB;AAED,eAAO,MAAM,gBAAgB;;;;;;;;QAMzB;;;WAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAaL,iEAAiE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkClE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;CA0BM,CAAC"}
|
package/dist/upload-item.js
CHANGED
|
@@ -94,6 +94,11 @@ export const uploadItemRecipe = {
|
|
|
94
94
|
row: {
|
|
95
95
|
minHeight: layout.rowHeight.twoLine,
|
|
96
96
|
paddingHorizontal: spacing.md,
|
|
97
|
+
/**
|
|
98
|
+
* Added 2026-10-06: Native hard-coded 8 and Web used 12 with a 64px floor, so the same row
|
|
99
|
+
* measured 68 on Native and 64-ish+ on Web. One value now drives both renderers.
|
|
100
|
+
*/
|
|
101
|
+
paddingVertical: spacing.xs,
|
|
97
102
|
gap: spacing.sm,
|
|
98
103
|
radius: "md",
|
|
99
104
|
},
|