@tastic/profile 0.1.2 → 0.1.3

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/index.js CHANGED
@@ -237,6 +237,7 @@ var styles2 = import_react_native2.StyleSheet.create({
237
237
  // src/ProfilesManager.tsx
238
238
  var import_auto_paper3 = require("@rific/auto-paper");
239
239
  var import_feedback_press2 = require("@rific/feedback-press");
240
+ var import_focus_chain = require("@rific/focus-chain");
240
241
  var import_toaster = require("@rific/toaster");
241
242
  var import_hud2 = require("@tastic/hud");
242
243
  var import_react2 = require("react");
@@ -383,12 +384,13 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
383
384
  ] });
384
385
  }
385
386
  function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag, onTagChange, tagInvalid, onSubmit, onDelete, autoFocus, fgMuted, host, dark }) {
386
- const tagInputRef = (0, import_react2.useRef)(null);
387
- const nameInputRef = (0, import_react2.useRef)(null);
387
+ const register = (0, import_focus_chain.useFocusChain)();
388
+ const tag = register();
389
+ const name = register();
388
390
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles3.editRow, children: [
389
391
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_hud2.InlineColorPicker, { id: "color", host, value: draftColor, onChange: onColorChange, dark }),
390
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.TextInput, { ref: tagInputRef, mode: "outlined", dense: true, value: draftTag, onChangeText: onTagChange, placeholder: "Tag", autoCapitalize: "characters", selectTextOnFocus: true, error: tagInvalid, returnKeyType: "next", style: styles3.tagInput, contentStyle: styles3.tagInputContent, onSubmitEditing: () => nameInputRef.current?.focus() }),
391
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.TextInput, { ref: nameInputRef, mode: "outlined", dense: true, value: draftName, onChangeText: onNameChange, maxLength: MAX_PROFILE_NAME_LENGTH, placeholder: "Name", autoFocus, returnKeyType: "done", style: styles3.nameInput, onSubmitEditing: onSubmit }),
392
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.TextInput, { ref: tag.ref, ...tag.props, mode: "outlined", dense: true, value: draftTag, onChangeText: onTagChange, placeholder: "Tag", autoCapitalize: "characters", selectTextOnFocus: true, error: tagInvalid, returnKeyType: "next", style: styles3.tagInput, contentStyle: styles3.tagInputContent }),
393
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.TextInput, { ref: name.ref, ...name.props, onSubmitEditing: onSubmit, mode: "outlined", dense: true, value: draftName, onChangeText: onNameChange, maxLength: MAX_PROFILE_NAME_LENGTH, placeholder: "Name", autoFocus, returnKeyType: "done", style: styles3.nameInput }),
392
394
  onDelete && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_feedback_press2.TouchableRipple, { onPress: onDelete, style: styles3.iconButton, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper3.Icon, { source: "trash-can-outline", size: 18, color: fgMuted }) })
393
395
  ] });
394
396
  }
package/dist/index.mjs CHANGED
@@ -205,6 +205,7 @@ var styles2 = StyleSheet2.create({
205
205
  // src/ProfilesManager.tsx
206
206
  import { useAutoPaperTheme } from "@rific/auto-paper";
207
207
  import { TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
208
+ import { useFocusChain } from "@rific/focus-chain";
208
209
  import { useToast } from "@rific/toaster";
209
210
  import { InlineColorPicker, usePopoverHost } from "@tastic/hud";
210
211
  import { useCallback, useEffect, useMemo as useMemo2, useRef, useState } from "react";
@@ -351,12 +352,13 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
351
352
  ] });
352
353
  }
