@tastic/profile 0.12.0 → 0.14.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.
@@ -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 { Button, Icon, Portal, Text, TextInput } from 'react-native-paper'
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/card colors - each defaults to the same
66
- // literal-black/white-by-appearance formula as before when omitted, so every existing caller
67
- // (none of which pass these) renders identically to before. Same convention as @tastic/hud's
68
- // BaseStatsScreen fg/cardBg overrides, for a caller with its own app-wide chrome palette instead
69
- // of that literal black/white convention. cardBorder/cancelBg/cancelFg have no override, matching
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, cardBg: cardBgOverride, titleVariant = 'headlineSmall', colorPreview: colorPreviewProp, useColorPickerAlign }: ProfilesManagerProps) {
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, colors } = useAutoPaperTheme()
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
- {confirmDeleteProfile && (
300
- <Portal>
301
- <View style={styles.overlay}>
302
- <View style={[styles.overlayCard, { backgroundColor: cardBg, borderColor: cardBorder }]}>
303
- <Icon source='alert-outline' size={64} color={colors.danger} />
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}&rsquo;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
  }
@@ -427,11 +396,17 @@ const styles = StyleSheet.create({
427
396
  gap: 4,
428
397
  paddingHorizontal: 20
429
398
  },
399
+ // zIndex: the color popover lives inside this row, and a zIndex only orders siblings of one parent
400
+ // (React Native Web also gives every positioned view its own stacking context), so the popover's
401
+ // own zIndex never reached the rows after this one: they were drawn over it (a Kid row and the New
402
+ // Profile row across the swatches, 2026-10-16). Raising the row lifts the popover with it. There
403
+ // is only ever one edit row.
430
404
  editRow: {
431
405
  alignItems: 'center',
432
406
  flexDirection: 'row',
433
407
  gap: 8,
434
- paddingVertical: 6
408
+ paddingVertical: 6,
409
+ zIndex: 1
435
410
  },
436
411
  header: {
437
412
  alignItems: 'center',
@@ -466,27 +441,6 @@ const styles = StyleSheet.create({
466
441
  borderStyle: 'dashed',
467
442
  borderWidth: 2
468
443
  },
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
444
  row: {
491
445
  borderRadius: 8,
492
446
  paddingVertical: 8
@@ -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
- // cardBg/titleVariant/colorPreview are forwarded straight through to the internal ProfilesManager.
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, cardBg, titleVariant, colorPreview, useColorPickerAlign }: ProfilesScreenProps) {
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} cardBg={cardBg} titleVariant={titleVariant} colorPreview={colorPreview} useColorPickerAlign={useColorPickerAlign} headerLeft={<IconButton icon='arrow-left' iconColor={fg} size={24} onPress={handleBack} accessibilityLabel='Back' />} />
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
  }
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { ProfileChip } from './ProfileChip'
2
2
  export { ProfileColorProvider, useProfileColorPreview, useProfileDisplayColor } from './ProfileColorContext'
3
- export { getProfilePickerContentSize, ProfilePicker, type ProfilePickerAlignResult, type ProfilePickerContentSize } from './ProfilePicker'
3
+ export { getProfilePickerContentSize, ProfilePicker, type ProfilePickerAlignResult, type ProfilePickerColorSection, type ProfilePickerContentSize } from './ProfilePicker'
4
4
  export { type ProfileEditPatch, ProfilesManager, type ProfilesManagerHandle, type ProfilesManagerProps } from './ProfilesManager'
5
5
  export { ProfilesScreen, type ProfilesScreenProps } from './ProfilesScreen'
6
6
  export { isValidProfile, isValidTag, MAX_PROFILE_NAME_LENGTH, MAX_TAG_LENGTH } from './profilesValidation'