@tastic/profile 0.14.1 → 0.16.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 +46 -11
- package/dist/index.d.mts +67 -30
- package/dist/index.d.ts +67 -30
- package/dist/index.js +429 -237
- package/dist/index.mjs +393 -206
- package/package.json +9 -9
- package/src/IdentityChip.tsx +8 -2
- package/src/OptionPicker.tsx +64 -0
- package/src/PickerMenu.tsx +512 -0
- package/src/ProfileColorContext.tsx +30 -8
- package/src/ProfilePicker.tsx +61 -426
- package/src/ProfilesManager.tsx +9 -6
- package/src/SwatchLayer.tsx +28 -0
- package/src/index.ts +4 -3
- package/src/redux/resolveInitialProfiles.ts +3 -2
- package/src/redux/seatColorsSlice.ts +79 -29
- package/src/redux/useSharedProfilesSync.ts +12 -6
- package/src/sharedProfileStore.ts +87 -11
package/dist/index.mjs
CHANGED
|
@@ -5,18 +5,67 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
5
5
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
6
|
});
|
|
7
7
|
|
|
8
|
+
// src/PickerMenu.tsx
|
|
9
|
+
import { defaultColors, FadingScrollView, getContrastColor as getContrastColor2 } from "@rific/auto-paper";
|
|
10
|
+
import { TouchableRipple } from "@rific/feedback-press";
|
|
11
|
+
import { PopoverBody, useAutoAlign } from "@tastic/hud/chrome";
|
|
12
|
+
import { useState } from "react";
|
|
13
|
+
import { StyleSheet as StyleSheet3, View as View3 } from "react-native";
|
|
14
|
+
import { Icon as Icon2, Text as Text2 } from "react-native-paper";
|
|
15
|
+
|
|
8
16
|
// src/IdentityChip.tsx
|
|
9
17
|
import { getContrastColor } from "@rific/auto-paper";
|
|
10
|
-
import { StyleSheet, View } from "react-native";
|
|
18
|
+
import { StyleSheet as StyleSheet2, View as View2 } from "react-native";
|
|
11
19
|
import { Icon, Text } from "react-native-paper";
|
|
20
|
+
|
|
21
|
+
// src/ProfileColorContext.tsx
|
|
22
|
+
import { createContext, useContext, useMemo } from "react";
|
|
12
23
|
import { jsx } from "react/jsx-runtime";
|
|
24
|
+
var ProfileColorContext = createContext({});
|
|
25
|
+
function ProfileColorProvider({ preview, renderSwatch, children }) {
|
|
26
|
+
const value = useMemo(() => ({ preview, renderSwatch }), [preview, renderSwatch]);
|
|
27
|
+
return /* @__PURE__ */ jsx(ProfileColorContext.Provider, { value, children });
|
|
28
|
+
}
|
|
29
|
+
function useProfileColorPreview() {
|
|
30
|
+
return useContext(ProfileColorContext).preview;
|
|
31
|
+
}
|
|
32
|
+
function useProfileSwatchRenderer() {
|
|
33
|
+
return useContext(ProfileColorContext).renderSwatch;
|
|
34
|
+
}
|
|
35
|
+
function useProfileDisplayColor(hex) {
|
|
36
|
+
const preview = useProfileColorPreview();
|
|
37
|
+
return preview ? preview(hex) : hex;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/SwatchLayer.tsx
|
|
41
|
+
import { StyleSheet, View } from "react-native";
|
|
42
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
43
|
+
function SwatchLayer({ render, color, size }) {
|
|
44
|
+
return /* @__PURE__ */ jsx2(View, { style: [styles.layer, { borderRadius: size / 2, height: size, width: size }], children: render(color, size) });
|
|
45
|
+
}
|
|
46
|
+
var styles = StyleSheet.create({
|
|
47
|
+
layer: {
|
|
48
|
+
left: 0,
|
|
49
|
+
overflow: "hidden",
|
|
50
|
+
pointerEvents: "none",
|
|
51
|
+
position: "absolute",
|
|
52
|
+
top: 0
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// src/IdentityChip.tsx
|
|
57
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
13
58
|
var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
|
|
14
59
|
function IdentityChip({ color, tag, icon, size, filled = true }) {
|
|
60
|
+
const renderSwatch = useProfileSwatchRenderer();
|
|
15
61
|
const contrastColor = getContrastColor(color);
|
|
16
62
|
const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : tag && Array.from(tag).length >= 3 ? 0.32 : 0.4);
|
|
17
|
-
return /* @__PURE__ */
|
|
63
|
+
return /* @__PURE__ */ jsxs(View2, { style: [styles2.chip, { borderRadius: size / 2, height: size, width: size }, filled && !renderSwatch && { backgroundColor: color }], children: [
|
|
64
|
+
filled && renderSwatch && /* @__PURE__ */ jsx3(SwatchLayer, { render: renderSwatch, color, size }),
|
|
65
|
+
tag ? /* @__PURE__ */ jsx3(Text, { style: [styles2.tag, { color: contrastColor, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx3(Icon, { source: icon, size: size * 0.6, color: contrastColor })
|
|
66
|
+
] });
|
|
18
67
|
}
|
|
19
|
-
var
|
|
68
|
+
var styles2 = StyleSheet2.create({
|
|
20
69
|
chip: {
|
|
21
70
|
alignItems: "center",
|
|
22
71
|
justifyContent: "center"
|
|
@@ -27,36 +76,8 @@ var styles = StyleSheet.create({
|
|
|
27
76
|
}
|
|
28
77
|
});
|
|
29
78
|
|
|
30
|
-
// src/
|
|
31
|
-
import {
|
|
32
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
33
|
-
var ProfileColorContext = createContext(void 0);
|
|
34
|
-
function ProfileColorProvider({ preview, children }) {
|
|
35
|
-
return /* @__PURE__ */ jsx2(ProfileColorContext.Provider, { value: preview, children });
|
|
36
|
-
}
|
|
37
|
-
function useProfileColorPreview() {
|
|
38
|
-
return useContext(ProfileColorContext);
|
|
39
|
-
}
|
|
40
|
-
function useProfileDisplayColor(hex) {
|
|
41
|
-
const preview = useProfileColorPreview();
|
|
42
|
-
return preview ? preview(hex) : hex;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// src/ProfileChip.tsx
|
|
46
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
47
|
-
function ProfileChip({ profile, size = 22, filled = true }) {
|
|
48
|
-
const color = useProfileDisplayColor(profile.color);
|
|
49
|
-
return /* @__PURE__ */ jsx3(IdentityChip, { color, tag: profile.tag, icon: "account", size, filled });
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// src/ProfilePicker.tsx
|
|
53
|
-
import { defaultColors, FadingScrollView, getContrastColor as getContrastColor2 } from "@rific/auto-paper";
|
|
54
|
-
import { TouchableRipple } from "@rific/feedback-press";
|
|
55
|
-
import { PopoverBody, useAutoAlign } from "@tastic/hud";
|
|
56
|
-
import { useMemo, useState } from "react";
|
|
57
|
-
import { StyleSheet as StyleSheet2, View as View2 } from "react-native";
|
|
58
|
-
import { Icon as Icon2, Text as Text2 } from "react-native-paper";
|
|
59
|
-
import { Fragment, jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
79
|
+
// src/PickerMenu.tsx
|
|
80
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
60
81
|
var TRIGGER_HEIGHT = 28;
|
|
61
82
|
var POPOVER_WIDTH = 220;
|
|
62
83
|
var ROW_HEIGHT = 36;
|
|
@@ -67,6 +88,8 @@ var CHIP_SIZE = 32;
|
|
|
67
88
|
var CHIP_TRIGGER_PADDING = 6;
|
|
68
89
|
var SWATCH_SIZE = 28;
|
|
69
90
|
var SWATCH_MIN_GAP = 6;
|
|
91
|
+
var SWATCH_RING_WIDTH = 2;
|
|
92
|
+
var COLOR_ROW_SWATCH_SIZE = 22;
|
|
70
93
|
var COLOR_ROW_CAPTIONED_HEIGHT = 48;
|
|
71
94
|
var COLOR_LABEL_LINE = 18;
|
|
72
95
|
var COLOR_CAPTION_LINE = 16;
|
|
@@ -83,59 +106,41 @@ function colorPageHeight(swatches) {
|
|
|
83
106
|
const rows = Math.ceil(swatches.length / GRID_COLUMNS);
|
|
84
107
|
return ROW_HEIGHT + GRID_TOP + rows * SWATCH_SIZE + (rows - 1) * GRID_GAP + GRID_BOTTOM;
|
|
85
108
|
}
|
|
86
|
-
function
|
|
87
|
-
const rowCount = profileCount + 1 + (hasManageRow ? 1 : 0);
|
|
109
|
+
function pickerContentSize(rowCount, colorSection) {
|
|
88
110
|
const list = rowCount * ROW_HEIGHT + (colorSection ? colorRowHeight(colorSection.caption) + COLOR_ROW_DIVIDER : 0);
|
|
89
111
|
const content = colorSection ? Math.max(list, colorPageHeight(colorSection.swatches ?? defaultColors)) : list;
|
|
90
112
|
return { width: POPOVER_WIDTH, height: LIST_PADDING * 2 + content };
|
|
91
113
|
}
|
|
92
|
-
function
|
|
93
|
-
const id = `${idPrefix}-profile`;
|
|
94
|
-
const colorPreview = useProfileColorPreview();
|
|
114
|
+
function PickerMenu({ id, host, color, dark, label, chip, colorSection, homeColor, contentSize, align: forcedAlign, alignOverride, rotation, triggerStyle, footer, children }) {
|
|
95
115
|
const menuBg = dark ? "#000000" : "#FFFFFF";
|
|
96
116
|
const fg = dark ? "#FFFFFF" : "#000000";
|
|
97
117
|
const open = host.openId === id;
|
|
98
|
-
const
|
|
99
|
-
const sortedProfiles = useMemo(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
100
|
-
const { width: contentWidth, height: contentHeight } = getProfilePickerContentSize(profiles.length, !!onManage, colorSection);
|
|
101
|
-
const auto = useAutoAlign(open, contentWidth, contentHeight, rotation);
|
|
118
|
+
const auto = useAutoAlign(open, contentSize.width, contentSize.height, rotation);
|
|
102
119
|
const placement = alignOverride ?? auto;
|
|
103
120
|
const { maxHeight, measured, triggerRef, verticalAlign } = placement;
|
|
104
121
|
const align = forcedAlign ?? placement.align;
|
|
105
|
-
const
|
|
106
|
-
onSelect(null);
|
|
107
|
-
host.close();
|
|
108
|
-
};
|
|
109
|
-
const handleSelectExisting = (profile) => {
|
|
110
|
-
if (profile.id === takenId) return;
|
|
111
|
-
onSelect(profile);
|
|
112
|
-
host.close();
|
|
113
|
-
};
|
|
114
|
-
const handleManage = () => {
|
|
115
|
-
host.close();
|
|
116
|
-
onManage?.();
|
|
117
|
-
};
|
|
118
|
-
const chipTrigger = !!colorSection;
|
|
122
|
+
const chipTrigger = !!chip;
|
|
119
123
|
const [triggerWidth, setTriggerWidth] = useState(0);
|
|
120
124
|
const handleAnchorLayout = chipTrigger ? (e) => setTriggerWidth(e.nativeEvent.layout.width) : void 0;
|
|
121
125
|
const [colorPage, setColorPage] = useState(false);
|
|
126
|
+
const showColorPage = !!colorSection && colorPage;
|
|
122
127
|
const handleTriggerPress = () => {
|
|
123
128
|
setColorPage(false);
|
|
124
129
|
host.toggle(id);
|
|
125
130
|
};
|
|
126
|
-
const triggerLabel =
|
|
127
|
-
const
|
|
128
|
-
return /* @__PURE__ */
|
|
129
|
-
/* @__PURE__ */ jsx4(TouchableRipple, { onPress: handleTriggerPress, borderless: chipTrigger, style: [
|
|
130
|
-
|
|
131
|
-
/* @__PURE__ */ jsx4(Text2, { style: [
|
|
131
|
+
const triggerLabel = label.toUpperCase();
|
|
132
|
+
const showFooter = !!footer && !showColorPage;
|
|
133
|
+
return /* @__PURE__ */ jsxs2(View3, { style: [styles3.anchor, open && styles3.anchorOpen], onLayout: handleAnchorLayout, children: [
|
|
134
|
+
/* @__PURE__ */ jsx4(TouchableRipple, { onPress: handleTriggerPress, borderless: chipTrigger, style: [styles3.trigger, chipTrigger && styles3.chipTrigger, triggerStyle], accessibilityRole: "button", accessibilityLabel: triggerLabel, children: /* @__PURE__ */ jsxs2(View3, { ref: triggerRef, collapsable: false, style: chipTrigger ? styles3.chipTriggerInner : styles3.triggerInner, children: [
|
|
135
|
+
chip && /* @__PURE__ */ jsx4(IdentityChip, { color, tag: chip.tag, icon: chip.icon, size: CHIP_SIZE }),
|
|
136
|
+
/* @__PURE__ */ jsx4(Text2, { style: [styles3.triggerLabel, { color: chipTrigger ? fg : color }], numberOfLines: 1, children: triggerLabel }),
|
|
132
137
|
/* @__PURE__ */ jsx4(Icon2, { source: "menu-down", size: 16, color: chipTrigger ? fg : color })
|
|
133
138
|
] }) }),
|
|
134
|
-
/* @__PURE__ */ jsx4(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: chipTrigger ? triggerWidth : TRIGGER_HEIGHT, children: /* @__PURE__ */
|
|
135
|
-
|
|
139
|
+
/* @__PURE__ */ jsx4(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: chipTrigger ? triggerWidth : TRIGGER_HEIGHT, children: /* @__PURE__ */ jsxs2(
|
|
140
|
+
View3,
|
|
136
141
|
{
|
|
137
142
|
style: [
|
|
138
|
-
|
|
143
|
+
styles3.menu,
|
|
139
144
|
{
|
|
140
145
|
backgroundColor: menuBg,
|
|
141
146
|
borderColor: color,
|
|
@@ -144,78 +149,88 @@ function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, color, d
|
|
|
144
149
|
}
|
|
145
150
|
],
|
|
146
151
|
children: [
|
|
147
|
-
/* @__PURE__ */ jsx4(FadingScrollView, { contentContainerStyle: [
|
|
152
|
+
/* @__PURE__ */ jsx4(FadingScrollView, { contentContainerStyle: [styles3.menuContent, showFooter && styles3.menuContentAboveFooter], showsVerticalScrollIndicator: false, children: colorSection && colorPage ? /* @__PURE__ */ jsx4(ColorPage, { section: colorSection, homeColor, fg, onBack: () => setColorPage(false) }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
148
153
|
colorSection && /* @__PURE__ */ jsx4(ColorRow, { section: colorSection, fg, dividerColor: color, onPress: () => setColorPage(true) }),
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const taken = profile.id === takenId;
|
|
155
|
-
const selected = selectedId === profile.id;
|
|
156
|
-
const rowColor = colorPreview ? colorPreview(profile.color) : profile.color;
|
|
157
|
-
return /* @__PURE__ */ jsx4(TouchableRipple, { disabled: taken, onPress: () => handleSelectExisting(profile), style: [styles2.row, selected && { backgroundColor: rowColor }], children: /* @__PURE__ */ jsxs(View2, { style: [styles2.rowInner, taken && styles2.rowDisabled], children: [
|
|
158
|
-
/* @__PURE__ */ jsx4(ProfileChip, { profile, filled: !selected }),
|
|
159
|
-
/* @__PURE__ */ jsx4(
|
|
160
|
-
Text2,
|
|
161
|
-
{
|
|
162
|
-
style: [
|
|
163
|
-
styles2.rowLabel,
|
|
164
|
-
{
|
|
165
|
-
color: selected ? getContrastColor2(rowColor) : fg
|
|
166
|
-
}
|
|
167
|
-
],
|
|
168
|
-
numberOfLines: 1,
|
|
169
|
-
children: profile.name
|
|
170
|
-
}
|
|
171
|
-
)
|
|
172
|
-
] }) }, profile.id);
|
|
173
|
-
})
|
|
174
|
-
] }) }, colorSection && colorPage ? "color" : "list"),
|
|
175
|
-
manageFooter && /* @__PURE__ */ jsxs(View2, { style: styles2.menuFooter, children: [
|
|
176
|
-
/* @__PURE__ */ jsx4(View2, { style: [styles2.divider, { backgroundColor: color }] }),
|
|
177
|
-
/* @__PURE__ */ jsx4(TouchableRipple, { onPress: handleManage, style: styles2.row, children: /* @__PURE__ */ jsxs(View2, { style: styles2.manageRowInner, children: [
|
|
178
|
-
/* @__PURE__ */ jsx4(Icon2, { source: "account-cog-outline", size: 16, color }),
|
|
179
|
-
/* @__PURE__ */ jsx4(Text2, { style: [styles2.manageLabel, { color: fg }], children: "Manage" })
|
|
180
|
-
] }) })
|
|
154
|
+
children
|
|
155
|
+
] }) }, showColorPage ? "color" : "list"),
|
|
156
|
+
showFooter && /* @__PURE__ */ jsxs2(View3, { style: styles3.menuFooter, children: [
|
|
157
|
+
/* @__PURE__ */ jsx4(View3, { style: [styles3.divider, { backgroundColor: color }] }),
|
|
158
|
+
footer
|
|
181
159
|
] })
|
|
182
160
|
]
|
|
183
161
|
}
|
|
184
162
|
) })
|
|
185
163
|
] });
|
|
186
164
|
}
|
|
165
|
+
function IconRow({ icon, label, selected, color, fg, disabled = false, onPress }) {
|
|
166
|
+
const textColor = selected ? getContrastColor2(color) : fg;
|
|
167
|
+
return /* @__PURE__ */ jsx4(TouchableRipple, { disabled, onPress, style: [pickerMenuStyles.row, selected && { backgroundColor: color }], children: /* @__PURE__ */ jsxs2(View3, { style: [pickerMenuStyles.rowInner, disabled && pickerMenuStyles.rowDisabled], children: [
|
|
168
|
+
/* @__PURE__ */ jsx4(View3, { style: styles3.iconSlot, children: /* @__PURE__ */ jsx4(Icon2, { source: icon, size: 13, color: textColor }) }),
|
|
169
|
+
/* @__PURE__ */ jsx4(Text2, { style: [pickerMenuStyles.rowLabel, { color: textColor }], numberOfLines: 1, children: label })
|
|
170
|
+
] }) });
|
|
171
|
+
}
|
|
187
172
|
function ColorRow({ section, fg, dividerColor, onPress }) {
|
|
188
173
|
const { value, previewValue, label = "Color", caption } = section;
|
|
174
|
+
const renderSwatch = useProfileSwatchRenderer();
|
|
189
175
|
const drawn = previewValue ? previewValue(value) : value;
|
|
190
|
-
return /* @__PURE__ */
|
|
191
|
-
/* @__PURE__ */ jsx4(TouchableRipple, { onPress, style: [
|
|
192
|
-
/* @__PURE__ */ jsx4(
|
|
193
|
-
/* @__PURE__ */
|
|
194
|
-
/* @__PURE__ */ jsx4(Text2, { style: [
|
|
195
|
-
caption ? /* @__PURE__ */ jsx4(Text2, { style: [
|
|
176
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
177
|
+
/* @__PURE__ */ jsx4(TouchableRipple, { onPress, style: [pickerMenuStyles.row, { height: colorRowHeight(caption) }], accessibilityRole: "button", accessibilityLabel: caption ? `${label}, ${caption}` : label, children: /* @__PURE__ */ jsxs2(View3, { style: pickerMenuStyles.rowInner, children: [
|
|
178
|
+
/* @__PURE__ */ jsx4(View3, { style: [styles3.colorRowSwatch, !renderSwatch && { backgroundColor: drawn }], children: renderSwatch && /* @__PURE__ */ jsx4(SwatchLayer, { render: renderSwatch, color: drawn, size: COLOR_ROW_SWATCH_SIZE }) }),
|
|
179
|
+
/* @__PURE__ */ jsxs2(View3, { style: styles3.colorRowText, children: [
|
|
180
|
+
/* @__PURE__ */ jsx4(Text2, { style: [styles3.colorRowLabel, { color: fg }], numberOfLines: 1, children: label }),
|
|
181
|
+
caption ? /* @__PURE__ */ jsx4(Text2, { style: [styles3.colorRowCaption, { color: fg }], numberOfLines: 1, children: caption }) : null
|
|
196
182
|
] }),
|
|
197
183
|
/* @__PURE__ */ jsx4(Icon2, { source: "chevron-right", size: 18, color: fg })
|
|
198
184
|
] }) }),
|
|
199
|
-
/* @__PURE__ */ jsx4(
|
|
185
|
+
/* @__PURE__ */ jsx4(View3, { style: [styles3.divider, { backgroundColor: dividerColor }] })
|
|
200
186
|
] });
|
|
201
187
|
}
|
|
202
|
-
function ColorPage({ section,
|
|
203
|
-
const { value, onChange, previewValue, swatches = defaultColors, label = "Color" } = section;
|
|
188
|
+
function ColorPage({ section, homeColor, fg, onBack }) {
|
|
189
|
+
const { value, onChange, previewValue, swatches = defaultColors, label = "Color", takenValue, allowSwapTaken } = section;
|
|
190
|
+
const renderSwatch = useProfileSwatchRenderer();
|
|
204
191
|
const displayFor = previewValue ?? ((hex) => hex);
|
|
205
|
-
return /* @__PURE__ */
|
|
206
|
-
/* @__PURE__ */ jsx4(TouchableRipple, { onPress: onBack, style:
|
|
207
|
-
/* @__PURE__ */ jsx4(
|
|
208
|
-
/* @__PURE__ */ jsx4(Text2, { style: [
|
|
192
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
193
|
+
/* @__PURE__ */ jsx4(TouchableRipple, { onPress: onBack, style: pickerMenuStyles.row, accessibilityRole: "button", accessibilityLabel: "Back", children: /* @__PURE__ */ jsxs2(View3, { style: pickerMenuStyles.rowInner, children: [
|
|
194
|
+
/* @__PURE__ */ jsx4(View3, { style: styles3.iconSlot, children: /* @__PURE__ */ jsx4(Icon2, { source: "chevron-left", size: 20, color: fg }) }),
|
|
195
|
+
/* @__PURE__ */ jsx4(Text2, { style: [styles3.colorRowLabel, { color: fg }], numberOfLines: 1, children: label })
|
|
209
196
|
] }) }),
|
|
210
|
-
/* @__PURE__ */ jsx4(
|
|
197
|
+
/* @__PURE__ */ jsx4(View3, { style: styles3.swatches, children: swatches.map((swatch) => {
|
|
211
198
|
const selected = swatch.value.toLowerCase() === value.toLowerCase();
|
|
212
|
-
const
|
|
199
|
+
const isHomeColor = !!homeColor && swatch.value.toLowerCase() === homeColor.toLowerCase();
|
|
200
|
+
const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
|
|
201
|
+
const swappable = taken && !!allowSwapTaken;
|
|
213
202
|
const swatchDisplay = displayFor(swatch.value);
|
|
214
|
-
|
|
203
|
+
const contrast = getContrastColor2(swatchDisplay);
|
|
204
|
+
const accessibilityLabel = swappable ? `${swatch.label}, swap` : taken ? `${swatch.label}, taken` : isHomeColor ? `${swatch.label}, profile color` : swatch.label;
|
|
205
|
+
const mark = selected ? /* @__PURE__ */ jsx4(Icon2, { source: "check", size: 16, color: contrast }) : swappable ? /* @__PURE__ */ jsx4(Icon2, { source: "swap-horizontal", size: 16, color: contrast }) : taken ? /* @__PURE__ */ jsx4(Icon2, { source: "close", size: 16, color: contrast }) : isHomeColor ? /* @__PURE__ */ jsx4(View3, { style: [styles3.homeDot, { backgroundColor: contrast }] }) : /* @__PURE__ */ jsx4(View3, {});
|
|
206
|
+
return /* @__PURE__ */ jsx4(TouchableRipple, { disabled: taken && !swappable, onPress: () => onChange(swatch.value), borderless: true, style: [styles3.swatch, !renderSwatch && { backgroundColor: swatchDisplay }, selected && styles3.swatchSelected, taken && !swappable && styles3.swatchTaken], accessibilityRole: "button", accessibilityLabel, accessibilityState: { selected, disabled: taken && !swappable }, children: renderSwatch ? /* @__PURE__ */ jsxs2(View3, { style: styles3.swatchFace, children: [
|
|
207
|
+
/* @__PURE__ */ jsx4(SwatchLayer, { render: renderSwatch, color: swatchDisplay, size: selected ? SWATCH_SIZE - 2 * SWATCH_RING_WIDTH : SWATCH_SIZE }),
|
|
208
|
+
mark
|
|
209
|
+
] }) : mark }, swatch.value);
|
|
215
210
|
}) })
|
|
216
211
|
] });
|
|
217
212
|
}
|
|
218
|
-
var
|
|
213
|
+
var pickerMenuStyles = StyleSheet3.create({
|
|
214
|
+
row: {
|
|
215
|
+
borderRadius: 8,
|
|
216
|
+
height: ROW_HEIGHT
|
|
217
|
+
},
|
|
218
|
+
rowDisabled: {
|
|
219
|
+
opacity: 0.35
|
|
220
|
+
},
|
|
221
|
+
rowInner: {
|
|
222
|
+
alignItems: "center",
|
|
223
|
+
flexDirection: "row",
|
|
224
|
+
flex: 1,
|
|
225
|
+
gap: 8,
|
|
226
|
+
paddingHorizontal: ROW_INSET
|
|
227
|
+
},
|
|
228
|
+
rowLabel: {
|
|
229
|
+
flex: 1,
|
|
230
|
+
fontSize: 14
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
var styles3 = StyleSheet3.create({
|
|
219
234
|
anchor: {
|
|
220
235
|
position: "relative"
|
|
221
236
|
},
|
|
@@ -251,45 +266,38 @@ var styles2 = StyleSheet2.create({
|
|
|
251
266
|
},
|
|
252
267
|
// The color in effect, in the same 22pt slot as every row's chip.
|
|
253
268
|
colorRowSwatch: {
|
|
254
|
-
borderRadius:
|
|
255
|
-
height:
|
|
256
|
-
width:
|
|
269
|
+
borderRadius: COLOR_ROW_SWATCH_SIZE / 2,
|
|
270
|
+
height: COLOR_ROW_SWATCH_SIZE,
|
|
271
|
+
width: COLOR_ROW_SWATCH_SIZE
|
|
257
272
|
},
|
|
258
273
|
colorRowText: {
|
|
259
274
|
flex: 1
|
|
260
275
|
},
|
|
261
|
-
// Separates
|
|
262
|
-
// action, not just another row in the same list. Tinted with the seat's own color
|
|
263
|
-
// backgroundColor) rather than a flat gray, matching the border/caret's own
|
|
276
|
+
// Separates the footer (a navigation action) and the Color row from the selectable rows - a
|
|
277
|
+
// different kind of action, not just another row in the same list. Tinted with the seat's own color
|
|
278
|
+
// (passed in as backgroundColor) rather than a flat gray, matching the border/caret's own
|
|
279
|
+
// accent-tinted look.
|
|
264
280
|
divider: {
|
|
265
281
|
height: 1,
|
|
266
282
|
marginVertical: 4,
|
|
267
283
|
opacity: 0.25
|
|
268
284
|
},
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
285
|
+
// The selected profile's own color (see ColorPage).
|
|
286
|
+
homeDot: {
|
|
287
|
+
borderRadius: 4,
|
|
288
|
+
height: 8,
|
|
289
|
+
width: 8
|
|
290
|
+
},
|
|
291
|
+
// Matches ProfileChip's own default size/shape exactly (see IconRow's own render-site comment) -
|
|
292
|
+
// no backgroundColor, unlike ProfileChip's own default: an icon row has no identity color of its
|
|
293
|
+
// own to fill it with, and the render site colors the icon directly instead.
|
|
294
|
+
iconSlot: {
|
|
273
295
|
alignItems: "center",
|
|
274
296
|
borderRadius: 11,
|
|
275
297
|
height: 22,
|
|
276
298
|
justifyContent: "center",
|
|
277
299
|
width: 22
|
|
278
300
|
},
|
|
279
|
-
manageLabel: {
|
|
280
|
-
fontSize: 13,
|
|
281
|
-
fontWeight: "700",
|
|
282
|
-
letterSpacing: 0.5
|
|
283
|
-
},
|
|
284
|
-
// Centered rather than left-aligned like a normal row - see this row's own render-site comment
|
|
285
|
-
// for why (a footer action, not another selectable identity).
|
|
286
|
-
manageRowInner: {
|
|
287
|
-
alignItems: "center",
|
|
288
|
-
flexDirection: "row",
|
|
289
|
-
gap: 6,
|
|
290
|
-
height: "100%",
|
|
291
|
-
justifyContent: "center"
|
|
292
|
-
},
|
|
293
301
|
menu: {
|
|
294
302
|
borderRadius: 12,
|
|
295
303
|
borderWidth: MENU_BORDER_WIDTH,
|
|
@@ -305,7 +313,7 @@ var styles2 = StyleSheet2.create({
|
|
|
305
313
|
menuContent: {
|
|
306
314
|
padding: LIST_PADDING
|
|
307
315
|
},
|
|
308
|
-
// With
|
|
316
|
+
// With a footer pinned below, its own padding takes the bottom edge (same total height as before).
|
|
309
317
|
menuContentAboveFooter: {
|
|
310
318
|
paddingBottom: 0
|
|
311
319
|
},
|
|
@@ -313,30 +321,6 @@ var styles2 = StyleSheet2.create({
|
|
|
313
321
|
paddingBottom: LIST_PADDING,
|
|
314
322
|
paddingHorizontal: LIST_PADDING
|
|
315
323
|
},
|
|
316
|
-
// The selected profile's own color (see ColorSection).
|
|
317
|
-
profileDot: {
|
|
318
|
-
borderRadius: 4,
|
|
319
|
-
height: 8,
|
|
320
|
-
width: 8
|
|
321
|
-
},
|
|
322
|
-
row: {
|
|
323
|
-
borderRadius: 8,
|
|
324
|
-
height: ROW_HEIGHT
|
|
325
|
-
},
|
|
326
|
-
rowDisabled: {
|
|
327
|
-
opacity: 0.35
|
|
328
|
-
},
|
|
329
|
-
rowInner: {
|
|
330
|
-
alignItems: "center",
|
|
331
|
-
flexDirection: "row",
|
|
332
|
-
flex: 1,
|
|
333
|
-
gap: 8,
|
|
334
|
-
paddingHorizontal: ROW_INSET
|
|
335
|
-
},
|
|
336
|
-
rowLabel: {
|
|
337
|
-
flex: 1,
|
|
338
|
-
fontSize: 14
|
|
339
|
-
},
|
|
340
324
|
swatch: {
|
|
341
325
|
alignItems: "center",
|
|
342
326
|
borderRadius: SWATCH_SIZE / 2,
|
|
@@ -344,9 +328,22 @@ var styles2 = StyleSheet2.create({
|
|
|
344
328
|
justifyContent: "center",
|
|
345
329
|
width: SWATCH_SIZE
|
|
346
330
|
},
|
|
331
|
+
// A drawn swatch (see ColorPage) and its mark: the whole circle inside the selected one's ring.
|
|
332
|
+
swatchFace: {
|
|
333
|
+
alignItems: "center",
|
|
334
|
+
bottom: 0,
|
|
335
|
+
justifyContent: "center",
|
|
336
|
+
left: 0,
|
|
337
|
+
position: "absolute",
|
|
338
|
+
right: 0,
|
|
339
|
+
top: 0
|
|
340
|
+
},
|
|
347
341
|
swatchSelected: {
|
|
348
342
|
borderColor: "#ffffff",
|
|
349
|
-
borderWidth:
|
|
343
|
+
borderWidth: SWATCH_RING_WIDTH
|
|
344
|
+
},
|
|
345
|
+
swatchTaken: {
|
|
346
|
+
opacity: 0.35
|
|
350
347
|
},
|
|
351
348
|
swatches: {
|
|
352
349
|
columnGap: GRID_GAP,
|
|
@@ -374,15 +371,114 @@ var styles2 = StyleSheet2.create({
|
|
|
374
371
|
}
|
|
375
372
|
});
|
|
376
373
|
|
|
377
|
-
// src/
|
|
378
|
-
import {
|
|
374
|
+
// src/OptionPicker.tsx
|
|
375
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
376
|
+
function getOptionPickerContentSize(optionCount, colorSection) {
|
|
377
|
+
return pickerContentSize(optionCount, colorSection);
|
|
378
|
+
}
|
|
379
|
+
function OptionPicker({ id, host, options, value, onChange, color, dark, icon, rowIcon = icon, colorSection, align, alignOverride, rotation = 0, triggerStyle }) {
|
|
380
|
+
const fg = dark ? "#FFFFFF" : "#000000";
|
|
381
|
+
const selected = options.find((option) => option.value === value);
|
|
382
|
+
const handleSelect = (option) => {
|
|
383
|
+
onChange(option.value);
|
|
384
|
+
host.close();
|
|
385
|
+
};
|
|
386
|
+
return /* @__PURE__ */ jsx5(PickerMenu, { id, host, color, dark, label: selected?.label ?? "", chip: { icon }, colorSection, contentSize: getOptionPickerContentSize(options.length, colorSection), align, alignOverride, rotation, triggerStyle, children: options.map((option) => /* @__PURE__ */ jsx5(IconRow, { icon: option.icon ?? rowIcon, label: option.label, selected: option.value === value, color, fg, onPress: () => handleSelect(option) }, option.value)) });
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// src/ProfileChip.tsx
|
|
390
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
391
|
+
function ProfileChip({ profile, size = 22, filled = true }) {
|
|
392
|
+
const color = useProfileDisplayColor(profile.color);
|
|
393
|
+
return /* @__PURE__ */ jsx6(IdentityChip, { color, tag: profile.tag, icon: "account", size, filled });
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/ProfilePicker.tsx
|
|
397
|
+
import { getContrastColor as getContrastColor3 } from "@rific/auto-paper";
|
|
379
398
|
import { TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
|
|
399
|
+
import { useMemo as useMemo2 } from "react";
|
|
400
|
+
import { StyleSheet as StyleSheet4, View as View4 } from "react-native";
|
|
401
|
+
import { Icon as Icon3, Text as Text3 } from "react-native-paper";
|
|
402
|
+
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
403
|
+
function getProfilePickerContentSize(profileCount, hasManageRow = false, colorSection) {
|
|
404
|
+
return pickerContentSize(profileCount + 1 + (hasManageRow ? 1 : 0), colorSection);
|
|
405
|
+
}
|
|
406
|
+
function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, takenIds, color, dark, align, alignOverride, rotation = 0, guestLabel, nullLabel = "Player", nullIcon = "account-off-outline", onSelect, onManage, colorSection, triggerStyle }) {
|
|
407
|
+
const id = `${idPrefix}-profile`;
|
|
408
|
+
const colorPreview = useProfileColorPreview();
|
|
409
|
+
const fg = dark ? "#FFFFFF" : "#000000";
|
|
410
|
+
const selectedProfile = profiles.find((p) => p.id === selectedId) ?? null;
|
|
411
|
+
const sortedProfiles = useMemo2(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
412
|
+
const contentSize = getProfilePickerContentSize(profiles.length, !!onManage, colorSection);
|
|
413
|
+
const isTaken = (profileId) => profileId === takenId || profileId !== selectedId && !!takenIds?.includes(profileId);
|
|
414
|
+
const handleSelectGuest = () => {
|
|
415
|
+
onSelect(null);
|
|
416
|
+
host.close();
|
|
417
|
+
};
|
|
418
|
+
const handleSelectExisting = (profile) => {
|
|
419
|
+
if (isTaken(profile.id)) return;
|
|
420
|
+
onSelect(profile);
|
|
421
|
+
host.close();
|
|
422
|
+
};
|
|
423
|
+
const handleManage = () => {
|
|
424
|
+
host.close();
|
|
425
|
+
onManage?.();
|
|
426
|
+
};
|
|
427
|
+
const manageFooter = onManage ? /* @__PURE__ */ jsx7(TouchableRipple2, { onPress: handleManage, style: pickerMenuStyles.row, children: /* @__PURE__ */ jsxs3(View4, { style: styles4.manageRowInner, children: [
|
|
428
|
+
/* @__PURE__ */ jsx7(Icon3, { source: "account-cog-outline", size: 16, color }),
|
|
429
|
+
/* @__PURE__ */ jsx7(Text3, { style: [styles4.manageLabel, { color: fg }], children: "Manage" })
|
|
430
|
+
] }) }) : void 0;
|
|
431
|
+
return /* @__PURE__ */ jsxs3(PickerMenu, { id, host, color, dark, label: selectedProfile?.name ?? guestLabel, chip: colorSection ? { tag: selectedProfile?.tag, icon: selectedProfile ? "account" : nullIcon } : void 0, colorSection, homeColor: selectedProfile?.color, contentSize, align, alignOverride, rotation, triggerStyle, footer: manageFooter, children: [
|
|
432
|
+
/* @__PURE__ */ jsx7(IconRow, { icon: nullIcon, label: nullLabel, selected: selectedId === null, color, fg, onPress: handleSelectGuest }),
|
|
433
|
+
sortedProfiles.map((profile) => {
|
|
434
|
+
const taken = isTaken(profile.id);
|
|
435
|
+
const selected = selectedId === profile.id;
|
|
436
|
+
const rowColor = colorPreview ? colorPreview(profile.color) : profile.color;
|
|
437
|
+
return /* @__PURE__ */ jsx7(TouchableRipple2, { disabled: taken, onPress: () => handleSelectExisting(profile), style: [pickerMenuStyles.row, selected && { backgroundColor: rowColor }], children: /* @__PURE__ */ jsxs3(View4, { style: [pickerMenuStyles.rowInner, taken && pickerMenuStyles.rowDisabled], children: [
|
|
438
|
+
/* @__PURE__ */ jsx7(ProfileChip, { profile, filled: !selected }),
|
|
439
|
+
/* @__PURE__ */ jsx7(
|
|
440
|
+
Text3,
|
|
441
|
+
{
|
|
442
|
+
style: [
|
|
443
|
+
pickerMenuStyles.rowLabel,
|
|
444
|
+
{
|
|
445
|
+
color: selected ? getContrastColor3(rowColor) : fg
|
|
446
|
+
}
|
|
447
|
+
],
|
|
448
|
+
numberOfLines: 1,
|
|
449
|
+
children: profile.name
|
|
450
|
+
}
|
|
451
|
+
)
|
|
452
|
+
] }) }, profile.id);
|
|
453
|
+
})
|
|
454
|
+
] });
|
|
455
|
+
}
|
|
456
|
+
var styles4 = StyleSheet4.create({
|
|
457
|
+
manageLabel: {
|
|
458
|
+
fontSize: 13,
|
|
459
|
+
fontWeight: "700",
|
|
460
|
+
letterSpacing: 0.5
|
|
461
|
+
},
|
|
462
|
+
// Centered rather than left-aligned like a normal row - see the footer's own comment for why (a
|
|
463
|
+
// footer action, not another selectable identity).
|
|
464
|
+
manageRowInner: {
|
|
465
|
+
alignItems: "center",
|
|
466
|
+
flexDirection: "row",
|
|
467
|
+
gap: 6,
|
|
468
|
+
height: "100%",
|
|
469
|
+
justifyContent: "center"
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// src/ProfilesManager.tsx
|
|
474
|
+
import { defaultColors as defaultColors2, FadingScrollView as FadingScrollView2, useAutoPaperTheme } from "@rific/auto-paper";
|
|
475
|
+
import { TouchableRipple as TouchableRipple3 } from "@rific/feedback-press";
|
|
380
476
|
import { useFocusChain } from "@rific/focus-chain";
|
|
381
477
|
import { useToast } from "@rific/toaster";
|
|
382
|
-
import { ConfirmDialog, getInlineColorPickerContentSize, InlineColorPicker, usePopoverHost } from "@tastic/hud";
|
|
383
|
-
import { useCallback, useEffect, useImperativeHandle, useMemo as
|
|
384
|
-
import { KeyboardAvoidingView, Platform,
|
|
385
|
-
import { Icon as
|
|
478
|
+
import { ConfirmDialog, getInlineColorPickerContentSize, InlineColorPicker, usePopoverHost } from "@tastic/hud/chrome";
|
|
479
|
+
import { useCallback, useEffect, useImperativeHandle, useMemo as useMemo3, useRef, useState as useState2 } from "react";
|
|
480
|
+
import { KeyboardAvoidingView, Platform, StyleSheet as StyleSheet5, useWindowDimensions, View as View5 } from "react-native";
|
|
481
|
+
import { Icon as Icon4, Text as Text4, TextInput } from "react-native-paper";
|
|
386
482
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
387
483
|
|
|
388
484
|
// src/profilesValidation.ts
|
|
@@ -418,7 +514,7 @@ function isValidProfile(value) {
|
|
|
418
514
|
}
|
|
419
515
|
|
|
420
516
|
// src/ProfilesManager.tsx
|
|
421
|
-
import { Fragment as Fragment2, jsx as
|
|
517
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
422
518
|
var CHIP_SIZE2 = 40;
|
|
423
519
|
function deriveTag(name) {
|
|
424
520
|
const trimmed = name.trim();
|
|
@@ -493,40 +589,40 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
493
589
|
}
|
|
494
590
|
setConfirmDeleteId(null);
|
|
495
591
|
}, [confirmDeleteId, editingId, onDelete]);
|
|
496
|
-
const sortedProfiles =
|
|
592
|
+
const sortedProfiles = useMemo3(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
497
593
|
const rows = [...sortedProfiles.map((profile) => ({ kind: "profile", profile })), { kind: "new" }];
|
|
498
|
-
return /* @__PURE__ */
|
|
499
|
-
/* @__PURE__ */
|
|
594
|
+
return /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
595
|
+
/* @__PURE__ */ jsxs4(View5, { style: [styles5.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
|
|
500
596
|
headerLeft,
|
|
501
|
-
/* @__PURE__ */
|
|
597
|
+
/* @__PURE__ */ jsx8(Text4, { variant: titleVariant, style: [styles5.title, { color: fg }], children: "Profiles" })
|
|
502
598
|
] }),
|
|
503
|
-
/* @__PURE__ */
|
|
599
|
+
/* @__PURE__ */ jsx8(KeyboardAvoidingView, { behavior: Platform.OS === "ios" ? "padding" : void 0, style: styles5.keyboardAvoiding, children: /* @__PURE__ */ jsx8(FadingScrollView2, { style: styles5.scrollView, contentContainerStyle: [styles5.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, keyboardShouldPersistTaps: "handled", children: rows.map((row) => {
|
|
504
600
|
if (row.kind === "new") {
|
|
505
601
|
if (editingId !== "new") {
|
|
506
|
-
return /* @__PURE__ */
|
|
507
|
-
/* @__PURE__ */
|
|
508
|
-
/* @__PURE__ */
|
|
602
|
+
return /* @__PURE__ */ jsx8(TouchableRipple3, { onPress: () => startEdit("new"), style: styles5.row, children: /* @__PURE__ */ jsxs4(View5, { style: styles5.rowInner, children: [
|
|
603
|
+
/* @__PURE__ */ jsx8(View5, { style: [styles5.rowChip, styles5.newChip, { borderColor: fg }], children: /* @__PURE__ */ jsx8(Icon4, { source: "plus", size: 20, color: fg }) }),
|
|
604
|
+
/* @__PURE__ */ jsx8(Text4, { variant: "bodyLarge", style: { color: fg }, children: "New Profile" })
|
|
509
605
|
] }) }, "new");
|
|
510
606
|
}
|
|
511
|
-
return /* @__PURE__ */
|
|
607
|
+
return /* @__PURE__ */ jsx8(EditRow, { draftName, onNameChange: handleNameChange, draftColor, onColorChange: setDraftColor, draftTag, onTagChange: handleTagChange, onSubmit: commitEdit, autoFocus: true, fgMuted, host, dark, colorPreview, useColorPickerAlign }, "new");
|
|
512
608
|
}
|
|
513
609
|
const { profile } = row;
|
|
514
610
|
if (editingId === profile.id) {
|
|
515
|
-
return /* @__PURE__ */
|
|
611
|
+
return /* @__PURE__ */ jsx8(EditRow, { draftName, onNameChange: handleNameChange, draftColor, onColorChange: setDraftColor, draftTag, onTagChange: handleTagChange, onSubmit: commitEdit, onDelete: () => setConfirmDeleteId(profile.id), fgMuted, host, dark, colorPreview, useColorPickerAlign }, profile.id);
|
|
516
612
|
}
|
|
517
|
-
return /* @__PURE__ */
|
|
518
|
-
/* @__PURE__ */
|
|
519
|
-
/* @__PURE__ */
|
|
613
|
+
return /* @__PURE__ */ jsx8(TouchableRipple3, { onPress: () => startEdit(profile), style: styles5.row, children: /* @__PURE__ */ jsxs4(View5, { style: styles5.rowInner, children: [
|
|
614
|
+
/* @__PURE__ */ jsx8(ProfileChip, { profile, size: CHIP_SIZE2 }),
|
|
615
|
+
/* @__PURE__ */ jsx8(Text4, { variant: "bodyLarge", style: [styles5.rowName, { color: fg }], numberOfLines: 1, children: profile.name })
|
|
520
616
|
] }) }, profile.id);
|
|
521
617
|
}) }) }),
|
|
522
|
-
/* @__PURE__ */
|
|
618
|
+
/* @__PURE__ */ jsx8(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) })
|
|
523
619
|
] });
|
|
524
620
|
}
|
|
525
621
|
function AlignedColorPicker({ useAlign, ...props }) {
|
|
526
622
|
const { width: windowWidth } = useWindowDimensions();
|
|
527
623
|
const { width, height } = getInlineColorPickerContentSize(defaultColors2.length, windowWidth);
|
|
528
624
|
const alignOverride = useAlign(props.host.openId === props.id, width, height);
|
|
529
|
-
return /* @__PURE__ */
|
|
625
|
+
return /* @__PURE__ */ jsx8(InlineColorPicker, { ...props, alignOverride });
|
|
530
626
|
}
|
|
531
627
|
function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag, onTagChange, onSubmit, onDelete, autoFocus, fgMuted, host, dark, colorPreview, useColorPickerAlign }) {
|
|
532
628
|
const register = useFocusChain();
|
|
@@ -539,14 +635,14 @@ function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag,
|
|
|
539
635
|
useEffect(() => {
|
|
540
636
|
if (host.openId === "color") tagFocusRef.current();
|
|
541
637
|
}, [host.openId]);
|
|
542
|
-
return /* @__PURE__ */
|
|
543
|
-
useColorPickerAlign ? /* @__PURE__ */
|
|
544
|
-
/* @__PURE__ */
|
|
545
|
-
/* @__PURE__ */
|
|
546
|
-
onDelete && /* @__PURE__ */
|
|
638
|
+
return /* @__PURE__ */ jsxs4(View5, { style: styles5.editRow, children: [
|
|
639
|
+
useColorPickerAlign ? /* @__PURE__ */ jsx8(AlignedColorPicker, { useAlign: useColorPickerAlign, id: "color", host, value: draftColor, onChange: onColorChange, previewValue: colorPreview, tag: draftTag, dark }) : /* @__PURE__ */ jsx8(InlineColorPicker, { id: "color", host, value: draftColor, onChange: onColorChange, previewValue: colorPreview, tag: draftTag, dark }),
|
|
640
|
+
/* @__PURE__ */ jsx8(TextInput, { ref: tag.ref, ...tag.props, value: draftTag, onChangeText: onTagChange, accessibilityLabel: "Tag", autoCapitalize: "characters", selectTextOnFocus: true, returnKeyType: "next", style: styles5.hiddenTagInput, pointerEvents: "none" }),
|
|
641
|
+
/* @__PURE__ */ jsx8(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: styles5.nameInput }),
|
|
642
|
+
onDelete && /* @__PURE__ */ jsx8(TouchableRipple3, { onPress: onDelete, style: styles5.iconButton, children: /* @__PURE__ */ jsx8(Icon4, { source: "trash-can-outline", size: 18, color: fgMuted }) })
|
|
547
643
|
] });
|
|
548
644
|
}
|
|
549
|
-
var
|
|
645
|
+
var styles5 = StyleSheet5.create({
|
|
550
646
|
content: {
|
|
551
647
|
gap: 4,
|
|
552
648
|
paddingHorizontal: 20
|
|
@@ -628,8 +724,8 @@ var styles3 = StyleSheet3.create({
|
|
|
628
724
|
import { useAutoPaperTheme as useAutoPaperTheme2 } from "@rific/auto-paper";
|
|
629
725
|
import { IconButton } from "@rific/feedback-press";
|
|
630
726
|
import { useRef as useRef2 } from "react";
|
|
631
|
-
import { StyleSheet as
|
|
632
|
-
import { jsx as
|
|
727
|
+
import { StyleSheet as StyleSheet6, View as View6 } from "react-native";
|
|
728
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
633
729
|
function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }) {
|
|
634
730
|
const { dark } = useAutoPaperTheme2();
|
|
635
731
|
const managerRef = useRef2(null);
|
|
@@ -639,9 +735,9 @@ function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, on
|
|
|
639
735
|
managerRef.current?.commitPendingEdit();
|
|
640
736
|
onBack();
|
|
641
737
|
};
|
|
642
|
-
return /* @__PURE__ */
|
|
738
|
+
return /* @__PURE__ */ jsx9(View6, { style: [styles6.container, { backgroundColor: bg }], children: /* @__PURE__ */ jsx9(ProfilesManager, { ref: managerRef, profiles, defaultColor, onCreate, onSave, onDelete, fg, fgMuted, titleVariant, colorPreview, useColorPickerAlign, headerLeft: /* @__PURE__ */ jsx9(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: handleBack, accessibilityLabel: "Back" }) }) });
|
|
643
739
|
}
|
|
644
|
-
var
|
|
740
|
+
var styles6 = StyleSheet6.create({
|
|
645
741
|
container: {
|
|
646
742
|
flex: 1
|
|
647
743
|
}
|
|
@@ -752,6 +848,7 @@ function createProfileRecord(input) {
|
|
|
752
848
|
}
|
|
753
849
|
|
|
754
850
|
// src/sharedProfileStore.ts
|
|
851
|
+
import { Platform as Platform2 } from "react-native";
|
|
755
852
|
var native = null;
|
|
756
853
|
try {
|
|
757
854
|
const { requireOptionalNativeModule } = __require("expo-modules-core");
|
|
@@ -760,15 +857,42 @@ try {
|
|
|
760
857
|
native = null;
|
|
761
858
|
}
|
|
762
859
|
var PROFILES_KEY = "sharedProfiles";
|
|
763
|
-
var
|
|
764
|
-
|
|
765
|
-
if (
|
|
766
|
-
let json = null;
|
|
860
|
+
var webKey = (groupId, key) => `${groupId}:${key}`;
|
|
861
|
+
var createWebStore = () => {
|
|
862
|
+
if (Platform2.OS !== "web") return null;
|
|
767
863
|
try {
|
|
768
|
-
|
|
864
|
+
const storage = globalThis.localStorage;
|
|
865
|
+
if (!storage) return null;
|
|
866
|
+
const probe = webKey("@tastic/profile", "probe");
|
|
867
|
+
storage.setItem(probe, "1");
|
|
868
|
+
storage.removeItem(probe);
|
|
869
|
+
let lastWriteFailed = false;
|
|
870
|
+
return {
|
|
871
|
+
storage,
|
|
872
|
+
store: {
|
|
873
|
+
getSharedJSON: async (groupId, key) => {
|
|
874
|
+
if (lastWriteFailed) throw new Error("The last write to the shared roster failed");
|
|
875
|
+
return storage.getItem(webKey(groupId, key));
|
|
876
|
+
},
|
|
877
|
+
setSharedJSON: async (groupId, key, value) => {
|
|
878
|
+
try {
|
|
879
|
+
storage.setItem(webKey(groupId, key), value);
|
|
880
|
+
lastWriteFailed = false;
|
|
881
|
+
} catch (error) {
|
|
882
|
+
lastWriteFailed = true;
|
|
883
|
+
throw error;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
};
|
|
769
888
|
} catch {
|
|
770
889
|
return null;
|
|
771
890
|
}
|
|
891
|
+
};
|
|
892
|
+
var web = native ? null : createWebStore();
|
|
893
|
+
if (web) native = web.store;
|
|
894
|
+
var isSharedProfileStoreAvailable = native !== null;
|
|
895
|
+
var parseRoster = (json) => {
|
|
772
896
|
if (!json) return [];
|
|
773
897
|
try {
|
|
774
898
|
const parsed = JSON.parse(json);
|
|
@@ -778,6 +902,16 @@ async function readSharedProfiles(groupId) {
|
|
|
778
902
|
} catch {
|
|
779
903
|
return null;
|
|
780
904
|
}
|
|
905
|
+
};
|
|
906
|
+
async function readSharedProfiles(groupId) {
|
|
907
|
+
if (!native) return [];
|
|
908
|
+
let json = null;
|
|
909
|
+
try {
|
|
910
|
+
json = await native.getSharedJSON(groupId, PROFILES_KEY);
|
|
911
|
+
} catch {
|
|
912
|
+
return null;
|
|
913
|
+
}
|
|
914
|
+
return parseRoster(json);
|
|
781
915
|
}
|
|
782
916
|
async function loadSharedProfiles(groupId) {
|
|
783
917
|
return await readSharedProfiles(groupId) ?? [];
|
|
@@ -789,6 +923,19 @@ async function saveSharedProfiles(groupId, profiles) {
|
|
|
789
923
|
} catch {
|
|
790
924
|
}
|
|
791
925
|
}
|
|
926
|
+
function subscribeSharedProfiles(groupId, listener) {
|
|
927
|
+
if (!web || typeof window === "undefined" || typeof window.addEventListener !== "function") return () => {
|
|
928
|
+
};
|
|
929
|
+
const storageKey = webKey(groupId, PROFILES_KEY);
|
|
930
|
+
const storage = web.storage;
|
|
931
|
+
const onStorage = (event) => {
|
|
932
|
+
if (event.key !== storageKey || event.storageArea !== storage || !event.newValue) return;
|
|
933
|
+
const profiles = parseRoster(event.newValue);
|
|
934
|
+
if (profiles !== null) listener(profiles);
|
|
935
|
+
};
|
|
936
|
+
window.addEventListener("storage", onStorage);
|
|
937
|
+
return () => window.removeEventListener("storage", onStorage);
|
|
938
|
+
}
|
|
792
939
|
|
|
793
940
|
// src/redux/resolveInitialProfiles.ts
|
|
794
941
|
async function resolveInitialProfiles(groupId, localFallback) {
|
|
@@ -805,21 +952,52 @@ async function resolveInitialProfiles(groupId, localFallback) {
|
|
|
805
952
|
|
|
806
953
|
// src/redux/seatColorsSlice.ts
|
|
807
954
|
var REHYDRATE2 = "persist/REHYDRATE";
|
|
955
|
+
function sameColor(a, b) {
|
|
956
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
957
|
+
}
|
|
958
|
+
function profileSeatColor(profile, profileColors) {
|
|
959
|
+
const pick = profileColors[profile.id];
|
|
960
|
+
return pick && sameColor(pick.profileColor, profile.color) ? pick.color : profile.color;
|
|
961
|
+
}
|
|
808
962
|
function createSeatColorsSlice(namespace, defaultState, mountKey = "seatColors") {
|
|
809
963
|
const setLastCpuColor = createActionCreator(`seatColors/${namespace}/setLastCpuColor`);
|
|
810
964
|
const setLastGuestColor = createActionCreator(`seatColors/${namespace}/setLastGuestColor`);
|
|
811
|
-
const
|
|
965
|
+
const setProfileColor = createActionCreator(`seatColors/${namespace}/setProfileColor`);
|
|
966
|
+
function withoutPick(state, id) {
|
|
967
|
+
if (!state.profileColors[id]) return state;
|
|
968
|
+
const profileColors = { ...state.profileColors };
|
|
969
|
+
delete profileColors[id];
|
|
970
|
+
return { ...state, profileColors };
|
|
971
|
+
}
|
|
812
972
|
function reducer(state = defaultState, action) {
|
|
813
973
|
if (action.type === REHYDRATE2) {
|
|
814
974
|
const persisted = action.payload?.[mountKey];
|
|
815
|
-
|
|
975
|
+
const next = { ...defaultState, ...state, ...persisted };
|
|
976
|
+
delete next.profileOverride;
|
|
977
|
+
return next;
|
|
816
978
|
}
|
|
817
979
|
if (setLastCpuColor.match(action)) return { ...state, lastCpuColor: action.payload };
|
|
818
980
|
if (setLastGuestColor.match(action)) return { ...state, lastGuestColor: { ...state.lastGuestColor, [action.payload.seat]: action.payload.color } };
|
|
819
|
-
if (
|
|
981
|
+
if (setProfileColor.match(action)) {
|
|
982
|
+
const { profile, color } = action.payload;
|
|
983
|
+
if (color === null || sameColor(color, profile.color)) return withoutPick(state, profile.id);
|
|
984
|
+
return { ...state, profileColors: { ...state.profileColors, [profile.id]: { color, profileColor: profile.color } } };
|
|
985
|
+
}
|
|
986
|
+
if (profilesActions.remove.match(action)) return withoutPick(state, action.payload);
|
|
987
|
+
if (profilesActions.update.match(action)) {
|
|
988
|
+
const { id, patch } = action.payload;
|
|
989
|
+
const pick = state.profileColors[id];
|
|
990
|
+
return pick && patch.color !== void 0 && !sameColor(patch.color, pick.profileColor) ? withoutPick(state, id) : state;
|
|
991
|
+
}
|
|
992
|
+
if (profilesActions.setAll.match(action)) {
|
|
993
|
+
return action.payload.reduce((next, profile) => {
|
|
994
|
+
const pick = next.profileColors[profile.id];
|
|
995
|
+
return pick && !sameColor(profile.color, pick.profileColor) ? withoutPick(next, profile.id) : next;
|
|
996
|
+
}, state);
|
|
997
|
+
}
|
|
820
998
|
return state;
|
|
821
999
|
}
|
|
822
|
-
return { actions: { setLastCpuColor, setLastGuestColor,
|
|
1000
|
+
return { actions: { setLastCpuColor, setLastGuestColor, setProfileColor }, reducer };
|
|
823
1001
|
}
|
|
824
1002
|
|
|
825
1003
|
// src/redux/useSharedProfilesSync.ts
|
|
@@ -840,9 +1018,13 @@ function useSharedProfilesSync({ groupId, onRemoteChange }) {
|
|
|
840
1018
|
if (!cancelled && shared !== null) onRemoteChangeRef.current(shared);
|
|
841
1019
|
});
|
|
842
1020
|
});
|
|
1021
|
+
const unsubscribe = subscribeSharedProfiles(groupId, (shared) => {
|
|
1022
|
+
if (!cancelled && !pendingWriteRef.current) onRemoteChangeRef.current(shared);
|
|
1023
|
+
});
|
|
843
1024
|
return () => {
|
|
844
1025
|
cancelled = true;
|
|
845
1026
|
sub.remove();
|
|
1027
|
+
unsubscribe();
|
|
846
1028
|
};
|
|
847
1029
|
}, [groupId]);
|
|
848
1030
|
const syncToShared = useCallback2(
|
|
@@ -868,6 +1050,7 @@ function avoidSeatColorClash(p1Color, p2Color, defaults) {
|
|
|
868
1050
|
export {
|
|
869
1051
|
MAX_PROFILE_NAME_LENGTH,
|
|
870
1052
|
MAX_TAG_LENGTH,
|
|
1053
|
+
OptionPicker,
|
|
871
1054
|
ProfileChip,
|
|
872
1055
|
ProfileColorProvider,
|
|
873
1056
|
ProfilePicker,
|
|
@@ -878,17 +1061,21 @@ export {
|
|
|
878
1061
|
createProfileRecord,
|
|
879
1062
|
createProfileSelectionSlice,
|
|
880
1063
|
createSeatColorsSlice,
|
|
1064
|
+
getOptionPickerContentSize,
|
|
881
1065
|
getProfilePickerContentSize,
|
|
882
1066
|
isSharedProfileStoreAvailable,
|
|
883
1067
|
isValidProfile,
|
|
884
1068
|
isValidTag,
|
|
885
1069
|
loadSharedProfiles,
|
|
1070
|
+
profileSeatColor,
|
|
886
1071
|
profilesActions,
|
|
887
1072
|
profilesReducer,
|
|
888
1073
|
readSharedProfiles,
|
|
889
1074
|
resolveInitialProfiles,
|
|
890
1075
|
saveSharedProfiles,
|
|
1076
|
+
subscribeSharedProfiles,
|
|
891
1077
|
useProfileColorPreview,
|
|
892
1078
|
useProfileDisplayColor,
|
|
1079
|
+
useProfileSwatchRenderer,
|
|
893
1080
|
useSharedProfilesSync
|
|
894
1081
|
};
|