353
354
  function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag, onTagChange, tagInvalid, onSubmit, onDelete, autoFocus, fgMuted, host, dark }) {
354
- const tagInputRef = useRef(null);
355
- const nameInputRef = useRef(null);
355
+ const register = useFocusChain();
356
+ const tag = register();
357
+ const name = register();
356
358
  return /* @__PURE__ */ jsxs2(View3, { style: styles3.editRow, children: [
357
359
  /* @__PURE__ */ jsx3(InlineColorPicker, { id: "color", host, value: draftColor, onChange: onColorChange, dark }),
358
- /* @__PURE__ */ jsx3(TextInput, { ref: tagInputRef, mode: "outlined", dense: true, value: draftTag, onChangeText: onTagChange, placeholder: "Tag", autoCapitalize: "characters", selectTextOnFocus: true, error: tagInvalid, returnKeyType: "next", style: styles3.tagInput, contentStyle: styles3.tagInputContent, onSubmitEditing: () => nameInputRef.current?.focus() }),
359
- /* @__PURE__ */ jsx3(TextInput, { ref: nameInputRef, mode: "outlined", dense: true, value: draftName, onChangeText: onNameChange, maxLength: MAX_PROFILE_NAME_LENGTH, placeholder: "Name", autoFocus, returnKeyType: "done", style: styles3.nameInput, onSubmitEditing: onSubmit }),
360
+ /* @__PURE__ */ jsx3(TextInput, { ref: tag.ref, ...tag.props, mode: "outlined", dense: true, value: draftTag, onChangeText: onTagChange, placeholder: "Tag", autoCapitalize: "characters", selectTextOnFocus: true, error: tagInvalid, returnKeyType: "next", style: styles3.tagInput, contentStyle: styles3.tagInputContent }),
361
+ /* @__PURE__ */ jsx3(TextInput, { ref: name.ref, ...name.props, onSubmitEditing: onSubmit, mode: "outlined", dense: true, value: draftName, onChangeText: onNameChange, maxLength: MAX_PROFILE_NAME_LENGTH, placeholder: "Name", autoFocus, returnKeyType: "done", style: styles3.nameInput }),
360
362
  onDelete && /* @__PURE__ */ jsx3(TouchableRipple2, { onPress: onDelete, style: styles3.iconButton, children: /* @__PURE__ */ jsx3(Icon3, { source: "trash-can-outline", size: 18, color: fgMuted }) })
361
363
  ] });
362
364
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tastic/profile",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Player profile identity for local-multiplayer React Native games — a saved name/color/tag a player selects per seat, with a picker, an inline-editable roster screen, and a reusable identity badge, deliberately blind to any app-specific fields (control scheme, stats) an app layers on top",
5
5
  "keywords": [
6
6
  "react-native",
@@ -1,9 +1,10 @@
1
1
  import { useAutoPaperTheme } from '@rific/auto-paper'
2
2
  import { TouchableRipple } from '@rific/feedback-press'
3
+ import { useFocusChain } from '@rific/focus-chain'
3
4
  import { useToast } from '@rific/toaster'
4
5
  import { InlineColorPicker, PopoverHost, usePopoverHost } from '@tastic/hud'
5
- import { ComponentRef, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
6
- import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, TextInput as RNTextInput, View } from 'react-native'
6
+ import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
7
+ import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, View } from 'react-native'
7
8
  import { Button, Icon, Portal, Text, TextInput } from 'react-native-paper'
8
9
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
9
10
 
@@ -275,21 +276,26 @@ interface EditRowProps {
275
276
  // color trigger here — it keeps its plain palette icon regardless of the row's own tag field, since
276
277
  // the tag is already right next to it in its own field, so repeating it on the color trigger too
277
278
  // would be redundant rather than informative.
278
- // Return-key chains tag -> name via explicit refs rather than each field submitting on its
279
- // own — submitting the *name* field is what actually commits the row (see the last field's own
280
- // onSubmitEditing override below), matching how a normal multi-field form reads: fill fields in
281
- // order, the last one finishes it.
279
+ // Return-key chains tag -> name via @rific/focus-chain rather than each field submitting on its
280
+ // own — submitting the *name* field is what actually commits the row, so its registration's own
281
+ // onSubmitEditing (a no-op — it's last in the chain) is overridden with onSubmit below, matching
282
+ // how a normal multi-field form reads: fill fields in order, the last one finishes it.
282
283
  function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag, onTagChange, tagInvalid, onSubmit, onDelete, autoFocus, fgMuted, host, dark }: EditRowProps) {
283
- const tagInputRef = useRef<ComponentRef<typeof RNTextInput>>(null)
284
- const nameInputRef = useRef<ComponentRef<typeof RNTextInput>>(null)
284
+ const register = useFocusChain()
285
+ const tag = register()
286
+ const name = register()
285
287
 
286
288
  return (
287
289
  <View style={styles.editRow}>
288
290
  <InlineColorPicker id='color' host={host} value={draftColor} onChange={onColorChange} dark={dark} />
289
291
  {/* selectTextOnFocus so tapping into an already-set tag selects it for wholesale replacement
290
292
  — the far more common edit than inserting into the middle of a 1-3 character value. */}
291
- <TextInput ref={tagInputRef} mode='outlined' dense value={draftTag} onChangeText={onTagChange} placeholder='Tag' autoCapitalize='characters' selectTextOnFocus error={tagInvalid} returnKeyType='next' style={styles.tagInput} contentStyle={styles.tagInputContent} onSubmitEditing={() => nameInputRef.current?.focus()} />
292
- <TextInput ref={nameInputRef} mode='outlined' dense value={draftName} onChangeText={onNameChange} maxLength={MAX_PROFILE_NAME_LENGTH} placeholder='Name' autoFocus={autoFocus} returnKeyType='done' style={styles.nameInput} onSubmitEditing={onSubmit} />
293
+ {/* eslint-disable-next-line react-hooks/refs -- tag.ref/tag.props come from useFocusChain's
294
+ register(), called during render by design (see the hook's own doc); the actual DOM/native
295
+ focus() call it wraps only ever fires later, from an event handler, never synchronously here */}
296
+ <TextInput ref={tag.ref} {...tag.props} mode='outlined' dense value={draftTag} onChangeText={onTagChange} placeholder='Tag' autoCapitalize='characters' selectTextOnFocus error={tagInvalid} returnKeyType='next' style={styles.tagInput} contentStyle={styles.tagInputContent} />
297
+ {/* eslint-disable-next-line react-hooks/refs -- see the tag field's identical note above */}
298
+ <TextInput ref={name.ref} {...name.props} onSubmitEditing={onSubmit} mode='outlined' dense value={draftName} onChangeText={onNameChange} maxLength={MAX_PROFILE_NAME_LENGTH} placeholder='Name' autoFocus={autoFocus} returnKeyType='done' style={styles.nameInput} />
293
299
  {onDelete && (
294
300
  <TouchableRipple onPress={onDelete} style={styles.iconButton}>
295
301
  <Icon source='trash-can-outline' size={18} color={fgMuted} />