feat(mobile): Wave 4 — RegisterPage responsive + TransferIndicator on settings + MessageInput sheets
- RegisterPage: scroll envelope, iOS-zoom-safe inputs, ≥44px tap targets, mobile-friendly invite chip + swatch row - TransferIndicator: mounted via MobileScreenHeader.rightActions across every settings/instance screen and inline in MobileChatScreen; touch-close listener; viewport-safe panel width - MessageInput popovers (emoji/GIF/mention) now route through new InputPopover wrapper — desktop popover, mobile bottom-sheet - EmojiPicker mobile branch: dynamicWidth + scoped CSS (.emoji-picker-wrapper--mobile) so the <em-emoji-picker> custom element fills the sheet; bigger touch targets (perLine 8, button 40, emoji 28); desktop unchanged - MessageInput trigger row: mobile-first sizing with md: desktop overrides (40×40 buttons + gap on mobile, 34×34 unchanged on desktop) - Spec updates: docs/systems/auth.md (RegisterPage responsive contract), docs/systems/uploads.md (TransferIndicator mobile surfaces)
This commit is contained in:
@@ -4,9 +4,16 @@ import data from '@emoji-mart/data';
|
||||
|
||||
interface EmojiPickerProps {
|
||||
onEmojiSelect: (emoji: { native: string }) => void;
|
||||
/**
|
||||
* Mobile rendering: stretch the picker to fill its container width
|
||||
* (the parent bottom-sheet provides the viewport-wide bounds), use
|
||||
* larger touch targets, and let the picker's own scroll area expand
|
||||
* to consume the available height of the sheet.
|
||||
*/
|
||||
mobile?: boolean;
|
||||
}
|
||||
|
||||
export function EmojiPicker({ onEmojiSelect }: EmojiPickerProps) {
|
||||
export function EmojiPicker({ onEmojiSelect, mobile = false }: EmojiPickerProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Prevent keyboard events from bubbling out (e.g. Enter submitting the chat input)
|
||||
@@ -18,8 +25,24 @@ export function EmojiPicker({ onEmojiSelect }: EmojiPickerProps) {
|
||||
return () => el.removeEventListener('keydown', stop);
|
||||
}, []);
|
||||
|
||||
// emoji-mart's Picker computes its own internal width from
|
||||
// `perLine * emojiButtonSize` UNLESS `dynamicWidth` is set, in which case
|
||||
// it stretches to its parent's width. On mobile we want full-viewport.
|
||||
// The mobile sheet wraps this picker in `flex-1 min-h-0 flex flex-col`,
|
||||
// so we make the wrapper fill that space and tell the picker to expand.
|
||||
//
|
||||
// The `<em-emoji-picker>` custom element has no intrinsic stretch behavior:
|
||||
// even with `dynamicWidth: true` it ships with `display: flex` but no
|
||||
// `width: 100%`, so it shrinks to its perLine*emojiButtonSize content width
|
||||
// unless we force it to fill. The `emoji-picker-wrapper--mobile` modifier
|
||||
// applies that override (see globals.css) — desktop keeps the legacy
|
||||
// intrinsic-width sizing.
|
||||
const wrapperClass = mobile
|
||||
? 'emoji-picker-wrapper emoji-picker-wrapper--mobile flex-1 min-h-0 w-full overflow-hidden'
|
||||
: 'emoji-picker-wrapper';
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="emoji-picker-wrapper">
|
||||
<div ref={containerRef} className={wrapperClass}>
|
||||
<Picker
|
||||
data={data}
|
||||
onEmojiSelect={onEmojiSelect}
|
||||
@@ -28,10 +51,11 @@ export function EmojiPicker({ onEmojiSelect }: EmojiPickerProps) {
|
||||
skinTonePosition="search"
|
||||
previewPosition="none"
|
||||
navPosition="bottom"
|
||||
perLine={10}
|
||||
perLine={mobile ? 8 : 10}
|
||||
maxFrequentRows={2}
|
||||
emojiSize={24}
|
||||
emojiButtonSize={32}
|
||||
emojiSize={mobile ? 28 : 24}
|
||||
emojiButtonSize={mobile ? 40 : 32}
|
||||
dynamicWidth={mobile ? true : false}
|
||||
categories={['frequent', 'people', 'nature', 'foods', 'activity', 'places', 'objects', 'symbols', 'flags']}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user