@tastic/profile 0.13.0 → 0.14.1
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 +10 -3
- package/dist/index.d.mts +15 -4
- package/dist/index.d.ts +15 -4
- package/dist/index.js +236 -90
- package/dist/index.mjs +246 -100
- package/package.json +2 -2
- package/src/IdentityChip.tsx +58 -0
- package/src/ProfileChip.tsx +2 -37
- package/src/ProfilePicker.tsx +315 -71
- package/src/ProfilesManager.tsx +7 -1
- package/src/index.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -48,17 +48,34 @@ __export(index_exports, {
|
|
|
48
48
|
});
|
|
49
49
|
module.exports = __toCommonJS(index_exports);
|
|
50
50
|
|
|
51
|
-
// src/
|
|
51
|
+
// src/IdentityChip.tsx
|
|
52
52
|
var import_auto_paper = require("@rific/auto-paper");
|
|
53
53
|
var import_react_native = require("react-native");
|
|
54
54
|
var import_react_native_paper = require("react-native-paper");
|
|
55
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
56
|
+
var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
|
|
57
|
+
function IdentityChip({ color, tag, icon, size, filled = true }) {
|
|
58
|
+
const contrastColor = (0, import_auto_paper.getContrastColor)(color);
|
|
59
|
+
const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : tag && Array.from(tag).length >= 3 ? 0.32 : 0.4);
|
|
60
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: [styles.chip, { borderRadius: size / 2, height: size, width: size }, filled && { backgroundColor: color }], children: tag ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Text, { style: [styles.tag, { color: contrastColor, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Icon, { source: icon, size: size * 0.6, color: contrastColor }) });
|
|
61
|
+
}
|
|
62
|
+
var styles = import_react_native.StyleSheet.create({
|
|
63
|
+
chip: {
|
|
64
|
+
alignItems: "center",
|
|
65
|
+
justifyContent: "center"
|
|
66
|
+
},
|
|
67
|
+
tag: {
|
|
68
|
+
fontWeight: "700",
|
|
69
|
+
paddingHorizontal: 2
|
|
70
|
+
}
|
|
71
|
+
});
|
|
55
72
|
|
|
56
73
|
// src/ProfileColorContext.tsx
|
|
57
74
|
var import_react = require("react");
|
|
58
|
-
var
|
|
75
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
59
76
|
var ProfileColorContext = (0, import_react.createContext)(void 0);
|
|
60
77
|
function ProfileColorProvider({ preview, children }) {
|
|
61
|
-
return /* @__PURE__ */ (0,
|
|
78
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ProfileColorContext.Provider, { value: preview, children });
|
|
62
79
|
}
|
|
63
80
|
function useProfileColorPreview() {
|
|
64
81
|
return (0, import_react.useContext)(ProfileColorContext);
|
|
@@ -69,24 +86,11 @@ function useProfileDisplayColor(hex) {
|
|
|
69
86
|
}
|
|
70
87
|
|
|
71
88
|
// src/ProfileChip.tsx
|
|
72
|
-
var
|
|
73
|
-
var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
|
|
89
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
74
90
|
function ProfileChip({ profile, size = 22, filled = true }) {
|
|
75
91
|
const color = useProfileDisplayColor(profile.color);
|
|
76
|
-
|
|
77
|
-
const tagFontSize = size * (profile.tag && EMOJI_PATTERN.test(profile.tag) ? 0.6 : 0.4);
|
|
78
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native.View, { style: [styles.chip, { borderRadius: size / 2, height: size, width: size }, filled && { backgroundColor: color }], children: profile.tag ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper.Text, { style: [styles.tag, { color: contrastColor, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: profile.tag }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper.Icon, { source: "account", size: size * 0.6, color: contrastColor }) });
|
|
92
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IdentityChip, { color, tag: profile.tag, icon: "account", size, filled });
|
|
79
93
|
}
|
|
80
|
-
var styles = import_react_native.StyleSheet.create({
|
|
81
|
-
chip: {
|
|
82
|
-
alignItems: "center",
|
|
83
|
-
justifyContent: "center"
|
|
84
|
-
},
|
|
85
|
-
tag: {
|
|
86
|
-
fontWeight: "700",
|
|
87
|
-
paddingHorizontal: 2
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
94
|
|
|
91
95
|
// src/ProfilePicker.tsx
|
|
92
96
|
var import_auto_paper2 = require("@rific/auto-paper");
|
|
@@ -95,16 +99,40 @@ var import_hud = require("@tastic/hud");
|
|
|
95
99
|
var import_react2 = require("react");
|
|
96
100
|
var import_react_native2 = require("react-native");
|
|
97
101
|
var import_react_native_paper2 = require("react-native-paper");
|
|
98
|
-
var
|
|
102
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
99
103
|
var TRIGGER_HEIGHT = 28;
|
|
100
104
|
var POPOVER_WIDTH = 220;
|
|
101
105
|
var ROW_HEIGHT = 36;
|
|
102
106
|
var LIST_PADDING = 8;
|
|
103
|
-
|
|
107
|
+
var MENU_BORDER_WIDTH = 2;
|
|
108
|
+
var ROW_INSET = 12;
|
|
109
|
+
var CHIP_SIZE = 32;
|
|
110
|
+
var CHIP_TRIGGER_PADDING = 6;
|
|
111
|
+
var SWATCH_SIZE = 28;
|
|
112
|
+
var SWATCH_MIN_GAP = 6;
|
|
113
|
+
var COLOR_ROW_CAPTIONED_HEIGHT = 48;
|
|
114
|
+
var COLOR_LABEL_LINE = 18;
|
|
115
|
+
var COLOR_CAPTION_LINE = 16;
|
|
116
|
+
var COLOR_ROW_DIVIDER = 9;
|
|
117
|
+
var GRID_TOP = 4;
|
|
118
|
+
var GRID_BOTTOM = 8;
|
|
119
|
+
var GRID_WIDTH = POPOVER_WIDTH - 2 * MENU_BORDER_WIDTH - 2 * LIST_PADDING - 2 * ROW_INSET;
|
|
120
|
+
var GRID_COLUMNS = Math.floor((GRID_WIDTH + SWATCH_MIN_GAP) / (SWATCH_SIZE + SWATCH_MIN_GAP));
|
|
121
|
+
var GRID_GAP = (GRID_WIDTH - GRID_COLUMNS * SWATCH_SIZE) / (GRID_COLUMNS - 1);
|
|
122
|
+
function colorRowHeight(caption) {
|
|
123
|
+
return caption ? COLOR_ROW_CAPTIONED_HEIGHT : ROW_HEIGHT;
|
|
124
|
+
}
|
|
125
|
+
function colorPageHeight(swatches) {
|
|
126
|
+
const rows = Math.ceil(swatches.length / GRID_COLUMNS);
|
|
127
|
+
return ROW_HEIGHT + GRID_TOP + rows * SWATCH_SIZE + (rows - 1) * GRID_GAP + GRID_BOTTOM;
|
|
128
|
+
}
|
|
129
|
+
function getProfilePickerContentSize(profileCount, hasManageRow = false, colorSection) {
|
|
104
130
|
const rowCount = profileCount + 1 + (hasManageRow ? 1 : 0);
|
|
105
|
-
|
|
131
|
+
const list = rowCount * ROW_HEIGHT + (colorSection ? colorRowHeight(colorSection.caption) + COLOR_ROW_DIVIDER : 0);
|
|
132
|
+
const content = colorSection ? Math.max(list, colorPageHeight(colorSection.swatches ?? import_auto_paper2.defaultColors)) : list;
|
|
133
|
+
return { width: POPOVER_WIDTH, height: LIST_PADDING * 2 + content };
|
|
106
134
|
}
|
|
107
|
-
function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, color, dark, align: forcedAlign, alignOverride, rotation = 0, guestLabel, nullLabel = "Player", nullIcon = "account-off-outline", onSelect, onManage }) {
|
|
135
|
+
function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, color, dark, align: forcedAlign, alignOverride, rotation = 0, guestLabel, nullLabel = "Player", nullIcon = "account-off-outline", onSelect, onManage, colorSection, triggerStyle }) {
|
|
108
136
|
const id = `${idPrefix}-profile`;
|
|
109
137
|
const colorPreview = useProfileColorPreview();
|
|
110
138
|
const menuBg = dark ? "#000000" : "#FFFFFF";
|
|
@@ -112,7 +140,7 @@ function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, color, d
|
|
|
112
140
|
const open = host.openId === id;
|
|
113
141
|
const selectedProfile = profiles.find((p) => p.id === selectedId) ?? null;
|
|
114
142
|
const sortedProfiles = (0, import_react2.useMemo)(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
115
|
-
const { width: contentWidth, height: contentHeight } = getProfilePickerContentSize(profiles.length, !!onManage);
|
|
143
|
+
const { width: contentWidth, height: contentHeight } = getProfilePickerContentSize(profiles.length, !!onManage, colorSection);
|
|
116
144
|
const auto = (0, import_hud.useAutoAlign)(open, contentWidth, contentHeight, rotation);
|
|
117
145
|
const placement = alignOverride ?? auto;
|
|
118
146
|
const { maxHeight, measured, triggerRef, verticalAlign } = placement;
|
|
@@ -130,13 +158,24 @@ function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, color, d
|
|
|
130
158
|
host.close();
|
|
131
159
|
onManage?.();
|
|
132
160
|
};
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
161
|
+
const chipTrigger = !!colorSection;
|
|
162
|
+
const [triggerWidth, setTriggerWidth] = (0, import_react2.useState)(0);
|
|
163
|
+
const handleAnchorLayout = chipTrigger ? (e) => setTriggerWidth(e.nativeEvent.layout.width) : void 0;
|
|
164
|
+
const [colorPage, setColorPage] = (0, import_react2.useState)(false);
|
|
165
|
+
const handleTriggerPress = () => {
|
|
166
|
+
setColorPage(false);
|
|
167
|
+
host.toggle(id);
|
|
168
|
+
};
|
|
169
|
+
const triggerLabel = (selectedProfile?.name ?? guestLabel).toUpperCase();
|
|
170
|
+
const manageFooter = !!onManage && !(colorSection && colorPage);
|
|
171
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { style: [styles2.anchor, open && styles2.anchorOpen], onLayout: handleAnchorLayout, children: [
|
|
172
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress: handleTriggerPress, borderless: chipTrigger, style: [styles2.trigger, chipTrigger && styles2.chipTrigger, triggerStyle], accessibilityRole: "button", accessibilityLabel: triggerLabel, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { ref: triggerRef, collapsable: false, style: chipTrigger ? styles2.chipTriggerInner : styles2.triggerInner, children: [
|
|
173
|
+
chipTrigger && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(IdentityChip, { color, tag: selectedProfile?.tag, icon: selectedProfile ? "account" : nullIcon, size: CHIP_SIZE }),
|
|
174
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles2.triggerLabel, { color: chipTrigger ? fg : color }], numberOfLines: 1, children: triggerLabel }),
|
|
175
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "menu-down", size: 16, color: chipTrigger ? fg : color })
|
|
137
176
|
] }) }),
|
|
138
|
-
/* @__PURE__ */ (0,
|
|
139
|
-
import_react_native2.
|
|
177
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_hud.PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: chipTrigger ? triggerWidth : TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
178
|
+
import_react_native2.View,
|
|
140
179
|
{
|
|
141
180
|
style: [
|
|
142
181
|
styles2.menu,
|
|
@@ -147,39 +186,40 @@ function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, color, d
|
|
|
147
186
|
maxHeight
|
|
148
187
|
}
|
|
149
188
|
],
|
|
150
|
-
contentContainerStyle: styles2.menuContent,
|
|
151
|
-
showsVerticalScrollIndicator: false,
|
|
152
189
|
children: [
|
|
153
|
-
/* @__PURE__ */ (0,
|
|
154
|
-
/* @__PURE__ */ (0,
|
|
155
|
-
/* @__PURE__ */ (0,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
/* @__PURE__ */ (0,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
190
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_auto_paper2.FadingScrollView, { contentContainerStyle: [styles2.menuContent, manageFooter && styles2.menuContentAboveFooter], showsVerticalScrollIndicator: false, children: colorSection && colorPage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ColorPage, { section: colorSection, profileColor: selectedProfile?.color, fg, onBack: () => setColorPage(false) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
191
|
+
colorSection && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ColorRow, { section: colorSection, fg, dividerColor: color, onPress: () => setColorPage(true) }),
|
|
192
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress: handleSelectGuest, style: [styles2.row, selectedId === null && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { style: styles2.rowInner, children: [
|
|
193
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native2.View, { style: styles2.guestChip, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: nullIcon, size: 13, color: selectedId === null ? (0, import_auto_paper2.getContrastColor)(color) : fg }) }),
|
|
194
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles2.rowLabel, { color: selectedId === null ? (0, import_auto_paper2.getContrastColor)(color) : fg }], children: nullLabel })
|
|
195
|
+
] }) }),
|
|
196
|
+
sortedProfiles.map((profile) => {
|
|
197
|
+
const taken = profile.id === takenId;
|
|
198
|
+
const selected = selectedId === profile.id;
|
|
199
|
+
const rowColor = colorPreview ? colorPreview(profile.color) : profile.color;
|
|
200
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { disabled: taken, onPress: () => handleSelectExisting(profile), style: [styles2.row, selected && { backgroundColor: rowColor }], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { style: [styles2.rowInner, taken && styles2.rowDisabled], children: [
|
|
201
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ProfileChip, { profile, filled: !selected }),
|
|
202
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
203
|
+
import_react_native_paper2.Text,
|
|
204
|
+
{
|
|
205
|
+
style: [
|
|
206
|
+
styles2.rowLabel,
|
|
207
|
+
{
|
|
208
|
+
color: selected ? (0, import_auto_paper2.getContrastColor)(rowColor) : fg
|
|
209
|
+
}
|
|
210
|
+
],
|
|
211
|
+
numberOfLines: 1,
|
|
212
|
+
children: profile.name
|
|
213
|
+
}
|
|
214
|
+
)
|
|
215
|
+
] }) }, profile.id);
|
|
216
|
+
})
|
|
217
|
+
] }) }, colorSection && colorPage ? "color" : "list"),
|
|
218
|
+
manageFooter && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { style: styles2.menuFooter, children: [
|
|
219
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native2.View, { style: [styles2.divider, { backgroundColor: color }] }),
|
|
220
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress: handleManage, style: styles2.row, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { style: styles2.manageRowInner, children: [
|
|
221
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "account-cog-outline", size: 16, color }),
|
|
222
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles2.manageLabel, { color: fg }], children: "Manage" })
|
|
183
223
|
] }) })
|
|
184
224
|
] })
|
|
185
225
|
]
|
|
@@ -187,6 +227,37 @@ function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, color, d
|
|
|
187
227
|
) })
|
|
188
228
|
] });
|
|
189
229
|
}
|
|
230
|
+
function ColorRow({ section, fg, dividerColor, onPress }) {
|
|
231
|
+
const { value, previewValue, label = "Color", caption } = section;
|
|
232
|
+
const drawn = previewValue ? previewValue(value) : value;
|
|
233
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
234
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress, style: [styles2.row, { height: colorRowHeight(caption) }], accessibilityRole: "button", accessibilityLabel: caption ? `${label}, ${caption}` : label, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { style: styles2.rowInner, children: [
|
|
235
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native2.View, { style: [styles2.colorRowSwatch, { backgroundColor: drawn }] }),
|
|
236
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { style: styles2.colorRowText, children: [
|
|
237
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles2.colorRowLabel, { color: fg }], numberOfLines: 1, children: label }),
|
|
238
|
+
caption ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles2.colorRowCaption, { color: fg }], numberOfLines: 1, children: caption }) : null
|
|
239
|
+
] }),
|
|
240
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "chevron-right", size: 18, color: fg })
|
|
241
|
+
] }) }),
|
|
242
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native2.View, { style: [styles2.divider, { backgroundColor: dividerColor }] })
|
|
243
|
+
] });
|
|
244
|
+
}
|
|
245
|
+
function ColorPage({ section, profileColor, fg, onBack }) {
|
|
246
|
+
const { value, onChange, previewValue, swatches = import_auto_paper2.defaultColors, label = "Color" } = section;
|
|
247
|
+
const displayFor = previewValue ?? ((hex) => hex);
|
|
248
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
249
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress: onBack, style: styles2.row, accessibilityRole: "button", accessibilityLabel: "Back", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { style: styles2.rowInner, children: [
|
|
250
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native2.View, { style: styles2.guestChip, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "chevron-left", size: 20, color: fg }) }),
|
|
251
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles2.colorRowLabel, { color: fg }], numberOfLines: 1, children: label })
|
|
252
|
+
] }) }),
|
|
253
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native2.View, { style: styles2.swatches, children: swatches.map((swatch) => {
|
|
254
|
+
const selected = swatch.value.toLowerCase() === value.toLowerCase();
|
|
255
|
+
const isProfileColor = !!profileColor && swatch.value.toLowerCase() === profileColor.toLowerCase();
|
|
256
|
+
const swatchDisplay = displayFor(swatch.value);
|
|
257
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress: () => onChange(swatch.value), borderless: true, style: [styles2.swatch, { backgroundColor: swatchDisplay }, selected && styles2.swatchSelected], accessibilityRole: "button", accessibilityLabel: isProfileColor ? `${swatch.label}, profile color` : swatch.label, accessibilityState: { selected }, children: selected ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "check", size: 16, color: (0, import_auto_paper2.getContrastColor)(swatchDisplay) }) : isProfileColor ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native2.View, { style: [styles2.profileDot, { backgroundColor: (0, import_auto_paper2.getContrastColor)(swatchDisplay) }] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native2.View, {}) }, swatch.value);
|
|
258
|
+
}) })
|
|
259
|
+
] });
|
|
260
|
+
}
|
|
190
261
|
var styles2 = import_react_native2.StyleSheet.create({
|
|
191
262
|
anchor: {
|
|
192
263
|
position: "relative"
|
|
@@ -196,6 +267,40 @@ var styles2 = import_react_native2.StyleSheet.create({
|
|
|
196
267
|
anchorOpen: {
|
|
197
268
|
zIndex: 100
|
|
198
269
|
},
|
|
270
|
+
// The pill, rounded at both ends. The host adds its own background through `triggerStyle`.
|
|
271
|
+
chipTrigger: {
|
|
272
|
+
borderRadius: (CHIP_SIZE + 2 * CHIP_TRIGGER_PADDING) / 2
|
|
273
|
+
},
|
|
274
|
+
// The padding lives on the measured view, not the pressable around it, so the placement measures
|
|
275
|
+
// the pill's real edge: measured from inside the padding, a popover opening toward a screen edge
|
|
276
|
+
// was sized as if the pill were 6pt further from it, and ran 2pt past that edge.
|
|
277
|
+
chipTriggerInner: {
|
|
278
|
+
alignItems: "center",
|
|
279
|
+
flexDirection: "row",
|
|
280
|
+
gap: 8,
|
|
281
|
+
height: CHIP_SIZE + 2 * CHIP_TRIGGER_PADDING,
|
|
282
|
+
paddingLeft: CHIP_TRIGGER_PADDING,
|
|
283
|
+
paddingRight: CHIP_TRIGGER_PADDING + 8
|
|
284
|
+
},
|
|
285
|
+
colorRowCaption: {
|
|
286
|
+
fontSize: 12,
|
|
287
|
+
lineHeight: COLOR_CAPTION_LINE,
|
|
288
|
+
opacity: 0.6
|
|
289
|
+
},
|
|
290
|
+
// A row's label, with a line height so the captioned Color row's height is exact.
|
|
291
|
+
colorRowLabel: {
|
|
292
|
+
fontSize: 14,
|
|
293
|
+
lineHeight: COLOR_LABEL_LINE
|
|
294
|
+
},
|
|
295
|
+
// The color in effect, in the same 22pt slot as every row's chip.
|
|
296
|
+
colorRowSwatch: {
|
|
297
|
+
borderRadius: 11,
|
|
298
|
+
height: 22,
|
|
299
|
+
width: 22
|
|
300
|
+
},
|
|
301
|
+
colorRowText: {
|
|
302
|
+
flex: 1
|
|
303
|
+
},
|
|
199
304
|
// Separates Manage (a navigation action) from the selectable rows above it - different kind of
|
|
200
305
|
// action, not just another row in the same list. Tinted with the seat's own color (passed in as
|
|
201
306
|
// backgroundColor) rather than a flat gray, matching the border/caret's own accent-tinted look.
|
|
@@ -230,7 +335,7 @@ var styles2 = import_react_native2.StyleSheet.create({
|
|
|
230
335
|
},
|
|
231
336
|
menu: {
|
|
232
337
|
borderRadius: 12,
|
|
233
|
-
borderWidth:
|
|
338
|
+
borderWidth: MENU_BORDER_WIDTH,
|
|
234
339
|
elevation: 8,
|
|
235
340
|
shadowColor: "#000000",
|
|
236
341
|
shadowOffset: { height: 2, width: 0 },
|
|
@@ -243,6 +348,20 @@ var styles2 = import_react_native2.StyleSheet.create({
|
|
|
243
348
|
menuContent: {
|
|
244
349
|
padding: LIST_PADDING
|
|
245
350
|
},
|
|
351
|
+
// With Manage pinned below, its own padding takes the bottom edge (same total height as before).
|
|
352
|
+
menuContentAboveFooter: {
|
|
353
|
+
paddingBottom: 0
|
|
354
|
+
},
|
|
355
|
+
menuFooter: {
|
|
356
|
+
paddingBottom: LIST_PADDING,
|
|
357
|
+
paddingHorizontal: LIST_PADDING
|
|
358
|
+
},
|
|
359
|
+
// The selected profile's own color (see ColorSection).
|
|
360
|
+
profileDot: {
|
|
361
|
+
borderRadius: 4,
|
|
362
|
+
height: 8,
|
|
363
|
+
width: 8
|
|
364
|
+
},
|
|
246
365
|
row: {
|
|
247
366
|
borderRadius: 8,
|
|
248
367
|
height: ROW_HEIGHT
|
|
@@ -255,12 +374,33 @@ var styles2 = import_react_native2.StyleSheet.create({
|
|
|
255
374
|
flexDirection: "row",
|
|
256
375
|
flex: 1,
|
|
257
376
|
gap: 8,
|
|
258
|
-
paddingHorizontal:
|
|
377
|
+
paddingHorizontal: ROW_INSET
|
|
259
378
|
},
|
|
260
379
|
rowLabel: {
|
|
261
380
|
flex: 1,
|
|
262
381
|
fontSize: 14
|
|
263
382
|
},
|
|
383
|
+
swatch: {
|
|
384
|
+
alignItems: "center",
|
|
385
|
+
borderRadius: SWATCH_SIZE / 2,
|
|
386
|
+
height: SWATCH_SIZE,
|
|
387
|
+
justifyContent: "center",
|
|
388
|
+
width: SWATCH_SIZE
|
|
389
|
+
},
|
|
390
|
+
swatchSelected: {
|
|
391
|
+
borderColor: "#ffffff",
|
|
392
|
+
borderWidth: 2
|
|
393
|
+
},
|
|
394
|
+
swatches: {
|
|
395
|
+
columnGap: GRID_GAP,
|
|
396
|
+
flexDirection: "row",
|
|
397
|
+
flexWrap: "wrap",
|
|
398
|
+
marginBottom: GRID_BOTTOM,
|
|
399
|
+
marginHorizontal: ROW_INSET,
|
|
400
|
+
marginTop: GRID_TOP,
|
|
401
|
+
rowGap: GRID_GAP,
|
|
402
|
+
width: GRID_WIDTH
|
|
403
|
+
},
|
|
264
404
|
trigger: {
|
|
265
405
|
alignSelf: "flex-start"
|
|
266
406
|
},
|
|
@@ -321,8 +461,8 @@ function isValidProfile(value) {
|
|
|
321
461
|
}
|
|
322
462
|
|
|
323
463
|
// src/ProfilesManager.tsx
|
|
324
|
-
var
|
|
325
|
-
var
|
|
464
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
465
|
+
var CHIP_SIZE2 = 40;
|
|
326
466
|
function deriveTag(name) {
|
|
327
467
|
const trimmed = name.trim();
|
|
328
468
|
return trimmed ? trimmed[0].toUpperCase() : "";
|
|
@@ -398,38 +538,38 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
398
538
|
}, [confirmDeleteId, editingId, onDelete]);
|
|
399
539
|
const sortedProfiles = (0, import_react3.useMemo)(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
400
540
|
const rows = [...sortedProfiles.map((profile) => ({ kind: "profile", profile })), { kind: "new" }];
|
|
401
|
-
return /* @__PURE__ */ (0,
|
|
402
|
-
/* @__PURE__ */ (0,
|
|
541
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
542
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native3.View, { style: [styles3.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
|
|
403
543
|
headerLeft,
|
|
404
|
-
/* @__PURE__ */ (0,
|
|
544
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_paper3.Text, { variant: titleVariant, style: [styles3.title, { color: fg }], children: "Profiles" })
|
|
405
545
|
] }),
|
|
406
|
-
/* @__PURE__ */ (0,
|
|
546
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.KeyboardAvoidingView, { behavior: import_react_native3.Platform.OS === "ios" ? "padding" : void 0, style: styles3.keyboardAvoiding, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.ScrollView, { style: styles3.scrollView, contentContainerStyle: [styles3.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, keyboardShouldPersistTaps: "handled", children: rows.map((row) => {
|
|
407
547
|
if (row.kind === "new") {
|
|
408
548
|
if (editingId !== "new") {
|
|
409
|
-
return /* @__PURE__ */ (0,
|
|
410
|
-
/* @__PURE__ */ (0,
|
|
411
|
-
/* @__PURE__ */ (0,
|
|
549
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => startEdit("new"), style: styles3.row, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native3.View, { style: styles3.rowInner, children: [
|
|
550
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.View, { style: [styles3.rowChip, styles3.newChip, { borderColor: fg }], children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_paper3.Icon, { source: "plus", size: 20, color: fg }) }),
|
|
551
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_paper3.Text, { variant: "bodyLarge", style: { color: fg }, children: "New Profile" })
|
|
412
552
|
] }) }, "new");
|
|
413
553
|
}
|
|
414
|
-
return /* @__PURE__ */ (0,
|
|
554
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EditRow, { draftName, onNameChange: handleNameChange, draftColor, onColorChange: setDraftColor, draftTag, onTagChange: handleTagChange, onSubmit: commitEdit, autoFocus: true, fgMuted, host, dark, colorPreview, useColorPickerAlign }, "new");
|
|
415
555
|
}
|
|
416
556
|
const { profile } = row;
|
|
417
557
|
if (editingId === profile.id) {
|
|
418
|
-
return /* @__PURE__ */ (0,
|
|
558
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EditRow, { draftName, onNameChange: handleNameChange, draftColor, onColorChange: setDraftColor, draftTag, onTagChange: handleTagChange, onSubmit: commitEdit, onDelete: () => setConfirmDeleteId(profile.id), fgMuted, host, dark, colorPreview, useColorPickerAlign }, profile.id);
|
|
419
559
|
}
|
|
420
|
-
return /* @__PURE__ */ (0,
|
|
421
|
-
/* @__PURE__ */ (0,
|
|
422
|
-
/* @__PURE__ */ (0,
|
|
560
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_feedback_press2.TouchableRipple, { onPress: () => startEdit(profile), style: styles3.row, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native3.View, { style: styles3.rowInner, children: [
|
|
561
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ProfileChip, { profile, size: CHIP_SIZE2 }),
|
|
562
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_paper3.Text, { variant: "bodyLarge", style: [styles3.rowName, { color: fg }], numberOfLines: 1, children: profile.name })
|
|
423
563
|
] }) }, profile.id);
|
|
424
564
|
}) }) }),
|
|
425
|
-
/* @__PURE__ */ (0,
|
|
565
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.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) })
|
|
426
566
|
] });
|
|
427
567
|
}
|
|
428
568
|
function AlignedColorPicker({ useAlign, ...props }) {
|
|
429
569
|
const { width: windowWidth } = (0, import_react_native3.useWindowDimensions)();
|
|
430
570
|
const { width, height } = (0, import_hud2.getInlineColorPickerContentSize)(import_auto_paper3.defaultColors.length, windowWidth);
|
|
431
571
|
const alignOverride = useAlign(props.host.openId === props.id, width, height);
|
|
432
|
-
return /* @__PURE__ */ (0,
|
|
572
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_hud2.InlineColorPicker, { ...props, alignOverride });
|
|
433
573
|
}
|
|
434
574
|
function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag, onTagChange, onSubmit, onDelete, autoFocus, fgMuted, host, dark, colorPreview, useColorPickerAlign }) {
|
|
435
575
|
const register = (0, import_focus_chain.useFocusChain)();
|
|
@@ -442,11 +582,11 @@ function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag,
|
|
|
442
582
|
(0, import_react3.useEffect)(() => {
|
|
443
583
|
if (host.openId === "color") tagFocusRef.current();
|
|
444
584
|
}, [host.openId]);
|
|
445
|
-
return /* @__PURE__ */ (0,
|
|
446
|
-
useColorPickerAlign ? /* @__PURE__ */ (0,
|
|
447
|
-
/* @__PURE__ */ (0,
|
|
448
|
-
/* @__PURE__ */ (0,
|
|
449
|
-
onDelete && /* @__PURE__ */ (0,
|
|
585
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native3.View, { style: styles3.editRow, children: [
|
|
586
|
+
useColorPickerAlign ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AlignedColorPicker, { useAlign: useColorPickerAlign, id: "color", host, value: draftColor, onChange: onColorChange, previewValue: colorPreview, tag: draftTag, dark }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_hud2.InlineColorPicker, { id: "color", host, value: draftColor, onChange: onColorChange, previewValue: colorPreview, tag: draftTag, dark }),
|
|
587
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_paper3.TextInput, { ref: tag.ref, ...tag.props, value: draftTag, onChangeText: onTagChange, accessibilityLabel: "Tag", autoCapitalize: "characters", selectTextOnFocus: true, returnKeyType: "next", style: styles3.hiddenTagInput, pointerEvents: "none" }),
|
|
588
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_paper3.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: styles3.nameInput }),
|
|
589
|
+
onDelete && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_feedback_press2.TouchableRipple, { onPress: onDelete, style: styles3.iconButton, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_paper3.Icon, { source: "trash-can-outline", size: 18, color: fgMuted }) })
|
|
450
590
|
] });
|
|
451
591
|
}
|
|
452
592
|
var styles3 = import_react_native3.StyleSheet.create({
|
|
@@ -454,11 +594,17 @@ var styles3 = import_react_native3.StyleSheet.create({
|
|
|
454
594
|
gap: 4,
|
|
455
595
|
paddingHorizontal: 20
|
|
456
596
|
},
|
|
597
|
+
// zIndex: the color popover lives inside this row, and a zIndex only orders siblings of one parent
|
|
598
|
+
// (React Native Web also gives every positioned view its own stacking context), so the popover's
|
|
599
|
+
// own zIndex never reached the rows after this one: they were drawn over it (a Kid row and the New
|
|
600
|
+
// Profile row across the swatches, 2026-10-16). Raising the row lifts the popover with it. There
|
|
601
|
+
// is only ever one edit row.
|
|
457
602
|
editRow: {
|
|
458
603
|
alignItems: "center",
|
|
459
604
|
flexDirection: "row",
|
|
460
605
|
gap: 8,
|
|
461
|
-
paddingVertical: 6
|
|
606
|
+
paddingVertical: 6,
|
|
607
|
+
zIndex: 1
|
|
462
608
|
},
|
|
463
609
|
header: {
|
|
464
610
|
alignItems: "center",
|
|
@@ -499,10 +645,10 @@ var styles3 = import_react_native3.StyleSheet.create({
|
|
|
499
645
|
},
|
|
500
646
|
rowChip: {
|
|
501
647
|
alignItems: "center",
|
|
502
|
-
borderRadius:
|
|
503
|
-
height:
|
|
648
|
+
borderRadius: CHIP_SIZE2 / 2,
|
|
649
|
+
height: CHIP_SIZE2,
|
|
504
650
|
justifyContent: "center",
|
|
505
|
-
width:
|
|
651
|
+
width: CHIP_SIZE2
|
|
506
652
|
},
|
|
507
653
|
rowInner: {
|
|
508
654
|
alignItems: "center",
|
|
@@ -526,7 +672,7 @@ var import_auto_paper4 = require("@rific/auto-paper");
|
|
|
526
672
|
var import_feedback_press3 = require("@rific/feedback-press");
|
|
527
673
|
var import_react4 = require("react");
|
|
528
674
|
var import_react_native4 = require("react-native");
|
|
529
|
-
var
|
|
675
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
530
676
|
function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }) {
|
|
531
677
|
const { dark } = (0, import_auto_paper4.useAutoPaperTheme)();
|
|
532
678
|
const managerRef = (0, import_react4.useRef)(null);
|
|
@@ -536,7 +682,7 @@ function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, on
|
|
|
536
682
|
managerRef.current?.commitPendingEdit();
|
|
537
683
|
onBack();
|
|
538
684
|
};
|
|
539
|
-
return /* @__PURE__ */ (0,
|
|
685
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native4.View, { style: [styles4.container, { backgroundColor: bg }], children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ProfilesManager, { ref: managerRef, profiles, defaultColor, onCreate, onSave, onDelete, fg, fgMuted, titleVariant, colorPreview, useColorPickerAlign, headerLeft: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_feedback_press3.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: handleBack, accessibilityLabel: "Back" }) }) });
|
|
540
686
|
}
|
|
541
687
|
var styles4 = import_react_native4.StyleSheet.create({
|
|
542
688
|
container: {
|