@tastic/profile 0.12.0 → 0.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/README.md +5 -8
- package/dist/index.d.mts +2 -4
- package/dist/index.d.ts +2 -4
- package/dist/index.js +5 -40
- package/dist/index.mjs +7 -42
- package/package.json +9 -9
- package/src/ProfilesManager.tsx +14 -66
- package/src/ProfilesScreen.tsx +3 -4
package/README.md
CHANGED
|
@@ -180,7 +180,7 @@ Same `profiles`/`defaultColor`/`onCreate`/`onSave`/`onDelete` props as `Profiles
|
|
|
180
180
|
`ProfileEditPatch`-shaped, still deliberately not widened to your own richer `CreateProfileInput`;
|
|
181
181
|
compose any extra required fields (a key/control scheme, whatever's specific to your game) in your
|
|
182
182
|
own closure at the call site, exactly as you already would calling `ProfilesManager` directly -
|
|
183
|
-
plus `onBack`. `fg`/`fgMuted`/`bg`/`
|
|
183
|
+
plus `onBack`. `fg`/`fgMuted`/`bg`/`titleVariant`/`colorPreview` are all independently
|
|
184
184
|
optional and forward straight through to the internal `ProfilesManager` (`bg` is new here -
|
|
185
185
|
`ProfilesManager` itself has no `bg`, since it doesn't own a container `View` of its own).
|
|
186
186
|
|
|
@@ -312,13 +312,10 @@ it.
|
|
|
312
312
|
saved color live." Cleared only when that seat's own profile selection genuinely changes, never by
|
|
313
313
|
merely leaving and returning to the screen.
|
|
314
314
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
no dedicated slice to put it in (see Snake's `src/redux/gameSlice.ts`). This section exists so a new
|
|
320
|
-
app's author reads the convention here first, rather than hand-rolling something that quietly can't
|
|
321
|
-
interop with the rest of the fleet.
|
|
315
|
+
All three live in one slice from this package's `createSeatColorsSlice` (since 0.5.0), which every
|
|
316
|
+
flagship game uses (see e.g. BoxHockey's or Snake's `src/redux/seatColorsSlice.ts`). This section
|
|
317
|
+
exists so a new app's author reads the convention here first, rather than hand-rolling something
|
|
318
|
+
that quietly can't interop with the rest of the fleet.
|
|
322
319
|
|
|
323
320
|
The resolution order at a loadout/lobby screen, for one seat:
|
|
324
321
|
|
package/dist/index.d.mts
CHANGED
|
@@ -77,12 +77,11 @@ interface ProfilesManagerProps {
|
|
|
77
77
|
ref?: Ref<ProfilesManagerHandle>;
|
|
78
78
|
fg?: string;
|
|
79
79
|
fgMuted?: string;
|
|
80
|
-
cardBg?: string;
|
|
81
80
|
titleVariant?: MD3TextVariant$1;
|
|
82
81
|
colorPreview?: (hex: string) => string;
|
|
83
82
|
useColorPickerAlign?: (open: boolean, contentWidth: number, contentHeight: number) => ProfilePickerAlignResult;
|
|
84
83
|
}
|
|
85
|
-
declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride,
|
|
84
|
+
declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, titleVariant, colorPreview: colorPreviewProp, useColorPickerAlign }: ProfilesManagerProps): react.JSX.Element;
|
|
86
85
|
|
|
87
86
|
type MD3TextVariant = 'displayLarge' | 'displayMedium' | 'displaySmall' | 'headlineLarge' | 'headlineMedium' | 'headlineSmall' | 'titleLarge' | 'titleMedium' | 'titleSmall' | 'labelLarge' | 'labelMedium' | 'labelSmall' | 'bodyLarge' | 'bodyMedium' | 'bodySmall';
|
|
88
87
|
interface ProfilesScreenProps {
|
|
@@ -95,12 +94,11 @@ interface ProfilesScreenProps {
|
|
|
95
94
|
fg?: string;
|
|
96
95
|
fgMuted?: string;
|
|
97
96
|
bg?: string;
|
|
98
|
-
cardBg?: string;
|
|
99
97
|
titleVariant?: MD3TextVariant;
|
|
100
98
|
colorPreview?: (hex: string) => string;
|
|
101
99
|
useColorPickerAlign?: ProfilesManagerProps['useColorPickerAlign'];
|
|
102
100
|
}
|
|
103
|
-
declare function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride,
|
|
101
|
+
declare function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }: ProfilesScreenProps): react.JSX.Element;
|
|
104
102
|
|
|
105
103
|
declare const MAX_PROFILE_NAME_LENGTH = 20;
|
|
106
104
|
declare const MAX_TAG_LENGTH = 3;
|
package/dist/index.d.ts
CHANGED
|
@@ -77,12 +77,11 @@ interface ProfilesManagerProps {
|
|
|
77
77
|
ref?: Ref<ProfilesManagerHandle>;
|
|
78
78
|
fg?: string;
|
|
79
79
|
fgMuted?: string;
|
|
80
|
-
cardBg?: string;
|
|
81
80
|
titleVariant?: MD3TextVariant$1;
|
|
82
81
|
colorPreview?: (hex: string) => string;
|
|
83
82
|
useColorPickerAlign?: (open: boolean, contentWidth: number, contentHeight: number) => ProfilePickerAlignResult;
|
|
84
83
|
}
|
|
85
|
-
declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride,
|
|
84
|
+
declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, titleVariant, colorPreview: colorPreviewProp, useColorPickerAlign }: ProfilesManagerProps): react.JSX.Element;
|
|
86
85
|
|
|
87
86
|
type MD3TextVariant = 'displayLarge' | 'displayMedium' | 'displaySmall' | 'headlineLarge' | 'headlineMedium' | 'headlineSmall' | 'titleLarge' | 'titleMedium' | 'titleSmall' | 'labelLarge' | 'labelMedium' | 'labelSmall' | 'bodyLarge' | 'bodyMedium' | 'bodySmall';
|
|
88
87
|
interface ProfilesScreenProps {
|
|
@@ -95,12 +94,11 @@ interface ProfilesScreenProps {
|
|
|
95
94
|
fg?: string;
|
|
96
95
|
fgMuted?: string;
|
|
97
96
|
bg?: string;
|
|
98
|
-
cardBg?: string;
|
|
99
97
|
titleVariant?: MD3TextVariant;
|
|
100
98
|
colorPreview?: (hex: string) => string;
|
|
101
99
|
useColorPickerAlign?: ProfilesManagerProps['useColorPickerAlign'];
|
|
102
100
|
}
|
|
103
|
-
declare function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride,
|
|
101
|
+
declare function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }: ProfilesScreenProps): react.JSX.Element;
|
|
104
102
|
|
|
105
103
|
declare const MAX_PROFILE_NAME_LENGTH = 20;
|
|
106
104
|
declare const MAX_TAG_LENGTH = 3;
|
package/dist/index.js
CHANGED
|
@@ -327,19 +327,15 @@ function deriveTag(name) {
|
|
|
327
327
|
const trimmed = name.trim();
|
|
328
328
|
return trimmed ? trimmed[0].toUpperCase() : "";
|
|
329
329
|
}
|
|
330
|
-
function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride,
|
|
330
|
+
function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, titleVariant = "headlineSmall", colorPreview: colorPreviewProp, useColorPickerAlign }) {
|
|
331
331
|
const contextColorPreview = useProfileColorPreview();
|
|
332
332
|
const colorPreview = colorPreviewProp ?? contextColorPreview;
|
|
333
|
-
const { dark
|
|
333
|
+
const { dark } = (0, import_auto_paper3.useAutoPaperTheme)();
|
|
334
334
|
const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
|
|
335
335
|
const { error: showErrorToast } = (0, import_toaster.useToast)();
|
|
336
336
|
const host = (0, import_hud2.usePopoverHost)();
|
|
337
337
|
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
338
338
|
const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
|
|
339
|
-
const cardBg = cardBgOverride ?? (dark ? "#111111" : "#F2F2F2");
|
|
340
|
-
const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
|
|
341
|
-
const cancelBg = dark ? "#2A2A2A" : "#E0E0E0";
|
|
342
|
-
const cancelFg = dark ? "#FFFFFF" : "#000000";
|
|
343
339
|
const [editingId, setEditingId] = (0, import_react3.useState)(null);
|
|
344
340
|
const [draftName, setDraftName] = (0, import_react3.useState)("");
|
|
345
341
|
const [draftColor, setDraftColor] = (0, import_react3.useState)(defaultColor);
|
|
@@ -426,17 +422,7 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
426
422
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper3.Text, { variant: "bodyLarge", style: [styles3.rowName, { color: fg }], numberOfLines: 1, children: profile.name })
|
|
427
423
|
] }) }, profile.id);
|
|
428
424
|
}) }) }),
|
|
429
|
-
|
|
430
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper3.Icon, { source: "alert-outline", size: 64, color: colors.danger }),
|
|
431
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper3.Text, { variant: "headlineLarge", style: [styles3.overlayTitle, { color: colors.danger }], children: "Delete Profile?" }),
|
|
432
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native_paper3.Text, { variant: "bodyLarge", style: [styles3.overlayBody, { color: fg }], children: [
|
|
433
|
-
"This permanently deletes ",
|
|
434
|
-
confirmDeleteProfile.name,
|
|
435
|
-
"\u2019s profile. This cannot be undone."
|
|
436
|
-
] }),
|
|
437
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper3.Button, { mode: "contained", onPress: () => setConfirmDeleteId(null), style: styles3.overlayButton, buttonColor: cancelBg, textColor: cancelFg, children: "Cancel" }),
|
|
438
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper3.Button, { mode: "contained", onPress: handleConfirmDelete, style: styles3.overlayButton, buttonColor: colors.danger, textColor: colors.onDanger, children: "Delete" })
|
|
439
|
-
] }) }) })
|
|
425
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_hud2.ConfirmDialog, { visible: confirmDeleteProfile !== null, title: "Delete Profile?", message: confirmDeleteProfile ? `This permanently deletes ${confirmDeleteProfile.name}\u2019s profile. This cannot be undone.` : "", confirmLabel: "Delete", cancelLabel: "Cancel", icon: "alert-outline", destructive: true, onConfirm: handleConfirmDelete, onCancel: () => setConfirmDeleteId(null) })
|
|
440
426
|
] });
|
|
441
427
|
}
|
|
442
428
|
function AlignedColorPicker({ useAlign, ...props }) {
|
|
@@ -507,27 +493,6 @@ var styles3 = import_react_native3.StyleSheet.create({
|
|
|
507
493
|
borderStyle: "dashed",
|
|
508
494
|
borderWidth: 2
|
|
509
495
|
},
|
|
510
|
-
overlay: {
|
|
511
|
-
alignItems: "center",
|
|
512
|
-
backgroundColor: "rgba(0,0,0,0.72)",
|
|
513
|
-
bottom: 0,
|
|
514
|
-
justifyContent: "center",
|
|
515
|
-
left: 0,
|
|
516
|
-
position: "absolute",
|
|
517
|
-
right: 0,
|
|
518
|
-
top: 0
|
|
519
|
-
},
|
|
520
|
-
overlayBody: { textAlign: "center" },
|
|
521
|
-
overlayButton: { width: 160 },
|
|
522
|
-
overlayCard: {
|
|
523
|
-
alignItems: "center",
|
|
524
|
-
borderRadius: 20,
|
|
525
|
-
borderWidth: 1,
|
|
526
|
-
gap: 16,
|
|
527
|
-
maxWidth: 360,
|
|
528
|
-
padding: 32
|
|
529
|
-
},
|
|
530
|
-
overlayTitle: { fontWeight: "bold", marginBottom: -8 },
|
|
531
496
|
row: {
|
|
532
497
|
borderRadius: 8,
|
|
533
498
|
paddingVertical: 8
|
|
@@ -562,7 +527,7 @@ var import_feedback_press3 = require("@rific/feedback-press");
|
|
|
562
527
|
var import_react4 = require("react");
|
|
563
528
|
var import_react_native4 = require("react-native");
|
|
564
529
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
565
|
-
function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride,
|
|
530
|
+
function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }) {
|
|
566
531
|
const { dark } = (0, import_auto_paper4.useAutoPaperTheme)();
|
|
567
532
|
const managerRef = (0, import_react4.useRef)(null);
|
|
568
533
|
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
@@ -571,7 +536,7 @@ function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, on
|
|
|
571
536
|
managerRef.current?.commitPendingEdit();
|
|
572
537
|
onBack();
|
|
573
538
|
};
|
|
574
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native4.View, { style: [styles4.container, { backgroundColor: bg }], children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ProfilesManager, { ref: managerRef, profiles, defaultColor, onCreate, onSave, onDelete, fg, fgMuted,
|
|
539
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native4.View, { style: [styles4.container, { backgroundColor: bg }], children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ProfilesManager, { ref: managerRef, profiles, defaultColor, onCreate, onSave, onDelete, fg, fgMuted, titleVariant, colorPreview, useColorPickerAlign, headerLeft: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: handleBack, accessibilityLabel: "Back" }) }) });
|
|
575
540
|
}
|
|
576
541
|
var styles4 = import_react_native4.StyleSheet.create({
|
|
577
542
|
container: {
|
package/dist/index.mjs
CHANGED
|
@@ -239,10 +239,10 @@ import { defaultColors, useAutoPaperTheme } from "@rific/auto-paper";
|
|
|
239
239
|
import { TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
|
|
240
240
|
import { useFocusChain } from "@rific/focus-chain";
|
|
241
241
|
import { useToast } from "@rific/toaster";
|
|
242
|
-
import { getInlineColorPickerContentSize, InlineColorPicker, usePopoverHost } from "@tastic/hud";
|
|
242
|
+
import { ConfirmDialog, getInlineColorPickerContentSize, InlineColorPicker, usePopoverHost } from "@tastic/hud";
|
|
243
243
|
import { useCallback, useEffect, useImperativeHandle, useMemo as useMemo2, useRef, useState } from "react";
|
|
244
244
|
import { KeyboardAvoidingView, Platform, ScrollView as ScrollView2, StyleSheet as StyleSheet3, useWindowDimensions, View as View3 } from "react-native";
|
|
245
|
-
import {
|
|
245
|
+
import { Icon as Icon3, Text as Text3, TextInput } from "react-native-paper";
|
|
246
246
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
247
247
|
|
|
248
248
|
// src/profilesValidation.ts
|
|
@@ -284,19 +284,15 @@ function deriveTag(name) {
|
|
|
284
284
|
const trimmed = name.trim();
|
|
285
285
|
return trimmed ? trimmed[0].toUpperCase() : "";
|
|
286
286
|
}
|
|
287
|
-
function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride,
|
|
287
|
+
function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, titleVariant = "headlineSmall", colorPreview: colorPreviewProp, useColorPickerAlign }) {
|
|
288
288
|
const contextColorPreview = useProfileColorPreview();
|
|
289
289
|
const colorPreview = colorPreviewProp ?? contextColorPreview;
|
|
290
|
-
const { dark
|
|
290
|
+
const { dark } = useAutoPaperTheme();
|
|
291
291
|
const insets = useSafeAreaInsets();
|
|
292
292
|
const { error: showErrorToast } = useToast();
|
|
293
293
|
const host = usePopoverHost();
|
|
294
294
|
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
295
295
|
const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
|
|
296
|
-
const cardBg = cardBgOverride ?? (dark ? "#111111" : "#F2F2F2");
|
|
297
|
-
const cardBorder = dark ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)";
|
|
298
|
-
const cancelBg = dark ? "#2A2A2A" : "#E0E0E0";
|
|
299
|
-
const cancelFg = dark ? "#FFFFFF" : "#000000";
|
|
300
296
|
const [editingId, setEditingId] = useState(null);
|
|
301
297
|
const [draftName, setDraftName] = useState("");
|
|
302
298
|
const [draftColor, setDraftColor] = useState(defaultColor);
|
|
@@ -383,17 +379,7 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
383
379
|
/* @__PURE__ */ jsx4(Text3, { variant: "bodyLarge", style: [styles3.rowName, { color: fg }], numberOfLines: 1, children: profile.name })
|
|
384
380
|
] }) }, profile.id);
|
|
385
381
|
}) }) }),
|
|
386
|
-
|
|
387
|
-
/* @__PURE__ */ jsx4(Icon3, { source: "alert-outline", size: 64, color: colors.danger }),
|
|
388
|
-
/* @__PURE__ */ jsx4(Text3, { variant: "headlineLarge", style: [styles3.overlayTitle, { color: colors.danger }], children: "Delete Profile?" }),
|
|
389
|
-
/* @__PURE__ */ jsxs2(Text3, { variant: "bodyLarge", style: [styles3.overlayBody, { color: fg }], children: [
|
|
390
|
-
"This permanently deletes ",
|
|
391
|
-
confirmDeleteProfile.name,
|
|
392
|
-
"\u2019s profile. This cannot be undone."
|
|
393
|
-
] }),
|
|
394
|
-
/* @__PURE__ */ jsx4(Button, { mode: "contained", onPress: () => setConfirmDeleteId(null), style: styles3.overlayButton, buttonColor: cancelBg, textColor: cancelFg, children: "Cancel" }),
|
|
395
|
-
/* @__PURE__ */ jsx4(Button, { mode: "contained", onPress: handleConfirmDelete, style: styles3.overlayButton, buttonColor: colors.danger, textColor: colors.onDanger, children: "Delete" })
|
|
396
|
-
] }) }) })
|
|
382
|
+
/* @__PURE__ */ jsx4(ConfirmDialog, { visible: confirmDeleteProfile !== null, title: "Delete Profile?", message: confirmDeleteProfile ? `This permanently deletes ${confirmDeleteProfile.name}\u2019s profile. This cannot be undone.` : "", confirmLabel: "Delete", cancelLabel: "Cancel", icon: "alert-outline", destructive: true, onConfirm: handleConfirmDelete, onCancel: () => setConfirmDeleteId(null) })
|
|
397
383
|
] });
|
|
398
384
|
}
|
|
399
385
|
function AlignedColorPicker({ useAlign, ...props }) {
|
|
@@ -464,27 +450,6 @@ var styles3 = StyleSheet3.create({
|
|
|
464
450
|
borderStyle: "dashed",
|
|
465
451
|
borderWidth: 2
|
|
466
452
|
},
|
|
467
|
-
overlay: {
|
|
468
|
-
alignItems: "center",
|
|
469
|
-
backgroundColor: "rgba(0,0,0,0.72)",
|
|
470
|
-
bottom: 0,
|
|
471
|
-
justifyContent: "center",
|
|
472
|
-
left: 0,
|
|
473
|
-
position: "absolute",
|
|
474
|
-
right: 0,
|
|
475
|
-
top: 0
|
|
476
|
-
},
|
|
477
|
-
overlayBody: { textAlign: "center" },
|
|
478
|
-
overlayButton: { width: 160 },
|
|
479
|
-
overlayCard: {
|
|
480
|
-
alignItems: "center",
|
|
481
|
-
borderRadius: 20,
|
|
482
|
-
borderWidth: 1,
|
|
483
|
-
gap: 16,
|
|
484
|
-
maxWidth: 360,
|
|
485
|
-
padding: 32
|
|
486
|
-
},
|
|
487
|
-
overlayTitle: { fontWeight: "bold", marginBottom: -8 },
|
|
488
453
|
row: {
|
|
489
454
|
borderRadius: 8,
|
|
490
455
|
paddingVertical: 8
|
|
@@ -519,7 +484,7 @@ import { IconButton } from "@rific/feedback-press";
|
|
|
519
484
|
import { useRef as useRef2 } from "react";
|
|
520
485
|
import { StyleSheet as StyleSheet4, View as View4 } from "react-native";
|
|
521
486
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
522
|
-
function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride,
|
|
487
|
+
function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }) {
|
|
523
488
|
const { dark } = useAutoPaperTheme2();
|
|
524
489
|
const managerRef = useRef2(null);
|
|
525
490
|
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
@@ -528,7 +493,7 @@ function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, on
|
|
|
528
493
|
managerRef.current?.commitPendingEdit();
|
|
529
494
|
onBack();
|
|
530
495
|
};
|
|
531
|
-
return /* @__PURE__ */ jsx5(View4, { style: [styles4.container, { backgroundColor: bg }], children: /* @__PURE__ */ jsx5(ProfilesManager, { ref: managerRef, profiles, defaultColor, onCreate, onSave, onDelete, fg, fgMuted,
|
|
496
|
+
return /* @__PURE__ */ jsx5(View4, { style: [styles4.container, { backgroundColor: bg }], children: /* @__PURE__ */ jsx5(ProfilesManager, { ref: managerRef, profiles, defaultColor, onCreate, onSave, onDelete, fg, fgMuted, titleVariant, colorPreview, useColorPickerAlign, headerLeft: /* @__PURE__ */ jsx5(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: handleBack, accessibilityLabel: "Back" }) }) });
|
|
532
497
|
}
|
|
533
498
|
var styles4 = StyleSheet4.create({
|
|
534
499
|
container: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tastic/profile",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
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",
|
|
@@ -65,13 +65,13 @@
|
|
|
65
65
|
"@infinitetoken/eslint-config": "0.2.0",
|
|
66
66
|
"@infinitetoken/jest-config": "^0.4.0",
|
|
67
67
|
"@infinitetoken/tsconfig": "^0.5.0",
|
|
68
|
-
"@rific/auto-paper": "^0.
|
|
69
|
-
"@rific/core": "^0.
|
|
70
|
-
"@rific/feedback-press": "^0.
|
|
71
|
-
"@rific/focus-chain": "^0.4.
|
|
72
|
-
"@rific/toaster": "^0.
|
|
73
|
-
"@tastic/core": "^0.
|
|
74
|
-
"@tastic/hud": "^0.
|
|
68
|
+
"@rific/auto-paper": "^0.13.2",
|
|
69
|
+
"@rific/core": "^0.3.1",
|
|
70
|
+
"@rific/feedback-press": "^0.13.0",
|
|
71
|
+
"@rific/focus-chain": "^0.4.8",
|
|
72
|
+
"@rific/toaster": "^0.8.0",
|
|
73
|
+
"@tastic/core": "^0.14.0",
|
|
74
|
+
"@tastic/hud": "^0.19.0",
|
|
75
75
|
"@testing-library/dom": "^10.4.1",
|
|
76
76
|
"@testing-library/react": "^16.3.2",
|
|
77
77
|
"@types/jest": "^30.0.0",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"prettier": "^3.8.3",
|
|
86
86
|
"react": "^19.0.0",
|
|
87
87
|
"react-dom": "^19.0.0",
|
|
88
|
-
"react-native": "^0.
|
|
88
|
+
"react-native": "^0.86.3",
|
|
89
89
|
"react-native-paper": "^5.15.2",
|
|
90
90
|
"react-native-safe-area-context": "^5.0.0",
|
|
91
91
|
"tsup": "^8.0.0",
|
package/src/ProfilesManager.tsx
CHANGED
|
@@ -2,10 +2,10 @@ import { defaultColors, useAutoPaperTheme } from '@rific/auto-paper'
|
|
|
2
2
|
import { TouchableRipple } from '@rific/feedback-press'
|
|
3
3
|
import { useFocusChain } from '@rific/focus-chain'
|
|
4
4
|
import { useToast } from '@rific/toaster'
|
|
5
|
-
import { getInlineColorPickerContentSize, InlineColorPicker, PopoverHost, usePopoverHost } from '@tastic/hud'
|
|
5
|
+
import { ConfirmDialog, getInlineColorPickerContentSize, InlineColorPicker, PopoverHost, usePopoverHost } from '@tastic/hud'
|
|
6
6
|
import { ComponentProps, ReactNode, Ref, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'
|
|
7
7
|
import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, useWindowDimensions, View } from 'react-native'
|
|
8
|
-
import {
|
|
8
|
+
import { Icon, Text, TextInput } from 'react-native-paper'
|
|
9
9
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
10
10
|
|
|
11
11
|
import { ProfileChip } from './ProfileChip'
|
|
@@ -62,17 +62,13 @@ export interface ProfilesManagerProps {
|
|
|
62
62
|
// React 19 accepts `ref` as a plain prop on a function component (no forwardRef needed) - see
|
|
63
63
|
// ProfilesManagerHandle's own doc for why a host may need this.
|
|
64
64
|
ref?: Ref<ProfilesManagerHandle>
|
|
65
|
-
// Independently optional overrides for this screen's text
|
|
66
|
-
// literal-black/white-by-appearance formula as before when omitted
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
// BaseStatsScreen's identical reasoning for cardBorder: their low-alpha/neutral values already
|
|
71
|
-
// read fine against any cardBg, and (for Cancel specifically) are deliberately neutral rather than
|
|
72
|
-
// theme-derived so a host's own live gameplay/seat color can't land there by coincidence.
|
|
65
|
+
// Independently optional overrides for this screen's text colors - each defaults to the same
|
|
66
|
+
// literal-black/white-by-appearance formula as before when omitted. Same convention as @tastic/hud's
|
|
67
|
+
// BaseStatsScreen fg override, for a caller with its own app-wide chrome palette instead of that
|
|
68
|
+
// literal black/white convention. (cardBg, which only colored the delete confirmation, went with it
|
|
69
|
+
// when that became hud's ConfirmDialog, 0.13.0.)
|
|
73
70
|
fg?: string
|
|
74
71
|
fgMuted?: string
|
|
75
|
-
cardBg?: string
|
|
76
72
|
// 'headlineSmall' (this screen's own default, below), matching BaseStatsScreen's own settled
|
|
77
73
|
// default and the reasoning behind it - this title sits next to a small headerLeft back-button
|
|
78
74
|
// glyph the same way, and the previous hardcoded 'displaySmall' read as oversized there too.
|
|
@@ -115,12 +111,12 @@ function deriveTag(name: string): string {
|
|
|
115
111
|
// really unmounts it on a given host's router - see ProfilesManagerHandle's own doc for the
|
|
116
112
|
// react-navigation-web case where it doesn't, and why a host there needs the `ref` escape hatch
|
|
117
113
|
// instead of relying on the unmount fallback alone.
|
|
118
|
-
export function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride,
|
|
114
|
+
export function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, titleVariant = 'headlineSmall', colorPreview: colorPreviewProp, useColorPickerAlign }: ProfilesManagerProps) {
|
|
119
115
|
// An explicit prop wins; otherwise the app-wide ProfileColorProvider's, so the swatch previews
|
|
120
116
|
// match the ProfileChips drawn in the list.
|
|
121
117
|
const contextColorPreview = useProfileColorPreview()
|
|
122
118
|
const colorPreview = colorPreviewProp ?? contextColorPreview
|
|
123
|
-
const { dark
|
|
119
|
+
const { dark } = useAutoPaperTheme()
|
|
124
120
|
const insets = useSafeAreaInsets()
|
|
125
121
|
const { error: showErrorToast } = useToast()
|
|
126
122
|
// Only one row is ever in edit mode at a time (see commitEdit, called before any new row opens),
|
|
@@ -128,12 +124,6 @@ export function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDe
|
|
|
128
124
|
const host = usePopoverHost()
|
|
129
125
|
const fg = fgOverride ?? (dark ? '#FFFFFF' : '#000000')
|
|
130
126
|
const fgMuted = fgMutedOverride ?? (dark ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.5)')
|
|
131
|
-
const cardBg = cardBgOverride ?? (dark ? '#111111' : '#F2F2F2')
|
|
132
|
-
const cardBorder = dark ? 'rgba(255,255,255,0.2)' : 'rgba(0,0,0,0.2)'
|
|
133
|
-
// A contained neutral, not an outlined one - reads as "the other button" next to Delete's solid
|
|
134
|
-
// danger fill, rather than looking unfinished/secondary beside it.
|
|
135
|
-
const cancelBg = dark ? '#2A2A2A' : '#E0E0E0'
|
|
136
|
-
const cancelFg = dark ? '#FFFFFF' : '#000000'
|
|
137
127
|
|
|
138
128
|
// 'new' clears back to null once a commit resolves - there's only ever one row open at a time.
|
|
139
129
|
const [editingId, setEditingId] = useState<string | 'new' | null>(null)
|
|
@@ -296,32 +286,11 @@ export function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDe
|
|
|
296
286
|
</ScrollView>
|
|
297
287
|
</KeyboardAvoidingView>
|
|
298
288
|
|
|
299
|
-
{
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
<Text variant='headlineLarge' style={[styles.overlayTitle, { color: colors.danger }]}>
|
|
305
|
-
Delete Profile?
|
|
306
|
-
</Text>
|
|
307
|
-
<Text variant='bodyLarge' style={[styles.overlayBody, { color: fg }]}>
|
|
308
|
-
This permanently deletes {confirmDeleteProfile.name}’s profile. This cannot be undone.
|
|
309
|
-
</Text>
|
|
310
|
-
{/* Contained neutral, not buttonColor={colors.primary} - colors.primary is often a
|
|
311
|
-
live gameplay/seat color in host apps, so a contained Cancel in that color would just
|
|
312
|
-
be whatever's currently painted there, with no relation to "safe to tap". */}
|
|
313
|
-
<Button mode='contained' onPress={() => setConfirmDeleteId(null)} style={styles.overlayButton} buttonColor={cancelBg} textColor={cancelFg}>
|
|
314
|
-
Cancel
|
|
315
|
-
</Button>
|
|
316
|
-
{/* colors.danger/onDanger - auto-paper's actual semantic role for this, not a seat's
|
|
317
|
-
own live color by luck of the default palette. */}
|
|
318
|
-
<Button mode='contained' onPress={handleConfirmDelete} style={styles.overlayButton} buttonColor={colors.danger} textColor={colors.onDanger}>
|
|
319
|
-
Delete
|
|
320
|
-
</Button>
|
|
321
|
-
</View>
|
|
322
|
-
</View>
|
|
323
|
-
</Portal>
|
|
324
|
-
)}
|
|
289
|
+
{/* hud's ConfirmDialog, the one confirm card every app in the fleet uses, so this reads like the
|
|
290
|
+
Reset Everything?/New Game confirms beside it (Jay, 2026-10-15: one look for every dialog). It
|
|
291
|
+
used to be its own card here, in this screen's literal black/white (or the host's cardBg), with
|
|
292
|
+
two stacked contained buttons. */}
|
|
293
|
+
<ConfirmDialog visible={confirmDeleteProfile !== null} title='Delete Profile?' message={confirmDeleteProfile ? `This permanently deletes ${confirmDeleteProfile.name}\u2019s profile. This cannot be undone.` : ''} confirmLabel='Delete' cancelLabel='Cancel' icon='alert-outline' destructive onConfirm={handleConfirmDelete} onCancel={() => setConfirmDeleteId(null)} />
|
|
325
294
|
</>
|
|
326
295
|
)
|
|
327
296
|
}
|
|
@@ -466,27 +435,6 @@ const styles = StyleSheet.create({
|
|
|
466
435
|
borderStyle: 'dashed',
|
|
467
436
|
borderWidth: 2
|
|
468
437
|
},
|
|
469
|
-
overlay: {
|
|
470
|
-
alignItems: 'center',
|
|
471
|
-
backgroundColor: 'rgba(0,0,0,0.72)',
|
|
472
|
-
bottom: 0,
|
|
473
|
-
justifyContent: 'center',
|
|
474
|
-
left: 0,
|
|
475
|
-
position: 'absolute',
|
|
476
|
-
right: 0,
|
|
477
|
-
top: 0
|
|
478
|
-
},
|
|
479
|
-
overlayBody: { textAlign: 'center' },
|
|
480
|
-
overlayButton: { width: 160 },
|
|
481
|
-
overlayCard: {
|
|
482
|
-
alignItems: 'center',
|
|
483
|
-
borderRadius: 20,
|
|
484
|
-
borderWidth: 1,
|
|
485
|
-
gap: 16,
|
|
486
|
-
maxWidth: 360,
|
|
487
|
-
padding: 32
|
|
488
|
-
},
|
|
489
|
-
overlayTitle: { fontWeight: 'bold', marginBottom: -8 },
|
|
490
438
|
row: {
|
|
491
439
|
borderRadius: 8,
|
|
492
440
|
paddingVertical: 8
|
package/src/ProfilesScreen.tsx
CHANGED
|
@@ -33,11 +33,10 @@ export interface ProfilesScreenProps {
|
|
|
33
33
|
// Independently optional overrides, each defaulting to the same literal-black/white-by-appearance
|
|
34
34
|
// formula every current caller renders with unchanged (none of them pass these today) - same
|
|
35
35
|
// convention as BaseStatsScreen's and ProfilesManager's own fg/bg-shaped overrides. fg/fgMuted/
|
|
36
|
-
//
|
|
36
|
+
// titleVariant/colorPreview are forwarded straight through to the internal ProfilesManager.
|
|
37
37
|
fg?: string
|
|
38
38
|
fgMuted?: string
|
|
39
39
|
bg?: string
|
|
40
|
-
cardBg?: string
|
|
41
40
|
titleVariant?: MD3TextVariant
|
|
42
41
|
colorPreview?: (hex: string) => string
|
|
43
42
|
// Forwarded straight through to the internal ProfilesManager - a placement hook for the color swatch popover, for
|
|
@@ -53,7 +52,7 @@ export interface ProfilesScreenProps {
|
|
|
53
52
|
// still doesn't call useProfiles()/useGameStats() (or any host-specific hook) itself - those two
|
|
54
53
|
// hooks' shapes are genuinely app-specific (different CreateProfileInput/GameStatsContextValue per
|
|
55
54
|
// app), so this stays props-in/callbacks-out, exactly like ProfilesManager itself.
|
|
56
|
-
export function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride,
|
|
55
|
+
export function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }: ProfilesScreenProps) {
|
|
57
56
|
const { dark } = useAutoPaperTheme()
|
|
58
57
|
// Owns the ref itself - nothing outside this component ever needs it, since this is now the one
|
|
59
58
|
// thing that both renders ProfilesManager AND owns the only navigation trigger next to it.
|
|
@@ -75,7 +74,7 @@ export function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDel
|
|
|
75
74
|
|
|
76
75
|
return (
|
|
77
76
|
<View style={[styles.container, { backgroundColor: bg }]}>
|
|
78
|
-
<ProfilesManager ref={managerRef} profiles={profiles} defaultColor={defaultColor} onCreate={onCreate} onSave={onSave} onDelete={onDelete} fg={fg} fgMuted={fgMuted}
|
|
77
|
+
<ProfilesManager ref={managerRef} profiles={profiles} defaultColor={defaultColor} onCreate={onCreate} onSave={onSave} onDelete={onDelete} fg={fg} fgMuted={fgMuted} titleVariant={titleVariant} colorPreview={colorPreview} useColorPickerAlign={useColorPickerAlign} headerLeft={<IconButton icon='arrow-left' iconColor={fg} size={24} onPress={handleBack} accessibilityLabel='Back' />} />
|
|
79
78
|
</View>
|
|
80
79
|
)
|
|
81
80
|
}
|