@tastic/profile 0.16.0 → 0.16.2
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 +8 -5
- package/dist/index.d.mts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +17 -10
- package/dist/index.mjs +17 -10
- package/package.json +4 -4
- package/src/IdentityChip.tsx +4 -3
- package/src/OptionPicker.tsx +5 -1
- package/src/PickerMenu.tsx +13 -7
- package/src/ProfileColorContext.tsx +16 -7
- package/src/SwatchLayer.tsx +12 -6
- package/src/index.ts +1 -1
package/README.md
CHANGED
|
@@ -310,16 +310,19 @@ untouched. `useProfileColorPreview()` returns the provider's function (or `undef
|
|
|
310
310
|
|
|
311
311
|
`renderSwatch` (also optional, with or without `preview`) draws the fill of every round swatch this
|
|
312
312
|
package draws, in place of the flat color: `ProfileChip`, the pickers' chip trigger, the Color row's
|
|
313
|
-
swatch and the color page's swatches. It's called as `renderSwatch(color, size)` with the
|
|
314
|
-
color (after `preview`)
|
|
315
|
-
|
|
316
|
-
the
|
|
313
|
+
swatch and the color page's swatches. It's called as `renderSwatch(color, size, place)` with the
|
|
314
|
+
drawn color (after `preview`), the circle's diameter and where the swatch is (`ProfileSwatchPlace`:
|
|
315
|
+
`'chip'`, `'colorRow'` or `'colorPage'`, 0.16.2+), and its drawing is clipped to the circle, behind
|
|
316
|
+
the tag, icon or check. Return `null` (or `undefined`) to keep a swatch's flat fill there. The tag and
|
|
317
|
+
check still take their contrast from `color`, so treat it as the primary color of whatever you draw.
|
|
318
|
+
Hangman draws a pie of each color's triad where colors are chosen, and keeps its chips flat so their
|
|
319
|
+
tags stay legible:
|
|
317
320
|
|
|
318
321
|
```tsx
|
|
319
322
|
import { getTriadicPalette } from '@rific/auto-paper'
|
|
320
323
|
import { ProfileColorProvider, type ProfileSwatchRenderer } from '@tastic/profile'
|
|
321
324
|
|
|
322
|
-
const renderSwatch: ProfileSwatchRenderer = (color, size) => <TriadPie colors={getTriadicPalette(color)} size={size} />
|
|
325
|
+
const renderSwatch: ProfileSwatchRenderer = (color, size, place) => (place === 'chip' ? null : <TriadPie colors={getTriadicPalette(color)} size={size} />)
|
|
323
326
|
<ProfileColorProvider renderSwatch={renderSwatch}>{children}</ProfileColorProvider>
|
|
324
327
|
```
|
|
325
328
|
|
package/dist/index.d.mts
CHANGED
|
@@ -30,6 +30,7 @@ interface OptionPickerOption<T extends string> {
|
|
|
30
30
|
value: T;
|
|
31
31
|
label: string;
|
|
32
32
|
icon?: string;
|
|
33
|
+
color?: string;
|
|
33
34
|
}
|
|
34
35
|
declare function getOptionPickerContentSize(optionCount: number, colorSection?: Pick<ProfilePickerColorSection, 'swatches' | 'caption'>): ProfilePickerContentSize;
|
|
35
36
|
interface OptionPickerProps<T extends string> {
|
|
@@ -66,7 +67,8 @@ interface Props {
|
|
|
66
67
|
}
|
|
67
68
|
declare function ProfileChip({ profile, size, filled }: Props): react.JSX.Element;
|
|
68
69
|
|
|
69
|
-
type
|
|
70
|
+
type ProfileSwatchPlace = 'chip' | 'colorRow' | 'colorPage';
|
|
71
|
+
type ProfileSwatchRenderer = (color: string, size: number, place: ProfileSwatchPlace) => ReactNode;
|
|
70
72
|
interface ProfileColorProviderProps {
|
|
71
73
|
preview?: (hex: string) => string;
|
|
72
74
|
renderSwatch?: ProfileSwatchRenderer;
|
|
@@ -265,4 +267,4 @@ declare function loadSharedProfiles(groupId: string): Promise<Profile[]>;
|
|
|
265
267
|
declare function saveSharedProfiles(groupId: string, profiles: Profile[]): Promise<void>;
|
|
266
268
|
declare function subscribeSharedProfiles(groupId: string, listener: (profiles: Profile[]) => void): () => void;
|
|
267
269
|
|
|
268
|
-
export { type CreateProfileInput, MAX_PROFILE_NAME_LENGTH, MAX_TAG_LENGTH, OptionPicker, type OptionPickerOption, type Profile, ProfileChip, type ProfileColorPick, ProfileColorProvider, type ProfileEditPatch, type ProfileExtensionActions, type ProfileExtensionSlice, type ProfileExtensionState, ProfilePicker, type ProfilePickerAlignResult, type ProfilePickerColorSection, type ProfilePickerContentSize, type ProfileSelectionActions, type ProfileSelectionSlice, type ProfileSelectionState, type ProfileSwatchRenderer, ProfilesManager, type ProfilesManagerHandle, type ProfilesManagerProps, ProfilesScreen, type ProfilesScreenProps, type SeatColorsActions, type SeatColorsSlice, type SeatColorsState, type SelectProfilePayload, type SetLastGuestColorPayload, type SetProfileColorPayload, type SetProfileExtensionPayload, type UpdateProfilePayload, type UseSharedProfilesSyncOptions, type UseSharedProfilesSyncResult, avoidSeatColorClash, createProfileExtensionSlice, createProfileRecord, createProfileSelectionSlice, createSeatColorsSlice, getOptionPickerContentSize, getProfilePickerContentSize, isSharedProfileStoreAvailable, isValidProfile, isValidTag, loadSharedProfiles, profileSeatColor, profilesActions, profilesReducer, readSharedProfiles, resolveInitialProfiles, saveSharedProfiles, subscribeSharedProfiles, useProfileColorPreview, useProfileDisplayColor, useProfileSwatchRenderer, useSharedProfilesSync };
|
|
270
|
+
export { type CreateProfileInput, MAX_PROFILE_NAME_LENGTH, MAX_TAG_LENGTH, OptionPicker, type OptionPickerOption, type Profile, ProfileChip, type ProfileColorPick, ProfileColorProvider, type ProfileEditPatch, type ProfileExtensionActions, type ProfileExtensionSlice, type ProfileExtensionState, ProfilePicker, type ProfilePickerAlignResult, type ProfilePickerColorSection, type ProfilePickerContentSize, type ProfileSelectionActions, type ProfileSelectionSlice, type ProfileSelectionState, type ProfileSwatchPlace, type ProfileSwatchRenderer, ProfilesManager, type ProfilesManagerHandle, type ProfilesManagerProps, ProfilesScreen, type ProfilesScreenProps, type SeatColorsActions, type SeatColorsSlice, type SeatColorsState, type SelectProfilePayload, type SetLastGuestColorPayload, type SetProfileColorPayload, type SetProfileExtensionPayload, type UpdateProfilePayload, type UseSharedProfilesSyncOptions, type UseSharedProfilesSyncResult, avoidSeatColorClash, createProfileExtensionSlice, createProfileRecord, createProfileSelectionSlice, createSeatColorsSlice, getOptionPickerContentSize, getProfilePickerContentSize, isSharedProfileStoreAvailable, isValidProfile, isValidTag, loadSharedProfiles, profileSeatColor, profilesActions, profilesReducer, readSharedProfiles, resolveInitialProfiles, saveSharedProfiles, subscribeSharedProfiles, useProfileColorPreview, useProfileDisplayColor, useProfileSwatchRenderer, useSharedProfilesSync };
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ interface OptionPickerOption<T extends string> {
|
|
|
30
30
|
value: T;
|
|
31
31
|
label: string;
|
|
32
32
|
icon?: string;
|
|
33
|
+
color?: string;
|
|
33
34
|
}
|
|
34
35
|
declare function getOptionPickerContentSize(optionCount: number, colorSection?: Pick<ProfilePickerColorSection, 'swatches' | 'caption'>): ProfilePickerContentSize;
|
|
35
36
|
interface OptionPickerProps<T extends string> {
|
|
@@ -66,7 +67,8 @@ interface Props {
|
|
|
66
67
|
}
|
|
67
68
|
declare function ProfileChip({ profile, size, filled }: Props): react.JSX.Element;
|
|
68
69
|
|
|
69
|
-
type
|
|
70
|
+
type ProfileSwatchPlace = 'chip' | 'colorRow' | 'colorPage';
|
|
71
|
+
type ProfileSwatchRenderer = (color: string, size: number, place: ProfileSwatchPlace) => ReactNode;
|
|
70
72
|
interface ProfileColorProviderProps {
|
|
71
73
|
preview?: (hex: string) => string;
|
|
72
74
|
renderSwatch?: ProfileSwatchRenderer;
|
|
@@ -265,4 +267,4 @@ declare function loadSharedProfiles(groupId: string): Promise<Profile[]>;
|
|
|
265
267
|
declare function saveSharedProfiles(groupId: string, profiles: Profile[]): Promise<void>;
|
|
266
268
|
declare function subscribeSharedProfiles(groupId: string, listener: (profiles: Profile[]) => void): () => void;
|
|
267
269
|
|
|
268
|
-
export { type CreateProfileInput, MAX_PROFILE_NAME_LENGTH, MAX_TAG_LENGTH, OptionPicker, type OptionPickerOption, type Profile, ProfileChip, type ProfileColorPick, ProfileColorProvider, type ProfileEditPatch, type ProfileExtensionActions, type ProfileExtensionSlice, type ProfileExtensionState, ProfilePicker, type ProfilePickerAlignResult, type ProfilePickerColorSection, type ProfilePickerContentSize, type ProfileSelectionActions, type ProfileSelectionSlice, type ProfileSelectionState, type ProfileSwatchRenderer, ProfilesManager, type ProfilesManagerHandle, type ProfilesManagerProps, ProfilesScreen, type ProfilesScreenProps, type SeatColorsActions, type SeatColorsSlice, type SeatColorsState, type SelectProfilePayload, type SetLastGuestColorPayload, type SetProfileColorPayload, type SetProfileExtensionPayload, type UpdateProfilePayload, type UseSharedProfilesSyncOptions, type UseSharedProfilesSyncResult, avoidSeatColorClash, createProfileExtensionSlice, createProfileRecord, createProfileSelectionSlice, createSeatColorsSlice, getOptionPickerContentSize, getProfilePickerContentSize, isSharedProfileStoreAvailable, isValidProfile, isValidTag, loadSharedProfiles, profileSeatColor, profilesActions, profilesReducer, readSharedProfiles, resolveInitialProfiles, saveSharedProfiles, subscribeSharedProfiles, useProfileColorPreview, useProfileDisplayColor, useProfileSwatchRenderer, useSharedProfilesSync };
|
|
270
|
+
export { type CreateProfileInput, MAX_PROFILE_NAME_LENGTH, MAX_TAG_LENGTH, OptionPicker, type OptionPickerOption, type Profile, ProfileChip, type ProfileColorPick, ProfileColorProvider, type ProfileEditPatch, type ProfileExtensionActions, type ProfileExtensionSlice, type ProfileExtensionState, ProfilePicker, type ProfilePickerAlignResult, type ProfilePickerColorSection, type ProfilePickerContentSize, type ProfileSelectionActions, type ProfileSelectionSlice, type ProfileSelectionState, type ProfileSwatchPlace, type ProfileSwatchRenderer, ProfilesManager, type ProfilesManagerHandle, type ProfilesManagerProps, ProfilesScreen, type ProfilesScreenProps, type SeatColorsActions, type SeatColorsSlice, type SeatColorsState, type SelectProfilePayload, type SetLastGuestColorPayload, type SetProfileColorPayload, type SetProfileExtensionPayload, type UpdateProfilePayload, type UseSharedProfilesSyncOptions, type UseSharedProfilesSyncResult, avoidSeatColorClash, createProfileExtensionSlice, createProfileRecord, createProfileSelectionSlice, createSeatColorsSlice, getOptionPickerContentSize, getProfilePickerContentSize, isSharedProfileStoreAvailable, isValidProfile, isValidTag, loadSharedProfiles, profileSeatColor, profilesActions, profilesReducer, readSharedProfiles, resolveInitialProfiles, saveSharedProfiles, subscribeSharedProfiles, useProfileColorPreview, useProfileDisplayColor, useProfileSwatchRenderer, useSharedProfilesSync };
|
package/dist/index.js
CHANGED
|
@@ -88,8 +88,11 @@ function useProfileDisplayColor(hex) {
|
|
|
88
88
|
// src/SwatchLayer.tsx
|
|
89
89
|
var import_react_native = require("react-native");
|
|
90
90
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
91
|
-
function
|
|
92
|
-
return
|
|
91
|
+
function drawSwatch(render, color, size, place) {
|
|
92
|
+
return render ? render(color, size, place) ?? null : null;
|
|
93
|
+
}
|
|
94
|
+
function SwatchLayer({ size, children }) {
|
|
95
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native.View, { style: [styles.layer, { borderRadius: size / 2, height: size, width: size }], children });
|
|
93
96
|
}
|
|
94
97
|
var styles = import_react_native.StyleSheet.create({
|
|
95
98
|
layer: {
|
|
@@ -106,10 +109,11 @@ var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
|
106
109
|
var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
|
|
107
110
|
function IdentityChip({ color, tag, icon, size, filled = true }) {
|
|
108
111
|
const renderSwatch = useProfileSwatchRenderer();
|
|
112
|
+
const drawing = filled ? drawSwatch(renderSwatch, color, size, "chip") : null;
|
|
109
113
|
const contrastColor = (0, import_auto_paper.getContrastColor)(color);
|
|
110
114
|
const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : tag && Array.from(tag).length >= 3 ? 0.32 : 0.4);
|
|
111
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { style: [styles2.chip, { borderRadius: size / 2, height: size, width: size }, filled &&
|
|
112
|
-
|
|
115
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { style: [styles2.chip, { borderRadius: size / 2, height: size, width: size }, filled && drawing === null && { backgroundColor: color }], children: [
|
|
116
|
+
drawing !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SwatchLayer, { size, children: drawing }),
|
|
113
117
|
tag ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper.Text, { style: [styles2.tag, { color: contrastColor, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper.Icon, { source: icon, size: size * 0.6, color: contrastColor })
|
|
114
118
|
] });
|
|
115
119
|
}
|
|
@@ -210,10 +214,10 @@ function PickerMenu({ id, host, color, dark, label, chip, colorSection, homeColo
|
|
|
210
214
|
) })
|
|
211
215
|
] });
|
|
212
216
|
}
|
|
213
|
-
function IconRow({ icon, label, selected, color, fg, disabled = false, onPress }) {
|
|
217
|
+
function IconRow({ icon, iconColor, label, selected, color, fg, disabled = false, onPress }) {
|
|
214
218
|
const textColor = selected ? (0, import_auto_paper2.getContrastColor)(color) : fg;
|
|
215
219
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { disabled, onPress, style: [pickerMenuStyles.row, selected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: [pickerMenuStyles.rowInner, disabled && pickerMenuStyles.rowDisabled], children: [
|
|
216
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: styles3.iconSlot, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: icon, size: 13, color: textColor }) }),
|
|
220
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: styles3.iconSlot, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: icon, size: 13, color: selected ? textColor : iconColor ?? textColor }) }),
|
|
217
221
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [pickerMenuStyles.rowLabel, { color: textColor }], numberOfLines: 1, children: label })
|
|
218
222
|
] }) });
|
|
219
223
|
}
|
|
@@ -221,9 +225,10 @@ function ColorRow({ section, fg, dividerColor, onPress }) {
|
|
|
221
225
|
const { value, previewValue, label = "Color", caption } = section;
|
|
222
226
|
const renderSwatch = useProfileSwatchRenderer();
|
|
223
227
|
const drawn = previewValue ? previewValue(value) : value;
|
|
228
|
+
const drawing = drawSwatch(renderSwatch, drawn, COLOR_ROW_SWATCH_SIZE, "colorRow");
|
|
224
229
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
225
230
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress, style: [pickerMenuStyles.row, { height: colorRowHeight(caption) }], accessibilityRole: "button", accessibilityLabel: caption ? `${label}, ${caption}` : label, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: pickerMenuStyles.rowInner, children: [
|
|
226
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles3.colorRowSwatch,
|
|
231
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles3.colorRowSwatch, drawing === null && { backgroundColor: drawn }], children: drawing !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SwatchLayer, { size: COLOR_ROW_SWATCH_SIZE, children: drawing }) }),
|
|
227
232
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: styles3.colorRowText, children: [
|
|
228
233
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles3.colorRowLabel, { color: fg }], numberOfLines: 1, children: label }),
|
|
229
234
|
caption ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles3.colorRowCaption, { color: fg }], numberOfLines: 1, children: caption }) : null
|
|
@@ -250,9 +255,11 @@ function ColorPage({ section, homeColor, fg, onBack }) {
|
|
|
250
255
|
const swatchDisplay = displayFor(swatch.value);
|
|
251
256
|
const contrast = (0, import_auto_paper2.getContrastColor)(swatchDisplay);
|
|
252
257
|
const accessibilityLabel = swappable ? `${swatch.label}, swap` : taken ? `${swatch.label}, taken` : isHomeColor ? `${swatch.label}, profile color` : swatch.label;
|
|
258
|
+
const drawnSize = selected ? SWATCH_SIZE - 2 * SWATCH_RING_WIDTH : SWATCH_SIZE;
|
|
259
|
+
const drawing = drawSwatch(renderSwatch, swatchDisplay, drawnSize, "colorPage");
|
|
253
260
|
const mark = selected ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "check", size: 16, color: contrast }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "swap-horizontal", size: 16, color: contrast }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "close", size: 16, color: contrast }) : isHomeColor ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles3.homeDot, { backgroundColor: contrast }] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, {});
|
|
254
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { disabled: taken && !swappable, onPress: () => onChange(swatch.value), borderless: true, style: [styles3.swatch,
|
|
255
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SwatchLayer, {
|
|
261
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { disabled: taken && !swappable, onPress: () => onChange(swatch.value), borderless: true, style: [styles3.swatch, drawing === null && { backgroundColor: swatchDisplay }, selected && styles3.swatchSelected, taken && !swappable && styles3.swatchTaken], accessibilityRole: "button", accessibilityLabel, accessibilityState: { selected, disabled: taken && !swappable }, children: drawing !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: styles3.swatchFace, children: [
|
|
262
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SwatchLayer, { size: drawnSize, children: drawing }),
|
|
256
263
|
mark
|
|
257
264
|
] }) : mark }, swatch.value);
|
|
258
265
|
}) })
|
|
@@ -431,7 +438,7 @@ function OptionPicker({ id, host, options, value, onChange, color, dark, icon, r
|
|
|
431
438
|
onChange(option.value);
|
|
432
439
|
host.close();
|
|
433
440
|
};
|
|
434
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(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__ */ (0, import_jsx_runtime5.jsx)(IconRow, { icon: option.icon ?? rowIcon, label: option.label, selected: option.value === value, color, fg, onPress: () => handleSelect(option) }, option.value)) });
|
|
441
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(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__ */ (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) }, option.value)) });
|
|
435
442
|
}
|
|
436
443
|
|
|
437
444
|
// src/ProfileChip.tsx
|
package/dist/index.mjs
CHANGED
|
@@ -40,8 +40,11 @@ function useProfileDisplayColor(hex) {
|
|
|
40
40
|
// src/SwatchLayer.tsx
|
|
41
41
|
import { StyleSheet, View } from "react-native";
|
|
42
42
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
43
|
-
function
|
|
44
|
-
return
|
|
43
|
+
function drawSwatch(render, color, size, place) {
|
|
44
|
+
return render ? render(color, size, place) ?? null : null;
|
|
45
|
+
}
|
|
46
|
+
function SwatchLayer({ size, children }) {
|
|
47
|
+
return /* @__PURE__ */ jsx2(View, { style: [styles.layer, { borderRadius: size / 2, height: size, width: size }], children });
|
|
45
48
|
}
|
|
46
49
|
var styles = StyleSheet.create({
|
|
47
50
|
layer: {
|
|
@@ -58,10 +61,11 @@ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
|
58
61
|
var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
|
|
59
62
|
function IdentityChip({ color, tag, icon, size, filled = true }) {
|
|
60
63
|
const renderSwatch = useProfileSwatchRenderer();
|
|
64
|
+
const drawing = filled ? drawSwatch(renderSwatch, color, size, "chip") : null;
|
|
61
65
|
const contrastColor = getContrastColor(color);
|
|
62
66
|
const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : tag && Array.from(tag).length >= 3 ? 0.32 : 0.4);
|
|
63
|
-
return /* @__PURE__ */ jsxs(View2, { style: [styles2.chip, { borderRadius: size / 2, height: size, width: size }, filled &&
|
|
64
|
-
|
|
67
|
+
return /* @__PURE__ */ jsxs(View2, { style: [styles2.chip, { borderRadius: size / 2, height: size, width: size }, filled && drawing === null && { backgroundColor: color }], children: [
|
|
68
|
+
drawing !== null && /* @__PURE__ */ jsx3(SwatchLayer, { size, children: drawing }),
|
|
65
69
|
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
70
|
] });
|
|
67
71
|
}
|
|
@@ -162,10 +166,10 @@ function PickerMenu({ id, host, color, dark, label, chip, colorSection, homeColo
|
|
|
162
166
|
) })
|
|
163
167
|
] });
|
|
164
168
|
}
|
|
165
|
-
function IconRow({ icon, label, selected, color, fg, disabled = false, onPress }) {
|
|
169
|
+
function IconRow({ icon, iconColor, label, selected, color, fg, disabled = false, onPress }) {
|
|
166
170
|
const textColor = selected ? getContrastColor2(color) : fg;
|
|
167
171
|
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 }) }),
|
|
172
|
+
/* @__PURE__ */ jsx4(View3, { style: styles3.iconSlot, children: /* @__PURE__ */ jsx4(Icon2, { source: icon, size: 13, color: selected ? textColor : iconColor ?? textColor }) }),
|
|
169
173
|
/* @__PURE__ */ jsx4(Text2, { style: [pickerMenuStyles.rowLabel, { color: textColor }], numberOfLines: 1, children: label })
|
|
170
174
|
] }) });
|
|
171
175
|
}
|
|
@@ -173,9 +177,10 @@ function ColorRow({ section, fg, dividerColor, onPress }) {
|
|
|
173
177
|
const { value, previewValue, label = "Color", caption } = section;
|
|
174
178
|
const renderSwatch = useProfileSwatchRenderer();
|
|
175
179
|
const drawn = previewValue ? previewValue(value) : value;
|
|
180
|
+
const drawing = drawSwatch(renderSwatch, drawn, COLOR_ROW_SWATCH_SIZE, "colorRow");
|
|
176
181
|
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
177
182
|
/* @__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,
|
|
183
|
+
/* @__PURE__ */ jsx4(View3, { style: [styles3.colorRowSwatch, drawing === null && { backgroundColor: drawn }], children: drawing !== null && /* @__PURE__ */ jsx4(SwatchLayer, { size: COLOR_ROW_SWATCH_SIZE, children: drawing }) }),
|
|
179
184
|
/* @__PURE__ */ jsxs2(View3, { style: styles3.colorRowText, children: [
|
|
180
185
|
/* @__PURE__ */ jsx4(Text2, { style: [styles3.colorRowLabel, { color: fg }], numberOfLines: 1, children: label }),
|
|
181
186
|
caption ? /* @__PURE__ */ jsx4(Text2, { style: [styles3.colorRowCaption, { color: fg }], numberOfLines: 1, children: caption }) : null
|
|
@@ -202,9 +207,11 @@ function ColorPage({ section, homeColor, fg, onBack }) {
|
|
|
202
207
|
const swatchDisplay = displayFor(swatch.value);
|
|
203
208
|
const contrast = getContrastColor2(swatchDisplay);
|
|
204
209
|
const accessibilityLabel = swappable ? `${swatch.label}, swap` : taken ? `${swatch.label}, taken` : isHomeColor ? `${swatch.label}, profile color` : swatch.label;
|
|
210
|
+
const drawnSize = selected ? SWATCH_SIZE - 2 * SWATCH_RING_WIDTH : SWATCH_SIZE;
|
|
211
|
+
const drawing = drawSwatch(renderSwatch, swatchDisplay, drawnSize, "colorPage");
|
|
205
212
|
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,
|
|
207
|
-
/* @__PURE__ */ jsx4(SwatchLayer, {
|
|
213
|
+
return /* @__PURE__ */ jsx4(TouchableRipple, { disabled: taken && !swappable, onPress: () => onChange(swatch.value), borderless: true, style: [styles3.swatch, drawing === null && { backgroundColor: swatchDisplay }, selected && styles3.swatchSelected, taken && !swappable && styles3.swatchTaken], accessibilityRole: "button", accessibilityLabel, accessibilityState: { selected, disabled: taken && !swappable }, children: drawing !== null ? /* @__PURE__ */ jsxs2(View3, { style: styles3.swatchFace, children: [
|
|
214
|
+
/* @__PURE__ */ jsx4(SwatchLayer, { size: drawnSize, children: drawing }),
|
|
208
215
|
mark
|
|
209
216
|
] }) : mark }, swatch.value);
|
|
210
217
|
}) })
|
|
@@ -383,7 +390,7 @@ function OptionPicker({ id, host, options, value, onChange, color, dark, icon, r
|
|
|
383
390
|
onChange(option.value);
|
|
384
391
|
host.close();
|
|
385
392
|
};
|
|
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)) });
|
|
393
|
+
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, iconColor: option.color, label: option.label, selected: option.value === value, color, fg, onPress: () => handleSelect(option) }, option.value)) });
|
|
387
394
|
}
|
|
388
395
|
|
|
389
396
|
// src/ProfileChip.tsx
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tastic/profile",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.2",
|
|
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",
|
|
@@ -66,12 +66,12 @@
|
|
|
66
66
|
"@infinitetoken/jest-config": "^0.5.2",
|
|
67
67
|
"@infinitetoken/tsconfig": "^0.6.0",
|
|
68
68
|
"@rific/auto-paper": "^0.13.3",
|
|
69
|
-
"@rific/core": "^0.
|
|
69
|
+
"@rific/core": "^0.4.0",
|
|
70
70
|
"@rific/feedback-press": "^0.13.0",
|
|
71
71
|
"@rific/focus-chain": "^0.4.8",
|
|
72
|
-
"@rific/toaster": "^0.8.
|
|
72
|
+
"@rific/toaster": "^0.8.3",
|
|
73
73
|
"@tastic/core": "^0.14.0",
|
|
74
|
-
"@tastic/hud": "^0.
|
|
74
|
+
"@tastic/hud": "^0.24.0",
|
|
75
75
|
"@testing-library/dom": "^10.4.1",
|
|
76
76
|
"@testing-library/react": "^16.3.2",
|
|
77
77
|
"@types/jest": "^30.0.0",
|
package/src/IdentityChip.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import { StyleSheet, View } from 'react-native'
|
|
|
3
3
|
import { Icon, Text } from 'react-native-paper'
|
|
4
4
|
|
|
5
5
|
import { useProfileSwatchRenderer } from './ProfileColorContext'
|
|
6
|
-
import { SwatchLayer } from './SwatchLayer'
|
|
6
|
+
import { drawSwatch, SwatchLayer } from './SwatchLayer'
|
|
7
7
|
|
|
8
8
|
// Presence check, not isValidTag's own full-string emoji validation (profilesValidation.ts) - this
|
|
9
9
|
// only decides a font-size ratio, so it doesn't need that check's ZWJ/variation-selector precision,
|
|
@@ -28,6 +28,7 @@ interface Props {
|
|
|
28
28
|
// place of the flat color (a filled chip only); the tag's contrast still comes from `color`.
|
|
29
29
|
export function IdentityChip({ color, tag, icon, size, filled = true }: Props) {
|
|
30
30
|
const renderSwatch = useProfileSwatchRenderer()
|
|
31
|
+
const drawing = filled ? drawSwatch(renderSwatch, color, size, 'chip') : null
|
|
31
32
|
const contrastColor = getContrastColor(color)
|
|
32
33
|
// An emoji glyph reads visually smaller than a bold letter at the same fontSize (the system emoji
|
|
33
34
|
// font leaves more of its own em-box empty), so a plain-text ratio that looks right for "J" still
|
|
@@ -39,8 +40,8 @@ export function IdentityChip({ color, tag, icon, size, filled = true }: Props) {
|
|
|
39
40
|
// them off there ("D..."); 0.32 fits them, with adjustsFontSizeToFit still the backstop on native.
|
|
40
41
|
const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : tag && Array.from(tag).length >= 3 ? 0.32 : 0.4)
|
|
41
42
|
return (
|
|
42
|
-
<View style={[styles.chip, { borderRadius: size / 2, height: size, width: size }, filled &&
|
|
43
|
-
{
|
|
43
|
+
<View style={[styles.chip, { borderRadius: size / 2, height: size, width: size }, filled && drawing === null && { backgroundColor: color }]}>
|
|
44
|
+
{drawing !== null && <SwatchLayer size={size}>{drawing}</SwatchLayer>}
|
|
44
45
|
{tag ? (
|
|
45
46
|
<Text style={[styles.tag, { color: contrastColor, fontSize: tagFontSize }]} numberOfLines={1} adjustsFontSizeToFit>
|
|
46
47
|
{tag}
|
package/src/OptionPicker.tsx
CHANGED
|
@@ -7,6 +7,10 @@ export interface OptionPickerOption<T extends string> {
|
|
|
7
7
|
value: T
|
|
8
8
|
label: string
|
|
9
9
|
icon?: string
|
|
10
|
+
// The option's own color (e.g. a difficulty's), for its row's icon while the row isn't the selected
|
|
11
|
+
// one; the selected row still fills with the picker's `color`, its icon contrasting as always.
|
|
12
|
+
// Without it the icon takes the row's text color.
|
|
13
|
+
color?: string
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
// The popover's content footprint: one row per option, plus (with a `colorSection`) the Color row,
|
|
@@ -57,7 +61,7 @@ export function OptionPicker<T extends string>({ id, host, options, value, onCha
|
|
|
57
61
|
return (
|
|
58
62
|
<PickerMenu id={id} host={host} color={color} dark={dark} label={selected?.label ?? ''} chip={{ icon }} colorSection={colorSection} contentSize={getOptionPickerContentSize(options.length, colorSection)} align={align} alignOverride={alignOverride} rotation={rotation} triggerStyle={triggerStyle}>
|
|
59
63
|
{options.map((option) => (
|
|
60
|
-
<IconRow key={option.value} icon={option.icon ?? rowIcon} label={option.label} selected={option.value === value} color={color} fg={fg} onPress={() => handleSelect(option)} />
|
|
64
|
+
<IconRow key={option.value} icon={option.icon ?? rowIcon} iconColor={option.color} label={option.label} selected={option.value === value} color={color} fg={fg} onPress={() => handleSelect(option)} />
|
|
61
65
|
))}
|
|
62
66
|
</PickerMenu>
|
|
63
67
|
)
|
package/src/PickerMenu.tsx
CHANGED
|
@@ -8,7 +8,7 @@ import { Icon, Text } from 'react-native-paper'
|
|
|
8
8
|
|
|
9
9
|
import { IdentityChip } from './IdentityChip'
|
|
10
10
|
import { useProfileSwatchRenderer } from './ProfileColorContext'
|
|
11
|
-
import { SwatchLayer } from './SwatchLayer'
|
|
11
|
+
import { drawSwatch, SwatchLayer } from './SwatchLayer'
|
|
12
12
|
|
|
13
13
|
// The trigger and popover ProfilePicker and OptionPicker share: a name trigger (or the chip pill),
|
|
14
14
|
// a bordered menu of rows, and with a `colorSection` a Color row that swaps the menu for a page of
|
|
@@ -231,6 +231,9 @@ export function PickerMenu({ id, host, color, dark, label, chip, colorSection, h
|
|
|
231
231
|
|
|
232
232
|
interface IconRowProps {
|
|
233
233
|
icon: string
|
|
234
|
+
// The icon's color while the row isn't selected (an OptionPicker option's own `color`). Defaults to
|
|
235
|
+
// `fg`, like the label.
|
|
236
|
+
iconColor?: string
|
|
234
237
|
label: string
|
|
235
238
|
// Fills the row with `color` (its icon and label then contrast against it).
|
|
236
239
|
selected: boolean
|
|
@@ -244,7 +247,7 @@ interface IconRowProps {
|
|
|
244
247
|
// A selected row's own background fills entirely with its identity color - matching how a selected
|
|
245
248
|
// row reads elsewhere (see @tastic/hud's SectionedDropdown) rather than a checkmark bolted onto an
|
|
246
249
|
// otherwise-plain row.
|
|
247
|
-
export function IconRow({ icon, label, selected, color, fg, disabled = false, onPress }: IconRowProps) {
|
|
250
|
+
export function IconRow({ icon, iconColor, label, selected, color, fg, disabled = false, onPress }: IconRowProps) {
|
|
248
251
|
const textColor = selected ? getContrastColor(color) : fg
|
|
249
252
|
return (
|
|
250
253
|
<TouchableRipple disabled={disabled} onPress={onPress} style={[pickerMenuStyles.row, selected && { backgroundColor: color }]}>
|
|
@@ -253,7 +256,7 @@ export function IconRow({ icon, label, selected, color, fg, disabled = false, on
|
|
|
253
256
|
circle, but still the same fixed-size slot every row's icon/chip sits in (see ProfileChip's
|
|
254
257
|
own doc), so this row's icon stays lined up with every other row regardless of selection. */}
|
|
255
258
|
<View style={styles.iconSlot}>
|
|
256
|
-
<Icon source={icon} size={13} color={textColor} />
|
|
259
|
+
<Icon source={icon} size={13} color={selected ? textColor : (iconColor ?? textColor)} />
|
|
257
260
|
</View>
|
|
258
261
|
<Text style={[pickerMenuStyles.rowLabel, { color: textColor }]} numberOfLines={1}>
|
|
259
262
|
{label}
|
|
@@ -269,12 +272,13 @@ function ColorRow({ section, fg, dividerColor, onPress }: { section: ProfilePick
|
|
|
269
272
|
const { value, previewValue, label = 'Color', caption } = section
|
|
270
273
|
const renderSwatch = useProfileSwatchRenderer()
|
|
271
274
|
const drawn = previewValue ? previewValue(value) : value
|
|
275
|
+
const drawing = drawSwatch(renderSwatch, drawn, COLOR_ROW_SWATCH_SIZE, 'colorRow')
|
|
272
276
|
return (
|
|
273
277
|
<>
|
|
274
278
|
<TouchableRipple onPress={onPress} style={[pickerMenuStyles.row, { height: colorRowHeight(caption) }]} accessibilityRole='button' accessibilityLabel={caption ? `${label}, ${caption}` : label}>
|
|
275
279
|
<View style={pickerMenuStyles.rowInner}>
|
|
276
280
|
{/* A ProfileColorProvider renderSwatch draws it in place of the flat fill. */}
|
|
277
|
-
<View style={[styles.colorRowSwatch,
|
|
281
|
+
<View style={[styles.colorRowSwatch, drawing === null && { backgroundColor: drawn }]}>{drawing !== null && <SwatchLayer size={COLOR_ROW_SWATCH_SIZE}>{drawing}</SwatchLayer>}</View>
|
|
278
282
|
<View style={styles.colorRowText}>
|
|
279
283
|
<Text style={[styles.colorRowLabel, { color: fg }]} numberOfLines={1}>
|
|
280
284
|
{label}
|
|
@@ -326,15 +330,17 @@ function ColorPage({ section, homeColor, fg, onBack }: { section: ProfilePickerC
|
|
|
326
330
|
const swatchDisplay = displayFor(swatch.value)
|
|
327
331
|
const contrast = getContrastColor(swatchDisplay)
|
|
328
332
|
const accessibilityLabel = swappable ? `${swatch.label}, swap` : taken ? `${swatch.label}, taken` : isHomeColor ? `${swatch.label}, profile color` : swatch.label
|
|
333
|
+
const drawnSize = selected ? SWATCH_SIZE - 2 * SWATCH_RING_WIDTH : SWATCH_SIZE
|
|
334
|
+
const drawing = drawSwatch(renderSwatch, swatchDisplay, drawnSize, 'colorPage')
|
|
329
335
|
const mark = selected ? <Icon source='check' size={16} color={contrast} /> : swappable ? <Icon source='swap-horizontal' size={16} color={contrast} /> : taken ? <Icon source='close' size={16} color={contrast} /> : isHomeColor ? <View style={[styles.homeDot, { backgroundColor: contrast }]} /> : <View />
|
|
330
336
|
// The same swatch as @tastic/hud's InlineColorPicker: a filled circle, the selected one
|
|
331
337
|
// ringed and checked, a taken one faded with an X. TouchableRipple takes one child, so a
|
|
332
338
|
// drawn swatch and its mark share a face that fills the circle inside the ring.
|
|
333
339
|
return (
|
|
334
|
-
<TouchableRipple key={swatch.value} disabled={taken && !swappable} onPress={() => onChange(swatch.value)} borderless style={[styles.swatch,
|
|
335
|
-
{
|
|
340
|
+
<TouchableRipple key={swatch.value} disabled={taken && !swappable} onPress={() => onChange(swatch.value)} borderless style={[styles.swatch, drawing === null && { backgroundColor: swatchDisplay }, selected && styles.swatchSelected, taken && !swappable && styles.swatchTaken]} accessibilityRole='button' accessibilityLabel={accessibilityLabel} accessibilityState={{ selected, disabled: taken && !swappable }}>
|
|
341
|
+
{drawing !== null ? (
|
|
336
342
|
<View style={styles.swatchFace}>
|
|
337
|
-
<SwatchLayer
|
|
343
|
+
<SwatchLayer size={drawnSize}>{drawing}</SwatchLayer>
|
|
338
344
|
{mark}
|
|
339
345
|
</View>
|
|
340
346
|
) : (
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { createContext, type ReactNode, useContext, useMemo } from 'react'
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// the
|
|
6
|
-
|
|
3
|
+
// Where a swatch is drawn: an IdentityChip (ProfileChip, the pickers' chip trigger), a picker's Color
|
|
4
|
+
// row, or a swatch on its color page. An app can tell an identity (a chip, which wants a legible mark
|
|
5
|
+
// on one color) from a choice of color (the Color row and page, where previewing more of what the
|
|
6
|
+
// color gives is the point): Hangman draws its triad pie only on the latter.
|
|
7
|
+
export type ProfileSwatchPlace = 'chip' | 'colorRow' | 'colorPage'
|
|
8
|
+
|
|
9
|
+
// Draws one color's swatch fill: a circle `size` across, standing in for the flat fill, at `place`.
|
|
10
|
+
// `color` is the drawn color (after `preview`), and it stays the primary one: the tag, icon, check or
|
|
11
|
+
// dot on top of the swatch takes its contrast from it. Returning null or undefined keeps that swatch's
|
|
12
|
+
// flat fill.
|
|
13
|
+
export type ProfileSwatchRenderer = (color: string, size: number, place: ProfileSwatchPlace) => ReactNode
|
|
7
14
|
|
|
8
15
|
interface ProfileColorContextValue {
|
|
9
16
|
preview?: (hex: string) => string
|
|
@@ -17,9 +24,11 @@ interface ProfileColorProviderProps {
|
|
|
17
24
|
// toward its card-back palette so they read muted, not as the raw swatch; a context rather than a
|
|
18
25
|
// module-level setting because that blend depends on light/dark mode.
|
|
19
26
|
preview?: (hex: string) => string
|
|
20
|
-
// Replaces the flat fill of
|
|
21
|
-
// pickers' chip trigger), the pickers' Color row swatch and their color page swatches
|
|
22
|
-
//
|
|
27
|
+
// Replaces the flat fill of the round swatches this package draws: IdentityChip (ProfileChip and the
|
|
28
|
+
// pickers' chip trigger), the pickers' Color row swatch and their color page swatches, each told
|
|
29
|
+
// where it is (ProfileSwatchPlace) and kept flat where it returns null. Hangman draws a pie of each
|
|
30
|
+
// color's triad on the Color row and page only. The renderer is drawn behind the swatch's mark,
|
|
31
|
+
// clipped to the circle.
|
|
23
32
|
// Rows that fill with a color (a selected picker row) stay flat, and so does hud's InlineColorPicker
|
|
24
33
|
// (ProfilesManager's color trigger and swatches), which doesn't read this context.
|
|
25
34
|
renderSwatch?: ProfileSwatchRenderer
|
package/src/SwatchLayer.tsx
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
|
+
import { type ReactNode } from 'react'
|
|
1
2
|
import { StyleSheet, View } from 'react-native'
|
|
2
3
|
|
|
3
|
-
import { type ProfileSwatchRenderer } from './ProfileColorContext'
|
|
4
|
+
import { type ProfileSwatchPlace, type ProfileSwatchRenderer } from './ProfileColorContext'
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
|
-
render: ProfileSwatchRenderer
|
|
7
|
-
// The drawn color, handed to `render` as is.
|
|
8
|
-
color: string
|
|
9
7
|
// The circle's diameter: the swatch's own size, less any ring drawn inside it.
|
|
10
8
|
size: number
|
|
9
|
+
// What the renderer drew (see drawSwatch).
|
|
10
|
+
children: ReactNode
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// What a ProfileColorProvider `renderSwatch` draws for one swatch, or null for the flat fill: there's
|
|
14
|
+
// no renderer, or it returned null or undefined for this place.
|
|
15
|
+
export function drawSwatch(render: ProfileSwatchRenderer | undefined, color: string, size: number, place: ProfileSwatchPlace): ReactNode {
|
|
16
|
+
return render ? (render(color, size, place) ?? null) : null
|
|
11
17
|
}
|
|
12
18
|
|
|
13
19
|
// A ProfileColorProvider `renderSwatch` drawing in place of a swatch's flat fill: absolutely placed at
|
|
14
20
|
// the swatch's top left (inside any border), behind the mark drawn after it, and clipped to the circle
|
|
15
21
|
// so a square drawing still reads as the round swatch. Not exported from index.ts.
|
|
16
|
-
export function SwatchLayer({
|
|
17
|
-
return <View style={[styles.layer, { borderRadius: size / 2, height: size, width: size }]}>{
|
|
22
|
+
export function SwatchLayer({ size, children }: Props) {
|
|
23
|
+
return <View style={[styles.layer, { borderRadius: size / 2, height: size, width: size }]}>{children}</View>
|
|
18
24
|
}
|
|
19
25
|
|
|
20
26
|
const styles = StyleSheet.create({
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { getOptionPickerContentSize, OptionPicker, type OptionPickerOption } from './OptionPicker'
|
|
2
2
|
export { ProfileChip } from './ProfileChip'
|
|
3
|
-
export { ProfileColorProvider, type ProfileSwatchRenderer, useProfileColorPreview, useProfileDisplayColor, useProfileSwatchRenderer } from './ProfileColorContext'
|
|
3
|
+
export { ProfileColorProvider, type ProfileSwatchPlace, type ProfileSwatchRenderer, useProfileColorPreview, useProfileDisplayColor, useProfileSwatchRenderer } from './ProfileColorContext'
|
|
4
4
|
export { getProfilePickerContentSize, ProfilePicker, type ProfilePickerAlignResult, type ProfilePickerColorSection, type ProfilePickerContentSize } from './ProfilePicker'
|
|
5
5
|
export { type ProfileEditPatch, ProfilesManager, type ProfilesManagerHandle, type ProfilesManagerProps } from './ProfilesManager'
|
|
6
6
|
export { ProfilesScreen, type ProfilesScreenProps } from './ProfilesScreen'
|