@tastic/profile 0.17.0 → 0.18.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/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +55 -41
- package/dist/index.mjs +34 -20
- package/package.json +1 -1
- package/src/OptionPicker.tsx +24 -10
- package/src/PickerMenu.tsx +17 -9
package/dist/index.d.mts
CHANGED
|
@@ -31,8 +31,9 @@ interface OptionPickerOption<T extends string> {
|
|
|
31
31
|
label: string;
|
|
32
32
|
icon?: string;
|
|
33
33
|
color?: string;
|
|
34
|
+
dividerBefore?: boolean;
|
|
34
35
|
}
|
|
35
|
-
declare function getOptionPickerContentSize(
|
|
36
|
+
declare function getOptionPickerContentSize(options: number | readonly OptionPickerOption<string>[], colorSection?: Pick<ProfilePickerColorSection, 'swatches' | 'caption'>): ProfilePickerContentSize;
|
|
36
37
|
interface OptionPickerProps<T extends string> {
|
|
37
38
|
id: string;
|
|
38
39
|
host: PopoverHost;
|
package/dist/index.d.ts
CHANGED
|
@@ -31,8 +31,9 @@ interface OptionPickerOption<T extends string> {
|
|
|
31
31
|
label: string;
|
|
32
32
|
icon?: string;
|
|
33
33
|
color?: string;
|
|
34
|
+
dividerBefore?: boolean;
|
|
34
35
|
}
|
|
35
|
-
declare function getOptionPickerContentSize(
|
|
36
|
+
declare function getOptionPickerContentSize(options: number | readonly OptionPickerOption<string>[], colorSection?: Pick<ProfilePickerColorSection, 'swatches' | 'caption'>): ProfilePickerContentSize;
|
|
36
37
|
interface OptionPickerProps<T extends string> {
|
|
37
38
|
id: string;
|
|
38
39
|
host: PopoverHost;
|
package/dist/index.js
CHANGED
|
@@ -53,6 +53,9 @@ __export(index_exports, {
|
|
|
53
53
|
});
|
|
54
54
|
module.exports = __toCommonJS(index_exports);
|
|
55
55
|
|
|
56
|
+
// src/OptionPicker.tsx
|
|
57
|
+
var import_react3 = require("react");
|
|
58
|
+
|
|
56
59
|
// src/PickerMenu.tsx
|
|
57
60
|
var import_auto_paper2 = require("@rific/auto-paper");
|
|
58
61
|
var import_feedback_press = require("@rific/feedback-press");
|
|
@@ -151,7 +154,7 @@ var COLOR_ROW_SWATCH_SIZE = 22;
|
|
|
151
154
|
var COLOR_ROW_CAPTIONED_HEIGHT = 48;
|
|
152
155
|
var COLOR_LABEL_LINE = 18;
|
|
153
156
|
var COLOR_CAPTION_LINE = 16;
|
|
154
|
-
var
|
|
157
|
+
var DIVIDER_HEIGHT = 9;
|
|
155
158
|
var GRID_TOP = 4;
|
|
156
159
|
var GRID_BOTTOM = 8;
|
|
157
160
|
var GRID_WIDTH = POPOVER_WIDTH - 2 * MENU_BORDER_WIDTH - 2 * LIST_PADDING - 2 * ROW_INSET;
|
|
@@ -164,8 +167,8 @@ function colorPageHeight(swatches) {
|
|
|
164
167
|
const rows = Math.ceil(swatches.length / GRID_COLUMNS);
|
|
165
168
|
return ROW_HEIGHT + GRID_TOP + rows * SWATCH_SIZE + (rows - 1) * GRID_GAP + GRID_BOTTOM;
|
|
166
169
|
}
|
|
167
|
-
function pickerContentSize(rowCount, colorSection) {
|
|
168
|
-
const list = rowCount * ROW_HEIGHT + (colorSection ? colorRowHeight(colorSection.caption) +
|
|
170
|
+
function pickerContentSize(rowCount, colorSection, dividerCount = 0) {
|
|
171
|
+
const list = rowCount * ROW_HEIGHT + dividerCount * DIVIDER_HEIGHT + (colorSection ? colorRowHeight(colorSection.caption) + DIVIDER_HEIGHT : 0);
|
|
169
172
|
const content = colorSection ? Math.max(list, colorPageHeight(colorSection.swatches ?? import_auto_paper2.defaultColors)) : list;
|
|
170
173
|
return { width: POPOVER_WIDTH, height: LIST_PADDING * 2 + content };
|
|
171
174
|
}
|
|
@@ -216,7 +219,7 @@ function PickerMenu({ id, host, color, dark, label, chip, colorSection, homeColo
|
|
|
216
219
|
children
|
|
217
220
|
] }) }, showColorPage ? "color" : "list"),
|
|
218
221
|
showFooter && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: styles3.menuFooter, children: [
|
|
219
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
222
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MenuDivider, { color }),
|
|
220
223
|
footer
|
|
221
224
|
] })
|
|
222
225
|
]
|
|
@@ -245,9 +248,12 @@ function ColorRow({ section, fg, dividerColor, onPress }) {
|
|
|
245
248
|
] }),
|
|
246
249
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "chevron-right", size: 18, color: fg })
|
|
247
250
|
] }) }),
|
|
248
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
251
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MenuDivider, { color: dividerColor })
|
|
249
252
|
] });
|
|
250
253
|
}
|
|
254
|
+
function MenuDivider({ color }) {
|
|
255
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles3.divider, { backgroundColor: color }] });
|
|
256
|
+
}
|
|
251
257
|
function ColorPage({ section, homeColor, fg, onBack }) {
|
|
252
258
|
const { value, onChange, previewValue, swatches = import_auto_paper2.defaultColors, label = "Color", takenValue, allowSwapTaken } = section;
|
|
253
259
|
const renderSwatch = useProfileSwatchRenderer();
|
|
@@ -339,7 +345,8 @@ var styles3 = import_react_native3.StyleSheet.create({
|
|
|
339
345
|
flex: 1
|
|
340
346
|
},
|
|
341
347
|
// Separates the footer (a navigation action) and the Color row from the selectable rows - a
|
|
342
|
-
// different kind of action, not just another row in the same list
|
|
348
|
+
// different kind of action, not just another row in the same list - and an OptionPicker's groups
|
|
349
|
+
// of options from each other (its `dividerBefore`). Tinted with the seat's own color
|
|
343
350
|
// (passed in as backgroundColor) rather than a flat gray, matching the border/caret's own
|
|
344
351
|
// accent-tinted look.
|
|
345
352
|
divider: {
|
|
@@ -447,8 +454,12 @@ var styles3 = import_react_native3.StyleSheet.create({
|
|
|
447
454
|
|
|
448
455
|
// src/OptionPicker.tsx
|
|
449
456
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
450
|
-
function
|
|
451
|
-
return
|
|
457
|
+
function hasDividerBefore(option, index) {
|
|
458
|
+
return index > 0 && !!option.dividerBefore;
|
|
459
|
+
}
|
|
460
|
+
function getOptionPickerContentSize(options, colorSection) {
|
|
461
|
+
if (typeof options === "number") return pickerContentSize(options, colorSection);
|
|
462
|
+
return pickerContentSize(options.length, colorSection, options.filter(hasDividerBefore).length);
|
|
452
463
|
}
|
|
453
464
|
function OptionPicker({ id, host, options, value, onChange, color, dark, icon, rowIcon = icon, colorSection, align, alignOverride, rotation = 0, triggerStyle }) {
|
|
454
465
|
const fg = dark ? "#FFFFFF" : "#000000";
|
|
@@ -457,7 +468,10 @@ function OptionPicker({ id, host, options, value, onChange, color, dark, icon, r
|
|
|
457
468
|
onChange(option.value);
|
|
458
469
|
host.close();
|
|
459
470
|
};
|
|
460
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PickerMenu, { id, host, color, dark, label: selected?.label ?? "", chip: { icon }, colorSection, contentSize: getOptionPickerContentSize(options
|
|
471
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PickerMenu, { id, host, color, dark, label: selected?.label ?? "", chip: { icon }, colorSection, contentSize: getOptionPickerContentSize(options, colorSection), align, alignOverride, rotation, triggerStyle, children: options.map((option, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react3.Fragment, { children: [
|
|
472
|
+
hasDividerBefore(option, index) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MenuDivider, { color }),
|
|
473
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconRow, { icon: option.icon ?? rowIcon, iconColor: option.color, label: option.label, selected: option.value === value, color, fg, onPress: () => handleSelect(option) })
|
|
474
|
+
] }, option.value)) });
|
|
461
475
|
}
|
|
462
476
|
|
|
463
477
|
// src/ProfileChip.tsx
|
|
@@ -470,7 +484,7 @@ function ProfileChip({ profile, size = 22, filled = true }) {
|
|
|
470
484
|
// src/ProfilePicker.tsx
|
|
471
485
|
var import_auto_paper3 = require("@rific/auto-paper");
|
|
472
486
|
var import_feedback_press2 = require("@rific/feedback-press");
|
|
473
|
-
var
|
|
487
|
+
var import_react4 = require("react");
|
|
474
488
|
var import_react_native4 = require("react-native");
|
|
475
489
|
var import_react_native_paper3 = require("react-native-paper");
|
|
476
490
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
@@ -482,7 +496,7 @@ function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, takenIds
|
|
|
482
496
|
const colorPreview = useProfileColorPreview();
|
|
483
497
|
const fg = dark ? "#FFFFFF" : "#000000";
|
|
484
498
|
const selectedProfile = profiles.find((p) => p.id === selectedId) ?? null;
|
|
485
|
-
const sortedProfiles = (0,
|
|
499
|
+
const sortedProfiles = (0, import_react4.useMemo)(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
486
500
|
const contentSize = getProfilePickerContentSize(profiles.length, !!onManage, colorSection);
|
|
487
501
|
const isTaken = (profileId) => profileId === takenId || profileId !== selectedId && !!takenIds?.includes(profileId);
|
|
488
502
|
const handleSelectGuest = () => {
|
|
@@ -550,7 +564,7 @@ var import_feedback_press3 = require("@rific/feedback-press");
|
|
|
550
564
|
var import_focus_chain = require("@rific/focus-chain");
|
|
551
565
|
var import_toaster = require("@rific/toaster");
|
|
552
566
|
var import_chrome2 = require("@tastic/hud/chrome");
|
|
553
|
-
var
|
|
567
|
+
var import_react5 = require("react");
|
|
554
568
|
var import_react_native5 = require("react-native");
|
|
555
569
|
var import_react_native_paper4 = require("react-native-paper");
|
|
556
570
|
var import_react_native_safe_area_context = require("react-native-safe-area-context");
|
|
@@ -601,18 +615,18 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
601
615
|
const { dark } = (0, import_auto_paper4.useAutoPaperTheme)();
|
|
602
616
|
const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
|
|
603
617
|
const { error: showErrorToast } = (0, import_toaster.useToast)();
|
|
604
|
-
const columnRef = (0,
|
|
618
|
+
const columnRef = (0, import_react5.useRef)(null);
|
|
605
619
|
const renderSwatch = useProfileSwatchRenderer();
|
|
606
620
|
const host = (0, import_chrome2.usePopoverHost)();
|
|
607
621
|
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
608
622
|
const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
|
|
609
|
-
const [editingId, setEditingId] = (0,
|
|
610
|
-
const [draftName, setDraftName] = (0,
|
|
611
|
-
const [draftColor, setDraftColor] = (0,
|
|
612
|
-
const [draftTag, setDraftTag] = (0,
|
|
613
|
-
const [tagManuallySet, setTagManuallySet] = (0,
|
|
614
|
-
const [confirmDeleteId, setConfirmDeleteId] = (0,
|
|
615
|
-
const commitEdit = (0,
|
|
623
|
+
const [editingId, setEditingId] = (0, import_react5.useState)(null);
|
|
624
|
+
const [draftName, setDraftName] = (0, import_react5.useState)("");
|
|
625
|
+
const [draftColor, setDraftColor] = (0, import_react5.useState)(defaultColor);
|
|
626
|
+
const [draftTag, setDraftTag] = (0, import_react5.useState)("");
|
|
627
|
+
const [tagManuallySet, setTagManuallySet] = (0, import_react5.useState)(false);
|
|
628
|
+
const [confirmDeleteId, setConfirmDeleteId] = (0, import_react5.useState)(null);
|
|
629
|
+
const commitEdit = (0, import_react5.useCallback)(() => {
|
|
616
630
|
const name = draftName.trim();
|
|
617
631
|
if (editingId && name) {
|
|
618
632
|
const tagValid = isValidTag(draftTag);
|
|
@@ -623,13 +637,13 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
623
637
|
}
|
|
624
638
|
setEditingId(null);
|
|
625
639
|
}, [editingId, draftName, draftColor, draftTag, onCreate, onSave, showErrorToast]);
|
|
626
|
-
const commitEditRef = (0,
|
|
627
|
-
(0,
|
|
640
|
+
const commitEditRef = (0, import_react5.useRef)(commitEdit);
|
|
641
|
+
(0, import_react5.useEffect)(() => {
|
|
628
642
|
commitEditRef.current = commitEdit;
|
|
629
643
|
});
|
|
630
|
-
(0,
|
|
631
|
-
(0,
|
|
632
|
-
const startEdit = (0,
|
|
644
|
+
(0, import_react5.useEffect)(() => () => commitEditRef.current(), []);
|
|
645
|
+
(0, import_react5.useImperativeHandle)(ref, () => ({ commitPendingEdit: () => commitEditRef.current() }), []);
|
|
646
|
+
const startEdit = (0, import_react5.useCallback)(
|
|
633
647
|
(target) => {
|
|
634
648
|
commitEdit();
|
|
635
649
|
if (target === "new") {
|
|
@@ -647,26 +661,26 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
647
661
|
},
|
|
648
662
|
[commitEdit, defaultColor]
|
|
649
663
|
);
|
|
650
|
-
const handleNameChange = (0,
|
|
664
|
+
const handleNameChange = (0, import_react5.useCallback)(
|
|
651
665
|
(name) => {
|
|
652
666
|
setDraftName(name);
|
|
653
667
|
if (!tagManuallySet) setDraftTag(deriveTag(name));
|
|
654
668
|
},
|
|
655
669
|
[tagManuallySet]
|
|
656
670
|
);
|
|
657
|
-
const handleTagChange = (0,
|
|
671
|
+
const handleTagChange = (0, import_react5.useCallback)((tag) => {
|
|
658
672
|
setDraftTag((previous) => clampTag(previous, tag));
|
|
659
673
|
setTagManuallySet(true);
|
|
660
674
|
}, []);
|
|
661
675
|
const confirmDeleteProfile = profiles.find((p) => p.id === confirmDeleteId) ?? null;
|
|
662
|
-
const handleConfirmDelete = (0,
|
|
676
|
+
const handleConfirmDelete = (0, import_react5.useCallback)(() => {
|
|
663
677
|
if (confirmDeleteId) {
|
|
664
678
|
onDelete(confirmDeleteId);
|
|
665
679
|
if (editingId === confirmDeleteId) setEditingId(null);
|
|
666
680
|
}
|
|
667
681
|
setConfirmDeleteId(null);
|
|
668
682
|
}, [confirmDeleteId, editingId, onDelete]);
|
|
669
|
-
const sortedProfiles = (0,
|
|
683
|
+
const sortedProfiles = (0, import_react5.useMemo)(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
670
684
|
const rows = [...sortedProfiles.map((profile) => ({ kind: "profile", profile })), { kind: "new" }];
|
|
671
685
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
672
686
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native5.View, { style: [styles5.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
|
|
@@ -702,15 +716,15 @@ function AlignedColorPicker({ useAlign, column, ...props }) {
|
|
|
702
716
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_chrome2.InlineColorPicker, { ...props, alignOverride });
|
|
703
717
|
}
|
|
704
718
|
function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag, onTagChange, onSubmit, onDelete, autoFocus, fgMuted, host, dark, colorPreview, useColorPickerAlign, column, renderSwatch }) {
|
|
705
|
-
const pickerSwatch = (0,
|
|
719
|
+
const pickerSwatch = (0, import_react5.useMemo)(() => renderSwatch ? (color, size, place) => renderSwatch(color, size, place === "trigger" ? "chip" : "colorPage") : void 0, [renderSwatch]);
|
|
706
720
|
const register = (0, import_focus_chain.useFocusChain)();
|
|
707
721
|
const tag = register();
|
|
708
722
|
const name = register();
|
|
709
|
-
const tagFocusRef = (0,
|
|
710
|
-
(0,
|
|
723
|
+
const tagFocusRef = (0, import_react5.useRef)(tag.props.focus);
|
|
724
|
+
(0, import_react5.useEffect)(() => {
|
|
711
725
|
tagFocusRef.current = tag.props.focus;
|
|
712
726
|
});
|
|
713
|
-
(0,
|
|
727
|
+
(0, import_react5.useEffect)(() => {
|
|
714
728
|
if (host.openId === "color") tagFocusRef.current();
|
|
715
729
|
}, [host.openId]);
|
|
716
730
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native5.View, { style: styles5.editRow, children: [
|
|
@@ -805,12 +819,12 @@ var styles5 = import_react_native5.StyleSheet.create({
|
|
|
805
819
|
// src/ProfilesScreen.tsx
|
|
806
820
|
var import_auto_paper5 = require("@rific/auto-paper");
|
|
807
821
|
var import_feedback_press4 = require("@rific/feedback-press");
|
|
808
|
-
var
|
|
822
|
+
var import_react6 = require("react");
|
|
809
823
|
var import_react_native6 = require("react-native");
|
|
810
824
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
811
825
|
function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }) {
|
|
812
826
|
const { dark } = (0, import_auto_paper5.useAutoPaperTheme)();
|
|
813
|
-
const managerRef = (0,
|
|
827
|
+
const managerRef = (0, import_react6.useRef)(null);
|
|
814
828
|
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
815
829
|
const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
|
|
816
830
|
const handleBack = () => {
|
|
@@ -1102,15 +1116,15 @@ function createSeatColorsSlice(namespace, defaultState, mountKey = "seatColors")
|
|
|
1102
1116
|
}
|
|
1103
1117
|
|
|
1104
1118
|
// src/redux/useSharedProfilesSync.ts
|
|
1105
|
-
var
|
|
1119
|
+
var import_react7 = require("react");
|
|
1106
1120
|
var import_react_native8 = require("react-native");
|
|
1107
1121
|
function useSharedProfilesSync({ groupId, onRemoteChange }) {
|
|
1108
|
-
const pendingWriteRef = (0,
|
|
1109
|
-
const onRemoteChangeRef = (0,
|
|
1110
|
-
(0,
|
|
1122
|
+
const pendingWriteRef = (0, import_react7.useRef)(false);
|
|
1123
|
+
const onRemoteChangeRef = (0, import_react7.useRef)(onRemoteChange);
|
|
1124
|
+
(0, import_react7.useEffect)(() => {
|
|
1111
1125
|
onRemoteChangeRef.current = onRemoteChange;
|
|
1112
1126
|
}, [onRemoteChange]);
|
|
1113
|
-
(0,
|
|
1127
|
+
(0, import_react7.useEffect)(() => {
|
|
1114
1128
|
if (!isSharedProfileStoreAvailable) return;
|
|
1115
1129
|
let cancelled = false;
|
|
1116
1130
|
const sub = import_react_native8.AppState.addEventListener("change", (next) => {
|
|
@@ -1128,7 +1142,7 @@ function useSharedProfilesSync({ groupId, onRemoteChange }) {
|
|
|
1128
1142
|
unsubscribe();
|
|
1129
1143
|
};
|
|
1130
1144
|
}, [groupId]);
|
|
1131
|
-
const syncToShared = (0,
|
|
1145
|
+
const syncToShared = (0, import_react7.useCallback)(
|
|
1132
1146
|
(next) => {
|
|
1133
1147
|
if (!isSharedProfileStoreAvailable) return;
|
|
1134
1148
|
pendingWriteRef.current = true;
|
package/dist/index.mjs
CHANGED
|
@@ -5,6 +5,9 @@ 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/OptionPicker.tsx
|
|
9
|
+
import { Fragment as Fragment2 } from "react";
|
|
10
|
+
|
|
8
11
|
// src/PickerMenu.tsx
|
|
9
12
|
import { defaultColors, FadingScrollView, getContrastColor as getContrastColor2 } from "@rific/auto-paper";
|
|
10
13
|
import { TouchableRipple } from "@rific/feedback-press";
|
|
@@ -103,7 +106,7 @@ var COLOR_ROW_SWATCH_SIZE = 22;
|
|
|
103
106
|
var COLOR_ROW_CAPTIONED_HEIGHT = 48;
|
|
104
107
|
var COLOR_LABEL_LINE = 18;
|
|
105
108
|
var COLOR_CAPTION_LINE = 16;
|
|
106
|
-
var
|
|
109
|
+
var DIVIDER_HEIGHT = 9;
|
|
107
110
|
var GRID_TOP = 4;
|
|
108
111
|
var GRID_BOTTOM = 8;
|
|
109
112
|
var GRID_WIDTH = POPOVER_WIDTH - 2 * MENU_BORDER_WIDTH - 2 * LIST_PADDING - 2 * ROW_INSET;
|
|
@@ -116,8 +119,8 @@ function colorPageHeight(swatches) {
|
|
|
116
119
|
const rows = Math.ceil(swatches.length / GRID_COLUMNS);
|
|
117
120
|
return ROW_HEIGHT + GRID_TOP + rows * SWATCH_SIZE + (rows - 1) * GRID_GAP + GRID_BOTTOM;
|
|
118
121
|
}
|
|
119
|
-
function pickerContentSize(rowCount, colorSection) {
|
|
120
|
-
const list = rowCount * ROW_HEIGHT + (colorSection ? colorRowHeight(colorSection.caption) +
|
|
122
|
+
function pickerContentSize(rowCount, colorSection, dividerCount = 0) {
|
|
123
|
+
const list = rowCount * ROW_HEIGHT + dividerCount * DIVIDER_HEIGHT + (colorSection ? colorRowHeight(colorSection.caption) + DIVIDER_HEIGHT : 0);
|
|
121
124
|
const content = colorSection ? Math.max(list, colorPageHeight(colorSection.swatches ?? defaultColors)) : list;
|
|
122
125
|
return { width: POPOVER_WIDTH, height: LIST_PADDING * 2 + content };
|
|
123
126
|
}
|
|
@@ -168,7 +171,7 @@ function PickerMenu({ id, host, color, dark, label, chip, colorSection, homeColo
|
|
|
168
171
|
children
|
|
169
172
|
] }) }, showColorPage ? "color" : "list"),
|
|
170
173
|
showFooter && /* @__PURE__ */ jsxs2(View3, { style: styles3.menuFooter, children: [
|
|
171
|
-
/* @__PURE__ */ jsx4(
|
|
174
|
+
/* @__PURE__ */ jsx4(MenuDivider, { color }),
|
|
172
175
|
footer
|
|
173
176
|
] })
|
|
174
177
|
]
|
|
@@ -197,9 +200,12 @@ function ColorRow({ section, fg, dividerColor, onPress }) {
|
|
|
197
200
|
] }),
|
|
198
201
|
/* @__PURE__ */ jsx4(Icon2, { source: "chevron-right", size: 18, color: fg })
|
|
199
202
|
] }) }),
|
|
200
|
-
/* @__PURE__ */ jsx4(
|
|
203
|
+
/* @__PURE__ */ jsx4(MenuDivider, { color: dividerColor })
|
|
201
204
|
] });
|
|
202
205
|
}
|
|
206
|
+
function MenuDivider({ color }) {
|
|
207
|
+
return /* @__PURE__ */ jsx4(View3, { style: [styles3.divider, { backgroundColor: color }] });
|
|
208
|
+
}
|
|
203
209
|
function ColorPage({ section, homeColor, fg, onBack }) {
|
|
204
210
|
const { value, onChange, previewValue, swatches = defaultColors, label = "Color", takenValue, allowSwapTaken } = section;
|
|
205
211
|
const renderSwatch = useProfileSwatchRenderer();
|
|
@@ -291,7 +297,8 @@ var styles3 = StyleSheet3.create({
|
|
|
291
297
|
flex: 1
|
|
292
298
|
},
|
|
293
299
|
// Separates the footer (a navigation action) and the Color row from the selectable rows - a
|
|
294
|
-
// different kind of action, not just another row in the same list
|
|
300
|
+
// different kind of action, not just another row in the same list - and an OptionPicker's groups
|
|
301
|
+
// of options from each other (its `dividerBefore`). Tinted with the seat's own color
|
|
295
302
|
// (passed in as backgroundColor) rather than a flat gray, matching the border/caret's own
|
|
296
303
|
// accent-tinted look.
|
|
297
304
|
divider: {
|
|
@@ -398,9 +405,13 @@ var styles3 = StyleSheet3.create({
|
|
|
398
405
|
});
|
|
399
406
|
|
|
400
407
|
// src/OptionPicker.tsx
|
|
401
|
-
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
402
|
-
function
|
|
403
|
-
return
|
|
408
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
409
|
+
function hasDividerBefore(option, index) {
|
|
410
|
+
return index > 0 && !!option.dividerBefore;
|
|
411
|
+
}
|
|
412
|
+
function getOptionPickerContentSize(options, colorSection) {
|
|
413
|
+
if (typeof options === "number") return pickerContentSize(options, colorSection);
|
|
414
|
+
return pickerContentSize(options.length, colorSection, options.filter(hasDividerBefore).length);
|
|
404
415
|
}
|
|
405
416
|
function OptionPicker({ id, host, options, value, onChange, color, dark, icon, rowIcon = icon, colorSection, align, alignOverride, rotation = 0, triggerStyle }) {
|
|
406
417
|
const fg = dark ? "#FFFFFF" : "#000000";
|
|
@@ -409,7 +420,10 @@ function OptionPicker({ id, host, options, value, onChange, color, dark, icon, r
|
|
|
409
420
|
onChange(option.value);
|
|
410
421
|
host.close();
|
|
411
422
|
};
|
|
412
|
-
return /* @__PURE__ */ jsx5(PickerMenu, { id, host, color, dark, label: selected?.label ?? "", chip: { icon }, colorSection, contentSize: getOptionPickerContentSize(options
|
|
423
|
+
return /* @__PURE__ */ jsx5(PickerMenu, { id, host, color, dark, label: selected?.label ?? "", chip: { icon }, colorSection, contentSize: getOptionPickerContentSize(options, colorSection), align, alignOverride, rotation, triggerStyle, children: options.map((option, index) => /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
424
|
+
hasDividerBefore(option, index) && /* @__PURE__ */ jsx5(MenuDivider, { color }),
|
|
425
|
+
/* @__PURE__ */ jsx5(IconRow, { icon: option.icon ?? rowIcon, iconColor: option.color, label: option.label, selected: option.value === value, color, fg, onPress: () => handleSelect(option) })
|
|
426
|
+
] }, option.value)) });
|
|
413
427
|
}
|
|
414
428
|
|
|
415
429
|
// src/ProfileChip.tsx
|
|
@@ -425,7 +439,7 @@ import { TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
|
|
|
425
439
|
import { useMemo as useMemo2 } from "react";
|
|
426
440
|
import { StyleSheet as StyleSheet4, View as View4 } from "react-native";
|
|
427
441
|
import { Icon as Icon3, Text as Text3 } from "react-native-paper";
|
|
428
|
-
import { jsx as jsx7, jsxs as
|
|
442
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
429
443
|
function getProfilePickerContentSize(profileCount, hasManageRow = false, colorSection) {
|
|
430
444
|
return pickerContentSize(profileCount + 1 + (hasManageRow ? 1 : 0), colorSection);
|
|
431
445
|
}
|
|
@@ -450,17 +464,17 @@ function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, takenIds
|
|
|
450
464
|
host.close();
|
|
451
465
|
onManage?.();
|
|
452
466
|
};
|
|
453
|
-
const manageFooter = onManage ? /* @__PURE__ */ jsx7(TouchableRipple2, { onPress: handleManage, style: pickerMenuStyles.row, children: /* @__PURE__ */
|
|
467
|
+
const manageFooter = onManage ? /* @__PURE__ */ jsx7(TouchableRipple2, { onPress: handleManage, style: pickerMenuStyles.row, children: /* @__PURE__ */ jsxs4(View4, { style: styles4.manageRowInner, children: [
|
|
454
468
|
/* @__PURE__ */ jsx7(Icon3, { source: "account-cog-outline", size: 16, color }),
|
|
455
469
|
/* @__PURE__ */ jsx7(Text3, { style: [styles4.manageLabel, { color: fg }], children: "Manage" })
|
|
456
470
|
] }) }) : void 0;
|
|
457
|
-
return /* @__PURE__ */
|
|
471
|
+
return /* @__PURE__ */ jsxs4(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: [
|
|
458
472
|
/* @__PURE__ */ jsx7(IconRow, { icon: nullIcon, label: nullLabel, selected: selectedId === null, color, fg, onPress: handleSelectGuest }),
|
|
459
473
|
sortedProfiles.map((profile) => {
|
|
460
474
|
const taken = isTaken(profile.id);
|
|
461
475
|
const selected = selectedId === profile.id;
|
|
462
476
|
const rowColor = colorPreview ? colorPreview(profile.color) : profile.color;
|
|
463
|
-
return /* @__PURE__ */ jsx7(TouchableRipple2, { disabled: taken, onPress: () => handleSelectExisting(profile), style: [pickerMenuStyles.row, selected && { backgroundColor: rowColor }], children: /* @__PURE__ */
|
|
477
|
+
return /* @__PURE__ */ jsx7(TouchableRipple2, { disabled: taken, onPress: () => handleSelectExisting(profile), style: [pickerMenuStyles.row, selected && { backgroundColor: rowColor }], children: /* @__PURE__ */ jsxs4(View4, { style: [pickerMenuStyles.rowInner, taken && pickerMenuStyles.rowDisabled], children: [
|
|
464
478
|
/* @__PURE__ */ jsx7(ProfileChip, { profile, filled: !selected }),
|
|
465
479
|
/* @__PURE__ */ jsx7(
|
|
466
480
|
Text3,
|
|
@@ -540,7 +554,7 @@ function isValidProfile(value) {
|
|
|
540
554
|
}
|
|
541
555
|
|
|
542
556
|
// src/ProfilesManager.tsx
|
|
543
|
-
import { Fragment as
|
|
557
|
+
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
544
558
|
var CHIP_SIZE2 = 40;
|
|
545
559
|
var textInputRef = (ref) => ref;
|
|
546
560
|
function deriveTag(name) {
|
|
@@ -620,15 +634,15 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
620
634
|
}, [confirmDeleteId, editingId, onDelete]);
|
|
621
635
|
const sortedProfiles = useMemo3(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
622
636
|
const rows = [...sortedProfiles.map((profile) => ({ kind: "profile", profile })), { kind: "new" }];
|
|
623
|
-
return /* @__PURE__ */
|
|
624
|
-
/* @__PURE__ */
|
|
637
|
+
return /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
638
|
+
/* @__PURE__ */ jsxs5(View5, { style: [styles5.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
|
|
625
639
|
headerLeft,
|
|
626
640
|
/* @__PURE__ */ jsx8(Text4, { variant: titleVariant, style: [styles5.title, { color: fg }], children: "Profiles" })
|
|
627
641
|
] }),
|
|
628
642
|
/* @__PURE__ */ jsx8(KeyboardAvoidingView, { behavior: Platform.OS === "ios" ? "padding" : void 0, style: styles5.keyboardAvoiding, children: /* @__PURE__ */ jsx8(View5, { ref: columnRef, collapsable: false, style: styles5.column, children: /* @__PURE__ */ jsx8(FadingScrollView2, { style: styles5.scrollView, contentContainerStyle: [styles5.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, keyboardShouldPersistTaps: "handled", children: rows.map((row) => {
|
|
629
643
|
if (row.kind === "new") {
|
|
630
644
|
if (editingId !== "new") {
|
|
631
|
-
return /* @__PURE__ */ jsx8(TouchableRipple3, { onPress: () => startEdit("new"), style: styles5.row, children: /* @__PURE__ */
|
|
645
|
+
return /* @__PURE__ */ jsx8(TouchableRipple3, { onPress: () => startEdit("new"), style: styles5.row, children: /* @__PURE__ */ jsxs5(View5, { style: styles5.rowInner, children: [
|
|
632
646
|
/* @__PURE__ */ jsx8(View5, { style: [styles5.rowChip, styles5.newChip, { borderColor: fg }], children: /* @__PURE__ */ jsx8(Icon4, { source: "plus", size: 20, color: fg }) }),
|
|
633
647
|
/* @__PURE__ */ jsx8(Text4, { variant: "bodyLarge", style: { color: fg }, children: "New Profile" })
|
|
634
648
|
] }) }, "new");
|
|
@@ -639,7 +653,7 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
639
653
|
if (editingId === profile.id) {
|
|
640
654
|
return /* @__PURE__ */ jsx8(EditRow, { draftName, onNameChange: handleNameChange, draftColor, onColorChange: setDraftColor, draftTag, onTagChange: handleTagChange, onSubmit: commitEdit, onDelete: () => setConfirmDeleteId(profile.id), fgMuted, host, dark, colorPreview, useColorPickerAlign, column: columnRef, renderSwatch }, profile.id);
|
|
641
655
|
}
|
|
642
|
-
return /* @__PURE__ */ jsx8(TouchableRipple3, { onPress: () => startEdit(profile), style: styles5.row, children: /* @__PURE__ */
|
|
656
|
+
return /* @__PURE__ */ jsx8(TouchableRipple3, { onPress: () => startEdit(profile), style: styles5.row, children: /* @__PURE__ */ jsxs5(View5, { style: styles5.rowInner, children: [
|
|
643
657
|
/* @__PURE__ */ jsx8(ProfileChip, { profile, size: CHIP_SIZE2 }),
|
|
644
658
|
/* @__PURE__ */ jsx8(Text4, { variant: "bodyLarge", style: [styles5.rowName, { color: fg }], numberOfLines: 1, children: profile.name })
|
|
645
659
|
] }) }, profile.id);
|
|
@@ -665,7 +679,7 @@ function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag,
|
|
|
665
679
|
useEffect(() => {
|
|
666
680
|
if (host.openId === "color") tagFocusRef.current();
|
|
667
681
|
}, [host.openId]);
|
|
668
|
-
return /* @__PURE__ */
|
|
682
|
+
return /* @__PURE__ */ jsxs5(View5, { style: styles5.editRow, children: [
|
|
669
683
|
useColorPickerAlign ? /* @__PURE__ */ jsx8(AlignedColorPicker, { useAlign: useColorPickerAlign, column, id: "color", host, value: draftColor, onChange: onColorChange, previewValue: colorPreview, tag: draftTag, dark, renderSwatch: pickerSwatch }) : /* @__PURE__ */ jsx8(InlineColorPicker, { id: "color", host, value: draftColor, onChange: onColorChange, previewValue: colorPreview, tag: draftTag, dark, renderSwatch: pickerSwatch, bounds: column }),
|
|
670
684
|
/* @__PURE__ */ jsx8(TextInput, { ref: textInputRef(tag.ref), ...tag.props, value: draftTag, onChangeText: onTagChange, accessibilityLabel: "Tag", autoCapitalize: "characters", selectTextOnFocus: true, returnKeyType: "next", style: styles5.hiddenTagInput, pointerEvents: "none" }),
|
|
671
685
|
/* @__PURE__ */ jsx8(TextInput, { ref: textInputRef(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 }),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tastic/profile",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Player profile identity for local-multiplayer React Native games: a saved name/color/tag a player selects per seat, with a picker, an inline-editable roster screen, and a reusable identity badge, deliberately blind to any app-specific fields (control scheme, stats) an app layers on top",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
package/src/OptionPicker.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { PopoverHost, PopoverRotation } from '@tastic/hud/chrome'
|
|
2
|
+
import { Fragment } from 'react'
|
|
2
3
|
import { type StyleProp, type ViewStyle } from 'react-native'
|
|
3
4
|
|
|
4
|
-
import { IconRow, pickerContentSize, PickerMenu, type ProfilePickerAlignResult, type ProfilePickerColorSection, type ProfilePickerContentSize } from './PickerMenu'
|
|
5
|
+
import { IconRow, MenuDivider, pickerContentSize, PickerMenu, type ProfilePickerAlignResult, type ProfilePickerColorSection, type ProfilePickerContentSize } from './PickerMenu'
|
|
5
6
|
|
|
6
7
|
export interface OptionPickerOption<T extends string> {
|
|
7
8
|
value: T
|
|
@@ -11,14 +12,24 @@ export interface OptionPickerOption<T extends string> {
|
|
|
11
12
|
// one; the selected row still fills with the picker's `color`, its icon contrasting as always.
|
|
12
13
|
// Without it the icon takes the row's text color.
|
|
13
14
|
color?: string
|
|
15
|
+
// A divider above this row, setting it and the rows after it apart from the ones before as a
|
|
16
|
+
// different kind of choice (a person after a CPU's levels). Ignored on the first option, which the
|
|
17
|
+
// Color row's own divider, or nothing, already sits above.
|
|
18
|
+
dividerBefore?: boolean
|
|
14
19
|
}
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
function hasDividerBefore(option: OptionPickerOption<string>, index: number): boolean {
|
|
22
|
+
return index > 0 && !!option.dividerBefore
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// The popover's content footprint: one row per option and its dividers, plus (with a
|
|
26
|
+
// `colorSection`) the Color row, or the color page if that's taller - the same formula as
|
|
27
|
+
// getProfilePickerContentSize, for a host that places this picker itself through `alignOverride` (a
|
|
28
|
+
// @tastic/split-screen zone's useZoneClampedAlign, for one). Given the options themselves it counts
|
|
29
|
+
// their dividers too; given a count, rows only.
|
|
30
|
+
export function getOptionPickerContentSize(options: number | readonly OptionPickerOption<string>[], colorSection?: Pick<ProfilePickerColorSection, 'swatches' | 'caption'>): ProfilePickerContentSize {
|
|
31
|
+
if (typeof options === 'number') return pickerContentSize(options, colorSection)
|
|
32
|
+
return pickerContentSize(options.length, colorSection, options.filter(hasDividerBefore).length)
|
|
22
33
|
}
|
|
23
34
|
|
|
24
35
|
interface OptionPickerProps<T extends string> {
|
|
@@ -61,9 +72,12 @@ export function OptionPicker<T extends string>({ id, host, options, value, onCha
|
|
|
61
72
|
}
|
|
62
73
|
|
|
63
74
|
return (
|
|
64
|
-
<PickerMenu id={id} host={host} color={color} dark={dark} label={selected?.label ?? ''} chip={{ icon }} colorSection={colorSection} contentSize={getOptionPickerContentSize(options
|
|
65
|
-
{options.map((option) => (
|
|
66
|
-
<
|
|
75
|
+
<PickerMenu id={id} host={host} color={color} dark={dark} label={selected?.label ?? ''} chip={{ icon }} colorSection={colorSection} contentSize={getOptionPickerContentSize(options, colorSection)} align={align} alignOverride={alignOverride} rotation={rotation} triggerStyle={triggerStyle}>
|
|
76
|
+
{options.map((option, index) => (
|
|
77
|
+
<Fragment key={option.value}>
|
|
78
|
+
{hasDividerBefore(option, index) && <MenuDivider color={color} />}
|
|
79
|
+
<IconRow icon={option.icon ?? rowIcon} iconColor={option.color} label={option.label} selected={option.value === value} color={color} fg={fg} onPress={() => handleSelect(option)} />
|
|
80
|
+
</Fragment>
|
|
67
81
|
))}
|
|
68
82
|
</PickerMenu>
|
|
69
83
|
)
|
package/src/PickerMenu.tsx
CHANGED
|
@@ -49,8 +49,9 @@ const COLOR_ROW_SWATCH_SIZE = 22
|
|
|
49
49
|
const COLOR_ROW_CAPTIONED_HEIGHT = 48
|
|
50
50
|
const COLOR_LABEL_LINE = 18
|
|
51
51
|
const COLOR_CAPTION_LINE = 16
|
|
52
|
-
//
|
|
53
|
-
|
|
52
|
+
// Every divider in the menu (under the Color row, over Manage, and between an OptionPicker's groups
|
|
53
|
+
// of options): 1pt plus 4pt above and below.
|
|
54
|
+
const DIVIDER_HEIGHT = 9
|
|
54
55
|
// The color page: a back row, then the grid with this much space above and below it.
|
|
55
56
|
const GRID_TOP = 4
|
|
56
57
|
const GRID_BOTTOM = 8
|
|
@@ -115,10 +116,11 @@ function colorPageHeight(swatches: SeedColor[]) {
|
|
|
115
116
|
// pages, exactly: the list with the Color row and its divider on top, or the color page (a back row
|
|
116
117
|
// and the swatches), so one placement fits whichever page is showing. It leaves out the menu's 2pt
|
|
117
118
|
// border and the Manage divider, ~13pt in all, so treat it as a placement estimate, not a
|
|
118
|
-
// pixel-exact measurement.
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
// pixel-exact measurement. `dividerCount` is the dividers between the rows (an OptionPicker's
|
|
120
|
+
// `dividerBefore`). getProfilePickerContentSize and getOptionPickerContentSize count the rows for
|
|
121
|
+
// their own picker.
|
|
122
|
+
export function pickerContentSize(rowCount: number, colorSection?: Pick<ProfilePickerColorSection, 'swatches' | 'caption'>, dividerCount = 0): ProfilePickerContentSize {
|
|
123
|
+
const list = rowCount * ROW_HEIGHT + dividerCount * DIVIDER_HEIGHT + (colorSection ? colorRowHeight(colorSection.caption) + DIVIDER_HEIGHT : 0)
|
|
122
124
|
const content = colorSection ? Math.max(list, colorPageHeight(colorSection.swatches ?? defaultColors)) : list
|
|
123
125
|
return { width: POPOVER_WIDTH, height: LIST_PADDING * 2 + content }
|
|
124
126
|
}
|
|
@@ -242,7 +244,7 @@ export function PickerMenu({ id, host, color, dark, label, chip, colorSection, h
|
|
|
242
244
|
below. Not on the color page, which only has the back row and the swatches. */}
|
|
243
245
|
{showFooter && (
|
|
244
246
|
<View style={styles.menuFooter}>
|
|
245
|
-
<
|
|
247
|
+
<MenuDivider color={color} />
|
|
246
248
|
{footer}
|
|
247
249
|
</View>
|
|
248
250
|
)}
|
|
@@ -315,11 +317,16 @@ function ColorRow({ section, fg, dividerColor, onPress }: { section: ProfilePick
|
|
|
315
317
|
<Icon source='chevron-right' size={18} color={fg} />
|
|
316
318
|
</View>
|
|
317
319
|
</TouchableRipple>
|
|
318
|
-
<
|
|
320
|
+
<MenuDivider color={dividerColor} />
|
|
319
321
|
</>
|
|
320
322
|
)
|
|
321
323
|
}
|
|
322
324
|
|
|
325
|
+
// A line across the list between kinds of rows (see `divider` below), in the seat's own color.
|
|
326
|
+
export function MenuDivider({ color }: { color: string }) {
|
|
327
|
+
return <View style={[styles.divider, { backgroundColor: color }]} />
|
|
328
|
+
}
|
|
329
|
+
|
|
323
330
|
// The color page: a back row to the list, then the swatches. `homeColor` is the selected profile's
|
|
324
331
|
// own color: its swatch carries a dot, so a player who has picked something else can find their way
|
|
325
332
|
// back to it (the profile's row shows that color, but matching it to one of two similar swatches by
|
|
@@ -443,7 +450,8 @@ const styles = StyleSheet.create({
|
|
|
443
450
|
flex: 1
|
|
444
451
|
},
|
|
445
452
|
// Separates the footer (a navigation action) and the Color row from the selectable rows - a
|
|
446
|
-
// different kind of action, not just another row in the same list
|
|
453
|
+
// different kind of action, not just another row in the same list - and an OptionPicker's groups
|
|
454
|
+
// of options from each other (its `dividerBefore`). Tinted with the seat's own color
|
|
447
455
|
// (passed in as backgroundColor) rather than a flat gray, matching the border/caret's own
|
|
448
456
|
// accent-tinted look.
|
|
449
457
|
divider: {
|