@tastic/profile 0.1.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/README.md +81 -0
- package/dist/index.d.mts +58 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +515 -0
- package/dist/index.mjs +482 -0
- package/package.json +107 -0
- package/src/ProfileChip.tsx +60 -0
- package/src/ProfilePicker.tsx +275 -0
- package/src/ProfilesManager.tsx +417 -0
- package/src/index.ts +5 -0
- package/src/profilesValidation.ts +42 -0
- package/src/types.ts +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# @tastic/profile
|
|
2
|
+
|
|
3
|
+
Player profile identity for local-multiplayer React Native games — a saved name/color/tag a player
|
|
4
|
+
selects per seat, with a selection popover, an inline-editable roster screen, and a reusable identity
|
|
5
|
+
badge. Deliberately blind to anything app-specific: a control scheme, a stats bucket, or anything
|
|
6
|
+
else keyed by profile id lives entirely on the host app's own side.
|
|
7
|
+
|
|
8
|
+
## Why a separate `Profile` type at all?
|
|
9
|
+
|
|
10
|
+
Every app built on this package needs its own extra fields on a profile — a key/gesture scheme, a
|
|
11
|
+
push/pull control mode, whatever's specific to that game. Baking any of those into this package's
|
|
12
|
+
own `Profile` type would mean every OTHER app carries fields it doesn't use. Instead, this package's
|
|
13
|
+
`Profile` is deliberately minimal:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
interface Profile {
|
|
17
|
+
id: string
|
|
18
|
+
name: string
|
|
19
|
+
color: string
|
|
20
|
+
tag: string
|
|
21
|
+
createdAt: number
|
|
22
|
+
updatedAt: number
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
A host app extends this on its own (`interface Profile extends BaseProfile { keyScheme: KeyScheme }`)
|
|
27
|
+
and structural typing carries the richer shape straight through every component here that only ever
|
|
28
|
+
reads the base fields — no wrapper or adapter needed. The one place this needs a little more than
|
|
29
|
+
plain structural typing is `ProfilePicker`'s `onSelect` callback, which hands a profile back *out* —
|
|
30
|
+
see its own doc comment for why that component is generic over your own `Profile` type.
|
|
31
|
+
|
|
32
|
+
## What's in here
|
|
33
|
+
|
|
34
|
+
- **`Profile`** — the base identity type described above.
|
|
35
|
+
- **`isValidProfile` / `isValidTag`** — hand-rolled validators (no RN import, so they stay usable in
|
|
36
|
+
plain-Node contexts like Jest without touching `react-native-paper`/`@rific/auto-paper`'s own
|
|
37
|
+
RN-touching barrels). `isValidTag` accepts up to `MAX_TAG_LENGTH` plain characters or exactly one
|
|
38
|
+
emoji (including a ZWJ-joined compound), and rejects mixing the two. `isValidProfile` checks only
|
|
39
|
+
this package's own base fields — a host app composes its own richer validator on top the same way
|
|
40
|
+
it extends the type:
|
|
41
|
+
```ts
|
|
42
|
+
function isValidProfile(v: unknown): v is Profile {
|
|
43
|
+
return isValidBaseProfile(v) && isValidKeyScheme((v as Partial<Profile>).keyScheme)
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
- **`ProfileChip`** — a profile's identity at a glance: its own color as a filled circle, with its
|
|
47
|
+
tag (or a generic fallback icon, if empty) in a contrast-safe color on top. `filled={false}` drops
|
|
48
|
+
the background fill for a selected-row context that already fills its own background.
|
|
49
|
+
- **`ProfilePicker`** — a name trigger + selection popover: pick an existing profile, fall back to a
|
|
50
|
+
null/"guest" selection, or (if `onManage` is passed) navigate to a management screen. Selection
|
|
51
|
+
only — creating, renaming, and deleting all live in `ProfilesManager`. Generic over your own
|
|
52
|
+
`Profile` type (see above).
|
|
53
|
+
- **`ProfilesManager`** — the roster screen itself: create/rename/recolor/retag/delete, editing
|
|
54
|
+
inline in the row you tap, with a destructive-delete confirmation. No navigation of its own (no
|
|
55
|
+
router, no back button) — a host app renders this as a routed screen's body and supplies its own
|
|
56
|
+
persistence via `profiles`/`onCreate`/`onSave`/`onDelete`.
|
|
57
|
+
|
|
58
|
+
## Install (local dev via yalc)
|
|
59
|
+
|
|
60
|
+
Not published to the public npm registry yet.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cd react-native-game-profile
|
|
64
|
+
npm run build
|
|
65
|
+
yalc publish
|
|
66
|
+
|
|
67
|
+
cd ../your-game
|
|
68
|
+
yalc add @tastic/profile
|
|
69
|
+
npm install
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Re-run `npm run build && yalc push` from this package after any change to propagate it to every
|
|
73
|
+
linked consumer at once.
|
|
74
|
+
|
|
75
|
+
## Peer dependencies
|
|
76
|
+
|
|
77
|
+
`react`, `react-native`, `react-native-paper`, `react-native-safe-area-context`, `@rific/auto-paper`
|
|
78
|
+
(`getContrastColor`, `useAutoPaperTheme`), `@rific/feedback-press` (`TouchableRipple`),
|
|
79
|
+
`@rific/focus-chain` (`useFocusChain`), `@rific/toaster` (`useToast`), `@tastic/hud`
|
|
80
|
+
(`InlineColorPicker`, `PopoverBody`, `PopoverHost`, `useAutoAlign`, `usePopoverHost`) — none of these
|
|
81
|
+
are bundled, so use whatever versions your app already has.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { PopoverHost } from '@tastic/hud';
|
|
4
|
+
|
|
5
|
+
interface Profile {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
color: string;
|
|
9
|
+
tag: string;
|
|
10
|
+
createdAt: number;
|
|
11
|
+
updatedAt: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
profile: Profile;
|
|
16
|
+
size?: number;
|
|
17
|
+
filled?: boolean;
|
|
18
|
+
}
|
|
19
|
+
declare function ProfileChip({ profile, size, filled }: Props): react.JSX.Element;
|
|
20
|
+
|
|
21
|
+
interface ProfilePickerProps<P extends Profile> {
|
|
22
|
+
idPrefix: string;
|
|
23
|
+
host: PopoverHost;
|
|
24
|
+
profiles: P[];
|
|
25
|
+
selectedId: string | null;
|
|
26
|
+
takenId?: string | null;
|
|
27
|
+
color: string;
|
|
28
|
+
dark: boolean;
|
|
29
|
+
align?: 'left' | 'right' | 'center';
|
|
30
|
+
guestLabel: string;
|
|
31
|
+
nullLabel?: string;
|
|
32
|
+
nullIcon?: string;
|
|
33
|
+
onSelect: (profile: P | null) => void;
|
|
34
|
+
onManage?: () => void;
|
|
35
|
+
}
|
|
36
|
+
declare function ProfilePicker<P extends Profile>({ idPrefix, host, profiles, selectedId, takenId, color, dark, align: alignOverride, guestLabel, nullLabel, nullIcon, onSelect, onManage }: ProfilePickerProps<P>): react.JSX.Element;
|
|
37
|
+
|
|
38
|
+
interface ProfileEditPatch {
|
|
39
|
+
name: string;
|
|
40
|
+
color: string;
|
|
41
|
+
tag: string;
|
|
42
|
+
}
|
|
43
|
+
interface ProfilesManagerProps {
|
|
44
|
+
profiles: Profile[];
|
|
45
|
+
defaultColor: string;
|
|
46
|
+
onCreate: (input: ProfileEditPatch) => void;
|
|
47
|
+
onSave: (id: string, patch: ProfileEditPatch) => void;
|
|
48
|
+
onDelete: (id: string) => void;
|
|
49
|
+
headerLeft?: ReactNode;
|
|
50
|
+
}
|
|
51
|
+
declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft }: ProfilesManagerProps): react.JSX.Element;
|
|
52
|
+
|
|
53
|
+
declare const MAX_PROFILE_NAME_LENGTH = 20;
|
|
54
|
+
declare const MAX_TAG_LENGTH = 3;
|
|
55
|
+
declare function isValidTag(value: unknown): value is string;
|
|
56
|
+
declare function isValidProfile(value: unknown): value is Profile;
|
|
57
|
+
|
|
58
|
+
export { MAX_PROFILE_NAME_LENGTH, MAX_TAG_LENGTH, type Profile, ProfileChip, ProfilePicker, ProfilesManager, type ProfilesManagerProps, isValidProfile, isValidTag };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { PopoverHost } from '@tastic/hud';
|
|
4
|
+
|
|
5
|
+
interface Profile {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
color: string;
|
|
9
|
+
tag: string;
|
|
10
|
+
createdAt: number;
|
|
11
|
+
updatedAt: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
profile: Profile;
|
|
16
|
+
size?: number;
|
|
17
|
+
filled?: boolean;
|
|
18
|
+
}
|
|
19
|
+
declare function ProfileChip({ profile, size, filled }: Props): react.JSX.Element;
|
|
20
|
+
|
|
21
|
+
interface ProfilePickerProps<P extends Profile> {
|
|
22
|
+
idPrefix: string;
|
|
23
|
+
host: PopoverHost;
|
|
24
|
+
profiles: P[];
|
|
25
|
+
selectedId: string | null;
|
|
26
|
+
takenId?: string | null;
|
|
27
|
+
color: string;
|
|
28
|
+
dark: boolean;
|
|
29
|
+
align?: 'left' | 'right' | 'center';
|
|
30
|
+
guestLabel: string;
|
|
31
|
+
nullLabel?: string;
|
|
32
|
+
nullIcon?: string;
|
|
33
|
+
onSelect: (profile: P | null) => void;
|
|
34
|
+
onManage?: () => void;
|
|
35
|
+
}
|
|
36
|
+
declare function ProfilePicker<P extends Profile>({ idPrefix, host, profiles, selectedId, takenId, color, dark, align: alignOverride, guestLabel, nullLabel, nullIcon, onSelect, onManage }: ProfilePickerProps<P>): react.JSX.Element;
|
|
37
|
+
|
|
38
|
+
interface ProfileEditPatch {
|
|
39
|
+
name: string;
|
|
40
|
+
color: string;
|
|
41
|
+
tag: string;
|
|
42
|
+
}
|
|
43
|
+
interface ProfilesManagerProps {
|
|
44
|
+
profiles: Profile[];
|
|
45
|
+
defaultColor: string;
|
|
46
|
+
onCreate: (input: ProfileEditPatch) => void;
|
|
47
|
+
onSave: (id: string, patch: ProfileEditPatch) => void;
|
|
48
|
+
onDelete: (id: string) => void;
|
|
49
|
+
headerLeft?: ReactNode;
|
|
50
|
+
}
|
|
51
|
+
declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft }: ProfilesManagerProps): react.JSX.Element;
|
|
52
|
+
|
|
53
|
+
declare const MAX_PROFILE_NAME_LENGTH = 20;
|
|
54
|
+
declare const MAX_TAG_LENGTH = 3;
|
|
55
|
+
declare function isValidTag(value: unknown): value is string;
|
|
56
|
+
declare function isValidProfile(value: unknown): value is Profile;
|
|
57
|
+
|
|
58
|
+
export { MAX_PROFILE_NAME_LENGTH, MAX_TAG_LENGTH, type Profile, ProfileChip, ProfilePicker, ProfilesManager, type ProfilesManagerProps, isValidProfile, isValidTag };
|