@tastic/profile 0.3.6 → 0.3.7
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.d.mts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +2 -1
- package/dist/index.mjs +3 -2
- package/package.json +1 -1
- package/src/ProfilesManager.tsx +26 -2
- package/src/index.ts +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
2
|
+
import { ReactNode, Ref } from 'react';
|
|
3
3
|
import { PopoverHost } from '@tastic/hud';
|
|
4
4
|
|
|
5
5
|
interface Profile {
|
|
@@ -40,6 +40,9 @@ interface ProfileEditPatch {
|
|
|
40
40
|
color: string;
|
|
41
41
|
tag: string;
|
|
42
42
|
}
|
|
43
|
+
interface ProfilesManagerHandle {
|
|
44
|
+
commitPendingEdit: () => void;
|
|
45
|
+
}
|
|
43
46
|
interface ProfilesManagerProps {
|
|
44
47
|
profiles: Profile[];
|
|
45
48
|
defaultColor: string;
|
|
@@ -47,8 +50,9 @@ interface ProfilesManagerProps {
|
|
|
47
50
|
onSave: (id: string, patch: ProfileEditPatch) => void;
|
|
48
51
|
onDelete: (id: string) => void;
|
|
49
52
|
headerLeft?: ReactNode;
|
|
53
|
+
ref?: Ref<ProfilesManagerHandle>;
|
|
50
54
|
}
|
|
51
|
-
declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft }: ProfilesManagerProps): react.JSX.Element;
|
|
55
|
+
declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref }: ProfilesManagerProps): react.JSX.Element;
|
|
52
56
|
|
|
53
57
|
declare const MAX_PROFILE_NAME_LENGTH = 20;
|
|
54
58
|
declare const MAX_TAG_LENGTH = 3;
|
|
@@ -59,4 +63,4 @@ declare const isSharedProfileStoreAvailable: boolean;
|
|
|
59
63
|
declare function loadSharedProfiles(groupId: string): Promise<Profile[]>;
|
|
60
64
|
declare function saveSharedProfiles(groupId: string, profiles: Profile[]): Promise<void>;
|
|
61
65
|
|
|
62
|
-
export { MAX_PROFILE_NAME_LENGTH, MAX_TAG_LENGTH, type Profile, ProfileChip, ProfilePicker, ProfilesManager, type ProfilesManagerProps, isSharedProfileStoreAvailable, isValidProfile, isValidTag, loadSharedProfiles, saveSharedProfiles };
|
|
66
|
+
export { MAX_PROFILE_NAME_LENGTH, MAX_TAG_LENGTH, type Profile, ProfileChip, ProfilePicker, ProfilesManager, type ProfilesManagerHandle, type ProfilesManagerProps, isSharedProfileStoreAvailable, isValidProfile, isValidTag, loadSharedProfiles, saveSharedProfiles };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
2
|
+
import { ReactNode, Ref } from 'react';
|
|
3
3
|
import { PopoverHost } from '@tastic/hud';
|
|
4
4
|
|
|
5
5
|
interface Profile {
|
|
@@ -40,6 +40,9 @@ interface ProfileEditPatch {
|
|
|
40
40
|
color: string;
|
|
41
41
|
tag: string;
|
|
42
42
|
}
|
|
43
|
+
interface ProfilesManagerHandle {
|
|
44
|
+
commitPendingEdit: () => void;
|
|
45
|
+
}
|
|
43
46
|
interface ProfilesManagerProps {
|
|
44
47
|
profiles: Profile[];
|
|
45
48
|
defaultColor: string;
|
|
@@ -47,8 +50,9 @@ interface ProfilesManagerProps {
|
|
|
47
50
|
onSave: (id: string, patch: ProfileEditPatch) => void;
|
|
48
51
|
onDelete: (id: string) => void;
|
|
49
52
|
headerLeft?: ReactNode;
|
|
53
|
+
ref?: Ref<ProfilesManagerHandle>;
|
|
50
54
|
}
|
|
51
|
-
declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft }: ProfilesManagerProps): react.JSX.Element;
|
|
55
|
+
declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref }: ProfilesManagerProps): react.JSX.Element;
|
|
52
56
|
|
|
53
57
|
declare const MAX_PROFILE_NAME_LENGTH = 20;
|
|
54
58
|
declare const MAX_TAG_LENGTH = 3;
|
|
@@ -59,4 +63,4 @@ declare const isSharedProfileStoreAvailable: boolean;
|
|
|
59
63
|
declare function loadSharedProfiles(groupId: string): Promise<Profile[]>;
|
|
60
64
|
declare function saveSharedProfiles(groupId: string, profiles: Profile[]): Promise<void>;
|
|
61
65
|
|
|
62
|
-
export { MAX_PROFILE_NAME_LENGTH, MAX_TAG_LENGTH, type Profile, ProfileChip, ProfilePicker, ProfilesManager, type ProfilesManagerProps, isSharedProfileStoreAvailable, isValidProfile, isValidTag, loadSharedProfiles, saveSharedProfiles };
|
|
66
|
+
export { MAX_PROFILE_NAME_LENGTH, MAX_TAG_LENGTH, type Profile, ProfileChip, ProfilePicker, ProfilesManager, type ProfilesManagerHandle, type ProfilesManagerProps, isSharedProfileStoreAvailable, isValidProfile, isValidTag, loadSharedProfiles, saveSharedProfiles };
|
package/dist/index.js
CHANGED
|
@@ -287,7 +287,7 @@ function deriveTag(name) {
|
|
|
287
287
|
const trimmed = name.trim();
|
|
288
288
|
return trimmed ? trimmed[0].toUpperCase() : "";
|
|
289
289
|
}
|
|
290
|
-
function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft }) {
|
|
290
|
+
function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref }) {
|
|
291
291
|
const { dark, colors } = (0, import_auto_paper3.useAutoPaperTheme)();
|
|
292
292
|
const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
|
|
293
293
|
const { error: showErrorToast } = (0, import_toaster.useToast)();
|
|
@@ -320,6 +320,7 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
320
320
|
commitEditRef.current = commitEdit;
|
|
321
321
|
});
|
|
322
322
|
(0, import_react2.useEffect)(() => () => commitEditRef.current(), []);
|
|
323
|
+
(0, import_react2.useImperativeHandle)(ref, () => ({ commitPendingEdit: () => commitEditRef.current() }), []);
|
|
323
324
|
const startEdit = (0, import_react2.useCallback)(
|
|
324
325
|
(target) => {
|
|
325
326
|
commitEdit();
|
package/dist/index.mjs
CHANGED
|
@@ -215,7 +215,7 @@ import { TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
|
|
|
215
215
|
import { useFocusChain } from "@rific/focus-chain";
|
|
216
216
|
import { useToast } from "@rific/toaster";
|
|
217
217
|
import { InlineColorPicker, usePopoverHost } from "@tastic/hud";
|
|
218
|
-
import { useCallback, useEffect, useMemo as useMemo2, useRef, useState } from "react";
|
|
218
|
+
import { useCallback, useEffect, useImperativeHandle, useMemo as useMemo2, useRef, useState } from "react";
|
|
219
219
|
import { KeyboardAvoidingView, Platform, ScrollView as ScrollView2, StyleSheet as StyleSheet3, View as View3 } from "react-native";
|
|
220
220
|
import { Button, Icon as Icon3, Portal, Text as Text3, TextInput } from "react-native-paper";
|
|
221
221
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
@@ -259,7 +259,7 @@ function deriveTag(name) {
|
|
|
259
259
|
const trimmed = name.trim();
|
|
260
260
|
return trimmed ? trimmed[0].toUpperCase() : "";
|
|
261
261
|
}
|
|
262
|
-
function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft }) {
|
|
262
|
+
function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref }) {
|
|
263
263
|
const { dark, colors } = useAutoPaperTheme();
|
|
264
264
|
const insets = useSafeAreaInsets();
|
|
265
265
|
const { error: showErrorToast } = useToast();
|
|
@@ -292,6 +292,7 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
292
292
|
commitEditRef.current = commitEdit;
|
|
293
293
|
});
|
|
294
294
|
useEffect(() => () => commitEditRef.current(), []);
|
|
295
|
+
useImperativeHandle(ref, () => ({ commitPendingEdit: () => commitEditRef.current() }), []);
|
|
295
296
|
const startEdit = useCallback(
|
|
296
297
|
(target) => {
|
|
297
298
|
commitEdit();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tastic/profile",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
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",
|
package/src/ProfilesManager.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import { TouchableRipple } from '@rific/feedback-press'
|
|
|
3
3
|
import { useFocusChain } from '@rific/focus-chain'
|
|
4
4
|
import { useToast } from '@rific/toaster'
|
|
5
5
|
import { InlineColorPicker, PopoverHost, usePopoverHost } from '@tastic/hud'
|
|
6
|
-
import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
6
|
+
import { ReactNode, Ref, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'
|
|
7
7
|
import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, View } from 'react-native'
|
|
8
8
|
import { Button, Icon, Portal, Text, TextInput } from 'react-native-paper'
|
|
9
9
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
@@ -22,6 +22,18 @@ interface ProfileEditPatch {
|
|
|
22
22
|
tag: string
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export interface ProfilesManagerHandle {
|
|
26
|
+
// Flushes whatever row is currently being edited — the same thing this component's own unmount
|
|
27
|
+
// effect does, but callable explicitly. Exists because "unmount" isn't a reliable "the user is
|
|
28
|
+
// navigating away" signal on every host: React Navigation's web renderer keeps a popped screen
|
|
29
|
+
// mounted-but-hidden instead of unmounting it, so a host relying only on the unmount fallback
|
|
30
|
+
// silently loses an in-progress edit on `router.back()` there. A host whose router doesn't
|
|
31
|
+
// genuinely unmount this component on navigation should hold a ref and call this right before
|
|
32
|
+
// navigating away (e.g. in its own back-button handler). Safe to call any time, including when
|
|
33
|
+
// nothing is being edited — a no-op then, same as the unmount effect already is.
|
|
34
|
+
commitPendingEdit: () => void
|
|
35
|
+
}
|
|
36
|
+
|
|
25
37
|
export interface ProfilesManagerProps {
|
|
26
38
|
profiles: Profile[]
|
|
27
39
|
// The color a brand-new profile's draft starts on — this component doesn't have (and shouldn't
|
|
@@ -37,6 +49,9 @@ export interface ProfilesManagerProps {
|
|
|
37
49
|
// rendering slot, not an onBack-style callback: this component doesn't know or care that it's a
|
|
38
50
|
// back button, only that the host wants something shown there. Omit to hide the slot.
|
|
39
51
|
headerLeft?: ReactNode
|
|
52
|
+
// React 19 accepts `ref` as a plain prop on a function component (no forwardRef needed) — see
|
|
53
|
+
// ProfilesManagerHandle's own doc for why a host may need this.
|
|
54
|
+
ref?: Ref<ProfilesManagerHandle>
|
|
40
55
|
}
|
|
41
56
|
|
|
42
57
|
type Row = { kind: 'profile'; profile: Profile } | { kind: 'new' }
|
|
@@ -55,7 +70,11 @@ function deriveTag(name: string): string {
|
|
|
55
70
|
// would still assume *some* host owns navigation; this component doesn't even go that far), so any
|
|
56
71
|
// consuming screen just renders it as its body inside its own routed wrapper (back button, native
|
|
57
72
|
// push/pop transition, whatever persistence hook backs `profiles`/`onCreate`/`onSave`/`onDelete`).
|
|
58
|
-
|
|
73
|
+
// Staying router-agnostic means this component can't know for itself whether "navigating away"
|
|
74
|
+
// really unmounts it on a given host's router — see ProfilesManagerHandle's own doc for the
|
|
75
|
+
// react-navigation-web case where it doesn't, and why a host there needs the `ref` escape hatch
|
|
76
|
+
// instead of relying on the unmount fallback alone.
|
|
77
|
+
export function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref }: ProfilesManagerProps) {
|
|
59
78
|
const { dark, colors } = useAutoPaperTheme()
|
|
60
79
|
const insets = useSafeAreaInsets()
|
|
61
80
|
const { error: showErrorToast } = useToast()
|
|
@@ -116,6 +135,11 @@ export function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDe
|
|
|
116
135
|
commitEditRef.current = commitEdit
|
|
117
136
|
})
|
|
118
137
|
useEffect(() => () => commitEditRef.current(), [])
|
|
138
|
+
// The explicit escape hatch for hosts whose "navigate away" doesn't genuinely unmount this
|
|
139
|
+
// component — see ProfilesManagerHandle's own doc. `[]` deps: the handle object itself never
|
|
140
|
+
// needs to change identity, since it always calls through the ref to whatever commitEdit is
|
|
141
|
+
// current at call time, the same indirection the unmount effect above already relies on.
|
|
142
|
+
useImperativeHandle(ref, () => ({ commitPendingEdit: () => commitEditRef.current() }), [])
|
|
119
143
|
|
|
120
144
|
const startEdit = useCallback(
|
|
121
145
|
(target: 'new' | Profile) => {
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { ProfileChip } from './ProfileChip'
|
|
2
2
|
export { ProfilePicker } from './ProfilePicker'
|
|
3
|
-
export { ProfilesManager, type ProfilesManagerProps } from './ProfilesManager'
|
|
3
|
+
export { ProfilesManager, type ProfilesManagerHandle, type ProfilesManagerProps } from './ProfilesManager'
|
|
4
4
|
export { isValidProfile, isValidTag, MAX_PROFILE_NAME_LENGTH, MAX_TAG_LENGTH } from './profilesValidation'
|
|
5
5
|
export { isSharedProfileStoreAvailable, loadSharedProfiles, saveSharedProfiles } from './sharedProfileStore'
|
|
6
6
|
export type { Profile } from './types'
|