@tastic/hud 0.1.0 → 0.1.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/dist/{TriggerGauge-HIPRGEWJ.mjs → TriggerGauge-XBKZ3KER.mjs} +9 -1
- package/dist/index.js +42 -35
- package/dist/index.mjs +22 -13
- package/package.json +1 -1
- package/src/ReadyButton.tsx +7 -3
- package/src/SectionedDropdown.tsx +18 -9
- package/src/TriggerGauge.tsx +11 -2
- package/src/TriggerGaugeHost.tsx +3 -4
- package/src/loadSkiaWeb.ts +8 -12
- package/src/loadSkiaWeb.web.ts +11 -0
|
@@ -100,7 +100,15 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
|
|
|
100
100
|
return path;
|
|
101
101
|
}, [segments, litIndicesKey, transitionFromKey, singleSelect, step, startDeg, dashAngleDeg, totalDurationMs, oval.x, oval.y, oval.width, oval.height]);
|
|
102
102
|
if (segments < 2) return null;
|
|
103
|
-
|
|
103
|
+
const gaugeSizeStyle = {
|
|
104
|
+
height: size,
|
|
105
|
+
width: size
|
|
106
|
+
};
|
|
107
|
+
const canvasSizeStyle = {
|
|
108
|
+
height: canvasSize,
|
|
109
|
+
width: canvasSize
|
|
110
|
+
};
|
|
111
|
+
return /* @__PURE__ */ jsx(View, { style: [styles.container, gaugeSizeStyle], pointerEvents: "none", children: /* @__PURE__ */ jsxs(Canvas, { style: canvasSizeStyle, children: [
|
|
104
112
|
/* @__PURE__ */ jsx(Path, { path: unlitPath, style: "stroke", strokeWidth: UNLIT_THICKNESS, strokeCap: "round", color: mutedColor, opacity: 0.35 }),
|
|
105
113
|
/* @__PURE__ */ jsx(Path, { path: litPath, style: "stroke", strokeWidth: LIT_THICKNESS, strokeCap: "round", color: accentColor, opacity: 1 })
|
|
106
114
|
] }) });
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __esm = (fn, res) => function __init() {
|
|
9
7
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
@@ -20,14 +18,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
20
18
|
}
|
|
21
19
|
return to;
|
|
22
20
|
};
|
|
23
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
-
mod
|
|
30
|
-
));
|
|
31
21
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
22
|
|
|
33
23
|
// src/TriggerGauge.tsx
|
|
@@ -122,18 +112,26 @@ function TriggerGauge({ segments, litIndices, size, accentColor, mutedColor, das
|
|
|
122
112
|
return path;
|
|
123
113
|
}, [segments, litIndicesKey, transitionFromKey, singleSelect, step, startDeg, dashAngleDeg, totalDurationMs, oval.x, oval.y, oval.width, oval.height]);
|
|
124
114
|
if (segments < 2) return null;
|
|
125
|
-
|
|
115
|
+
const gaugeSizeStyle = {
|
|
116
|
+
height: size,
|
|
117
|
+
width: size
|
|
118
|
+
};
|
|
119
|
+
const canvasSizeStyle = {
|
|
120
|
+
height: canvasSize,
|
|
121
|
+
width: canvasSize
|
|
122
|
+
};
|
|
123
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.View, { style: [styles4.container, gaugeSizeStyle], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native_skia.Canvas, { style: canvasSizeStyle, children: [
|
|
126
124
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_skia.Path, { path: unlitPath, style: "stroke", strokeWidth: UNLIT_THICKNESS, strokeCap: "round", color: mutedColor, opacity: 0.35 }),
|
|
127
125
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_skia.Path, { path: litPath, style: "stroke", strokeWidth: LIT_THICKNESS, strokeCap: "round", color: accentColor, opacity: 1 })
|
|
128
126
|
] }) });
|
|
129
127
|
}
|
|
130
|
-
var import_react_native_skia, import_react2,
|
|
128
|
+
var import_react_native_skia, import_react2, import_react_native7, import_react_native_reanimated, import_jsx_runtime5, UNLIT_THICKNESS, LIT_THICKNESS, DASH_GAP_PX, ARC_GAP, DEFAULT_START_DEG, MIN_DASH_ANGLE_DEG, MOUNT_DURATION_MS, MOUNT_STAGGER_MS, SELECTION_TRANSITION_DURATION_MS, styles4;
|
|
131
129
|
var init_TriggerGauge = __esm({
|
|
132
130
|
"src/TriggerGauge.tsx"() {
|
|
133
131
|
"use strict";
|
|
134
132
|
import_react_native_skia = require("@shopify/react-native-skia");
|
|
135
133
|
import_react2 = require("react");
|
|
136
|
-
|
|
134
|
+
import_react_native7 = require("react-native");
|
|
137
135
|
import_react_native_reanimated = require("react-native-reanimated");
|
|
138
136
|
import_jsx_runtime5 = require("react/jsx-runtime");
|
|
139
137
|
UNLIT_THICKNESS = 3;
|
|
@@ -145,7 +143,7 @@ var init_TriggerGauge = __esm({
|
|
|
145
143
|
MOUNT_DURATION_MS = 450;
|
|
146
144
|
MOUNT_STAGGER_MS = 40;
|
|
147
145
|
SELECTION_TRANSITION_DURATION_MS = 260;
|
|
148
|
-
styles4 =
|
|
146
|
+
styles4 = import_react_native7.StyleSheet.create({
|
|
149
147
|
container: {
|
|
150
148
|
alignItems: "center",
|
|
151
149
|
justifyContent: "center",
|
|
@@ -398,29 +396,30 @@ var styles2 = import_react_native4.StyleSheet.create({
|
|
|
398
396
|
});
|
|
399
397
|
|
|
400
398
|
// src/loadSkiaWeb.ts
|
|
401
|
-
var import_react_native5 = require("react-native");
|
|
402
399
|
function loadSkiaWeb() {
|
|
403
|
-
|
|
404
|
-
return import("@shopify/react-native-skia/src/web").then((m) => m.LoadSkiaWeb());
|
|
400
|
+
return Promise.resolve();
|
|
405
401
|
}
|
|
406
402
|
|
|
407
403
|
// src/PressAwayOverlay.tsx
|
|
408
|
-
var
|
|
404
|
+
var import_react_native5 = require("react-native");
|
|
409
405
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
410
406
|
function PressAwayOverlay({ active, onPress, style }) {
|
|
411
407
|
if (!active) return null;
|
|
412
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
408
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [import_react_native5.StyleSheet.absoluteFill, style] });
|
|
413
409
|
}
|
|
414
410
|
|
|
415
411
|
// src/ReadyButton.tsx
|
|
416
412
|
var import_feedback_press2 = require("@rific/feedback-press");
|
|
417
|
-
var
|
|
413
|
+
var import_react_native6 = require("react-native");
|
|
418
414
|
var import_react_native_paper2 = require("react-native-paper");
|
|
419
415
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
420
416
|
function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
|
|
421
|
-
|
|
417
|
+
const readyButtonStyle = [styles3.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
|
|
418
|
+
const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
|
|
419
|
+
const labelColorStyle = { color: ready ? "#000000" : color };
|
|
420
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press2.TouchableRipple, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
|
|
422
421
|
}
|
|
423
|
-
var styles3 =
|
|
422
|
+
var styles3 = import_react_native6.StyleSheet.create({
|
|
424
423
|
readyButton: {
|
|
425
424
|
alignItems: "center",
|
|
426
425
|
borderRadius: 12,
|
|
@@ -435,7 +434,7 @@ var styles3 = import_react_native7.StyleSheet.create({
|
|
|
435
434
|
// src/SectionedDropdown.tsx
|
|
436
435
|
var import_auto_paper2 = require("@rific/auto-paper");
|
|
437
436
|
var import_feedback_press3 = require("@rific/feedback-press");
|
|
438
|
-
var
|
|
437
|
+
var import_react_native8 = require("react-native");
|
|
439
438
|
var import_react_native_paper3 = require("react-native-paper");
|
|
440
439
|
|
|
441
440
|
// src/TriggerGaugeHost.tsx
|
|
@@ -493,13 +492,16 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
|
|
|
493
492
|
}
|
|
494
493
|
gaugeOffset += section.options.length;
|
|
495
494
|
}
|
|
496
|
-
|
|
497
|
-
|
|
495
|
+
const anchorStyle = [styles5.anchor, open && styles5.anchorOpen];
|
|
496
|
+
const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
|
|
497
|
+
const dividerStyle = { backgroundColor: menuBorderColor };
|
|
498
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: anchorStyle, children: [
|
|
499
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { ref: triggerRef, collapsable: false, style: styles5.triggerBox, children: [
|
|
498
500
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
|
|
499
501
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press3.IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
|
|
500
502
|
] }),
|
|
501
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
502
|
-
sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
503
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native8.ScrollView, { style: [styles5.menu, menuStyle], contentContainerStyle: styles5.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { children: [
|
|
504
|
+
sectionIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native8.View, { style: [styles5.divider, dividerStyle] }),
|
|
503
505
|
section.kind === "single" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
504
506
|
SingleSection,
|
|
505
507
|
{
|
|
@@ -528,11 +530,14 @@ function SingleSection({
|
|
|
528
530
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: section.options.map((option) => {
|
|
529
531
|
const selected = option.value === section.value;
|
|
530
532
|
const rowColor = selected ? onAccentColor : mutedColor;
|
|
531
|
-
|
|
533
|
+
const itemStyle = [styles5.item, selected && { backgroundColor: accentColor }];
|
|
534
|
+
const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
|
|
535
|
+
const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
|
|
536
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press3.TouchableRipple, { onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: styles5.itemRow, children: [
|
|
532
537
|
option.icon && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper3.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
|
|
533
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
534
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper3.Text, { variant: "labelLarge", style:
|
|
535
|
-
option.description && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper3.Text, { variant: "labelSmall", style:
|
|
538
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: styles5.itemLabelColumn, children: [
|
|
539
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper3.Text, { variant: "labelLarge", style: labelStyle, children: option.label }),
|
|
540
|
+
option.description && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper3.Text, { variant: "labelSmall", style: descriptionStyle, children: option.description })
|
|
536
541
|
] })
|
|
537
542
|
] }) }, option.value);
|
|
538
543
|
}) });
|
|
@@ -552,15 +557,17 @@ function MultiSection({
|
|
|
552
557
|
const rowColor = selected ? onAccentColor : mutedColor;
|
|
553
558
|
const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
|
|
554
559
|
const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
|
|
555
|
-
|
|
560
|
+
const itemStyle = [styles5.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
|
|
561
|
+
const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
|
|
562
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press3.TouchableRipple, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: styles5.itemRow, children: [
|
|
556
563
|
option.icon && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper3.Icon, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
|
|
557
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper3.Text, { variant: "labelLarge", style: [styles5.itemLabelFlex,
|
|
564
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper3.Text, { variant: "labelLarge", style: [styles5.itemLabelFlex, labelStyle], children: option.label })
|
|
558
565
|
] }) }, option.value);
|
|
559
566
|
}),
|
|
560
567
|
section.allClear && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press3.TouchableRipple, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles5.item, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper3.Text, { variant: "labelLarge", style: [styles5.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
|
|
561
568
|
] });
|
|
562
569
|
}
|
|
563
|
-
var styles5 =
|
|
570
|
+
var styles5 = import_react_native8.StyleSheet.create({
|
|
564
571
|
allClearLabel: {
|
|
565
572
|
fontWeight: "bold",
|
|
566
573
|
textAlign: "center"
|
|
@@ -577,7 +584,7 @@ var styles5 = import_react_native9.StyleSheet.create({
|
|
|
577
584
|
zIndex: 100
|
|
578
585
|
},
|
|
579
586
|
divider: {
|
|
580
|
-
height:
|
|
587
|
+
height: import_react_native8.StyleSheet.hairlineWidth,
|
|
581
588
|
marginVertical: 4
|
|
582
589
|
},
|
|
583
590
|
item: {
|
package/dist/index.mjs
CHANGED
|
@@ -225,10 +225,8 @@ var styles2 = StyleSheet2.create({
|
|
|
225
225
|
});
|
|
226
226
|
|
|
227
227
|
// src/loadSkiaWeb.ts
|
|
228
|
-
import { Platform as Platform2 } from "react-native";
|
|
229
228
|
function loadSkiaWeb() {
|
|
230
|
-
|
|
231
|
-
return import("@shopify/react-native-skia/src/web").then((m) => m.LoadSkiaWeb());
|
|
229
|
+
return Promise.resolve();
|
|
232
230
|
}
|
|
233
231
|
|
|
234
232
|
// src/PressAwayOverlay.tsx
|
|
@@ -245,7 +243,10 @@ import { StyleSheet as StyleSheet4 } from "react-native";
|
|
|
245
243
|
import { Text } from "react-native-paper";
|
|
246
244
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
247
245
|
function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
|
|
248
|
-
|
|
246
|
+
const readyButtonStyle = [styles3.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
|
|
247
|
+
const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
|
|
248
|
+
const labelColorStyle = { color: ready ? "#000000" : color };
|
|
249
|
+
return /* @__PURE__ */ jsx4(TouchableRipple2, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ jsx4(Text, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
|
|
249
250
|
}
|
|
250
251
|
var styles3 = StyleSheet4.create({
|
|
251
252
|
readyButton: {
|
|
@@ -268,7 +269,7 @@ import { Icon as Icon2, Text as Text2 } from "react-native-paper";
|
|
|
268
269
|
// src/TriggerGaugeHost.tsx
|
|
269
270
|
import { lazy, Suspense } from "react";
|
|
270
271
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
271
|
-
var LazyTriggerGauge = lazy(() => loadSkiaWeb().then(() => import("./TriggerGauge-
|
|
272
|
+
var LazyTriggerGauge = lazy(() => loadSkiaWeb().then(() => import("./TriggerGauge-XBKZ3KER.mjs").then((m) => ({ default: m.TriggerGauge }))));
|
|
272
273
|
function TriggerGaugeHost(props) {
|
|
273
274
|
return /* @__PURE__ */ jsx5(Suspense, { fallback: null, children: /* @__PURE__ */ jsx5(LazyTriggerGauge, { ...props }) });
|
|
274
275
|
}
|
|
@@ -320,13 +321,16 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
|
|
|
320
321
|
}
|
|
321
322
|
gaugeOffset += section.options.length;
|
|
322
323
|
}
|
|
323
|
-
|
|
324
|
+
const anchorStyle = [styles4.anchor, open && styles4.anchorOpen];
|
|
325
|
+
const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
|
|
326
|
+
const dividerStyle = { backgroundColor: menuBorderColor };
|
|
327
|
+
return /* @__PURE__ */ jsxs3(View4, { style: anchorStyle, children: [
|
|
324
328
|
/* @__PURE__ */ jsxs3(View4, { ref: triggerRef, collapsable: false, style: styles4.triggerBox, children: [
|
|
325
329
|
/* @__PURE__ */ jsx6(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
|
|
326
330
|
/* @__PURE__ */ jsx6(IconButton, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
|
|
327
331
|
] }),
|
|
328
|
-
/* @__PURE__ */ jsx6(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ jsx6(ScrollView2, { style: [styles4.menu,
|
|
329
|
-
sectionIndex > 0 && /* @__PURE__ */ jsx6(View4, { style: [styles4.divider,
|
|
332
|
+
/* @__PURE__ */ jsx6(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ jsx6(ScrollView2, { style: [styles4.menu, menuStyle], contentContainerStyle: styles4.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs3(View4, { children: [
|
|
333
|
+
sectionIndex > 0 && /* @__PURE__ */ jsx6(View4, { style: [styles4.divider, dividerStyle] }),
|
|
330
334
|
section.kind === "single" ? /* @__PURE__ */ jsx6(
|
|
331
335
|
SingleSection,
|
|
332
336
|
{
|
|
@@ -355,11 +359,14 @@ function SingleSection({
|
|
|
355
359
|
return /* @__PURE__ */ jsx6(Fragment2, { children: section.options.map((option) => {
|
|
356
360
|
const selected = option.value === section.value;
|
|
357
361
|
const rowColor = selected ? onAccentColor : mutedColor;
|
|
358
|
-
|
|
362
|
+
const itemStyle = [styles4.item, selected && { backgroundColor: accentColor }];
|
|
363
|
+
const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
|
|
364
|
+
const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
|
|
365
|
+
return /* @__PURE__ */ jsx6(TouchableRipple3, { onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs3(View4, { style: styles4.itemRow, children: [
|
|
359
366
|
option.icon && /* @__PURE__ */ jsx6(Icon2, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
|
|
360
367
|
/* @__PURE__ */ jsxs3(View4, { style: styles4.itemLabelColumn, children: [
|
|
361
|
-
/* @__PURE__ */ jsx6(Text2, { variant: "labelLarge", style:
|
|
362
|
-
option.description && /* @__PURE__ */ jsx6(Text2, { variant: "labelSmall", style:
|
|
368
|
+
/* @__PURE__ */ jsx6(Text2, { variant: "labelLarge", style: labelStyle, children: option.label }),
|
|
369
|
+
option.description && /* @__PURE__ */ jsx6(Text2, { variant: "labelSmall", style: descriptionStyle, children: option.description })
|
|
363
370
|
] })
|
|
364
371
|
] }) }, option.value);
|
|
365
372
|
}) });
|
|
@@ -379,9 +386,11 @@ function MultiSection({
|
|
|
379
386
|
const rowColor = selected ? onAccentColor : mutedColor;
|
|
380
387
|
const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
|
|
381
388
|
const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
|
|
382
|
-
|
|
389
|
+
const itemStyle = [styles4.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
|
|
390
|
+
const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
|
|
391
|
+
return /* @__PURE__ */ jsx6(TouchableRipple3, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ jsxs3(View4, { style: styles4.itemRow, children: [
|
|
383
392
|
option.icon && /* @__PURE__ */ jsx6(Icon2, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
|
|
384
|
-
/* @__PURE__ */ jsx6(Text2, { variant: "labelLarge", style: [styles4.itemLabelFlex,
|
|
393
|
+
/* @__PURE__ */ jsx6(Text2, { variant: "labelLarge", style: [styles4.itemLabelFlex, labelStyle], children: option.label })
|
|
385
394
|
] }) }, option.value);
|
|
386
395
|
}),
|
|
387
396
|
section.allClear && /* @__PURE__ */ jsx6(TouchableRipple3, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles4.item, children: /* @__PURE__ */ jsx6(Text2, { variant: "labelLarge", style: [styles4.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tastic/hud",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Visual component kit for local-multiplayer React Native games: inline (non-modal) popovers, dropdowns, color pickers, gauges, ready buttons, and dialogs, built to stay scoped to one player's own zone on a shared screen",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
package/src/ReadyButton.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TouchableRipple } from '@rific/feedback-press'
|
|
2
|
-
import { StyleProp, StyleSheet, ViewStyle } from 'react-native'
|
|
2
|
+
import { StyleProp, StyleSheet, TextStyle, ViewStyle } from 'react-native'
|
|
3
3
|
import { Text } from 'react-native-paper'
|
|
4
4
|
|
|
5
5
|
import { MONO_FONT } from './fonts'
|
|
@@ -21,9 +21,13 @@ interface Props {
|
|
|
21
21
|
// below a shared row — e.g. a vs-CPU mode with only one human player needs just one Ready toggle,
|
|
22
22
|
// not one tucked under each slot.
|
|
23
23
|
export function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }: Props) {
|
|
24
|
+
const readyButtonStyle = [styles.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style]
|
|
25
|
+
const labelTextStyle: TextStyle = { fontFamily: labelFontFamily, fontWeight: 'bold' }
|
|
26
|
+
const labelColorStyle = { color: ready ? '#000000' : color }
|
|
27
|
+
|
|
24
28
|
return (
|
|
25
|
-
<TouchableRipple onPress={onToggleReady} borderless style={
|
|
26
|
-
<Text variant='labelLarge' style={[
|
|
29
|
+
<TouchableRipple onPress={onToggleReady} borderless style={readyButtonStyle}>
|
|
30
|
+
<Text variant='labelLarge' style={[labelTextStyle, labelColorStyle]}>
|
|
27
31
|
{ready ? 'READY ✓' : 'READY?'}
|
|
28
32
|
</Text>
|
|
29
33
|
</TouchableRipple>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getBlendedColor, getColorRoles } from '@rific/auto-paper'
|
|
2
2
|
import { IconButton, TouchableRipple } from '@rific/feedback-press'
|
|
3
|
-
import { ScrollView, StyleSheet, View } from 'react-native'
|
|
3
|
+
import { ScrollView, StyleSheet, TextStyle, View } from 'react-native'
|
|
4
4
|
import { Icon, Text } from 'react-native-paper'
|
|
5
5
|
|
|
6
6
|
import { MONO_FONT } from './fonts'
|
|
@@ -177,8 +177,12 @@ export function SectionedDropdown({ id, host, icon, accessibilityLabel, sections
|
|
|
177
177
|
gaugeOffset += section.options.length
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
const anchorStyle = [styles.anchor, open && styles.anchorOpen]
|
|
181
|
+
const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight }
|
|
182
|
+
const dividerStyle = { backgroundColor: menuBorderColor }
|
|
183
|
+
|
|
180
184
|
return (
|
|
181
|
-
<View style={
|
|
185
|
+
<View style={anchorStyle}>
|
|
182
186
|
<View ref={triggerRef} collapsable={false} style={styles.triggerBox}>
|
|
183
187
|
<TriggerGaugeHost segments={gaugeSegments} litIndices={gaugeLitIndices} size={TRIGGER_SIZE} accentColor={accentColor} mutedColor={mutedColor} />
|
|
184
188
|
<IconButton icon={triggerIcon} iconColor={triggerIconColor} size={triggerIconSize} accessibilityLabel={accessibilityLabel} onPress={() => host.toggle(id)} />
|
|
@@ -194,13 +198,13 @@ export function SectionedDropdown({ id, host, icon, accessibilityLabel, sections
|
|
|
194
198
|
is always <= maxHeight then, so nothing scrolls); on a short landscape screen where neither
|
|
195
199
|
direction has enough room, this is what keeps the menu from just running off the screen
|
|
196
200
|
edge instead of flipping only made it run off a *smaller* amount. */}
|
|
197
|
-
<ScrollView style={[styles.menu,
|
|
201
|
+
<ScrollView style={[styles.menu, menuStyle]} contentContainerStyle={styles.menuContent} showsVerticalScrollIndicator={false}>
|
|
198
202
|
{sections.map((section, sectionIndex) => (
|
|
199
203
|
<View key={section.id}>
|
|
200
204
|
{/* Every section after the first gets a rule above it — the divider belongs to the
|
|
201
205
|
boundary between sections, not to either section itself, so it's keyed off position
|
|
202
206
|
rather than each section carrying its own "divider below me" flag. */}
|
|
203
|
-
{sectionIndex > 0 && <View style={[styles.divider,
|
|
207
|
+
{sectionIndex > 0 && <View style={[styles.divider, dividerStyle]} />}
|
|
204
208
|
|
|
205
209
|
{section.kind === 'single' ? (
|
|
206
210
|
<SingleSection
|
|
@@ -247,16 +251,19 @@ function SingleSection({
|
|
|
247
251
|
{section.options.map((option) => {
|
|
248
252
|
const selected = option.value === section.value
|
|
249
253
|
const rowColor = selected ? onAccentColor : mutedColor
|
|
254
|
+
const itemStyle = [styles.item, selected && { backgroundColor: accentColor }]
|
|
255
|
+
const labelStyle: TextStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? 'bold' : 'normal' }
|
|
256
|
+
const descriptionStyle: TextStyle = { fontFamily: labelFontFamily, color: rowColor }
|
|
250
257
|
return (
|
|
251
|
-
<TouchableRipple key={option.value} onPress={() => onSelect(option.value)} style={
|
|
258
|
+
<TouchableRipple key={option.value} onPress={() => onSelect(option.value)} style={itemStyle}>
|
|
252
259
|
<View style={styles.itemRow}>
|
|
253
260
|
{option.icon && <Icon source={option.icon} size={MENU_ITEM_ICON_SIZE} color={rowColor} />}
|
|
254
261
|
<View style={styles.itemLabelColumn}>
|
|
255
|
-
<Text variant='labelLarge' style={
|
|
262
|
+
<Text variant='labelLarge' style={labelStyle}>
|
|
256
263
|
{option.label}
|
|
257
264
|
</Text>
|
|
258
265
|
{option.description && (
|
|
259
|
-
<Text variant='labelSmall' style={
|
|
266
|
+
<Text variant='labelSmall' style={descriptionStyle}>
|
|
260
267
|
{option.description}
|
|
261
268
|
</Text>
|
|
262
269
|
)}
|
|
@@ -299,11 +306,13 @@ function MultiSection({
|
|
|
299
306
|
// separately-rounded pills.
|
|
300
307
|
const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value)
|
|
301
308
|
const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value)
|
|
309
|
+
const itemStyle = [styles.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }]
|
|
310
|
+
const labelStyle: TextStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? 'bold' : 'normal' }
|
|
302
311
|
return (
|
|
303
|
-
<TouchableRipple key={option.value} onPress={() => section.onChange(selected ? section.value.filter((v: unknown) => v !== option.value) : [...section.value, option.value])} style={
|
|
312
|
+
<TouchableRipple key={option.value} onPress={() => section.onChange(selected ? section.value.filter((v: unknown) => v !== option.value) : [...section.value, option.value])} style={itemStyle}>
|
|
304
313
|
<View style={styles.itemRow}>
|
|
305
314
|
{option.icon && <Icon source={option.icon} size={MENU_ITEM_ICON_SIZE} color={rowColor} />}
|
|
306
|
-
<Text variant='labelLarge' style={[styles.itemLabelFlex,
|
|
315
|
+
<Text variant='labelLarge' style={[styles.itemLabelFlex, labelStyle]}>
|
|
307
316
|
{option.label}
|
|
308
317
|
</Text>
|
|
309
318
|
</View>
|
package/src/TriggerGauge.tsx
CHANGED
|
@@ -266,9 +266,18 @@ export function TriggerGauge({ segments, litIndices, size, accentColor, mutedCol
|
|
|
266
266
|
|
|
267
267
|
if (segments < 2) return null
|
|
268
268
|
|
|
269
|
+
const gaugeSizeStyle = {
|
|
270
|
+
height: size,
|
|
271
|
+
width: size
|
|
272
|
+
}
|
|
273
|
+
const canvasSizeStyle = {
|
|
274
|
+
height: canvasSize,
|
|
275
|
+
width: canvasSize
|
|
276
|
+
}
|
|
277
|
+
|
|
269
278
|
return (
|
|
270
|
-
<View style={[styles.container,
|
|
271
|
-
<Canvas style={
|
|
279
|
+
<View style={[styles.container, gaugeSizeStyle]} pointerEvents='none'>
|
|
280
|
+
<Canvas style={canvasSizeStyle}>
|
|
272
281
|
<Path path={unlitPath} style='stroke' strokeWidth={UNLIT_THICKNESS} strokeCap='round' color={mutedColor} opacity={0.35} />
|
|
273
282
|
<Path path={litPath} style='stroke' strokeWidth={LIT_THICKNESS} strokeCap='round' color={accentColor} opacity={1} />
|
|
274
283
|
</Canvas>
|
package/src/TriggerGaugeHost.tsx
CHANGED
|
@@ -11,10 +11,9 @@ import { TriggerGaugeProps } from './TriggerGauge'
|
|
|
11
11
|
// file therefore never imports TriggerGauge itself except as a type (erased at compile time,
|
|
12
12
|
// carrying no runtime module reference) — `lazy()` is what actually defers the real import to
|
|
13
13
|
// runtime, after loadSkiaWeb() resolves. Always lazy, on every platform, rather than a native/web
|
|
14
|
-
// branch here
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
// clears on the next tick, not a perceptible delay. The Suspense fallback renders nothing, so a
|
|
14
|
+
// branch here, purely to keep this file simple — loadSkiaWeb() itself already resolves immediately
|
|
15
|
+
// on native (see its own file split), so the Suspense boundary there just clears on the next tick,
|
|
16
|
+
// not a perceptible delay. The Suspense fallback renders nothing, so a
|
|
18
17
|
// consumer's trigger icon still shows immediately and the gauge ring just pops in a beat later.
|
|
19
18
|
const LazyTriggerGauge = lazy(() => loadSkiaWeb().then(() => import('./TriggerGauge').then((m) => ({ default: m.TriggerGauge }))))
|
|
20
19
|
|
package/src/loadSkiaWeb.ts
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// that pre-flattens the whole module graph itself (tsup/esbuild) never sees or swaps in a `.web.ts`
|
|
9
|
-
// variant the way a consuming app's own Metro bundler would, so the split has to be a runtime
|
|
10
|
-
// branch that survives being bundled, not a build-time file choice that wouldn't.
|
|
1
|
+
// Native counterpart to loadSkiaWeb.web.ts — Skia on native (via JSI, no WASM) is ready as soon as
|
|
2
|
+
// the app's native module is linked, so there's nothing to actually wait on here. This file must
|
|
3
|
+
// never import '@shopify/react-native-skia/src/web' (even conditionally behind a Platform.OS check):
|
|
4
|
+
// that module pulls in canvaskit-wasm, which does `require('fs')` — a runtime check doesn't stop
|
|
5
|
+
// Metro from statically resolving that import to build the native bundle graph, and Metro has no
|
|
6
|
+
// `fs` for iOS/Android, so the build fails outright. Metro's own platform-extension resolution (this
|
|
7
|
+
// file vs. loadSkiaWeb.web.ts) is what actually keeps the web-only module out of the native graph.
|
|
11
8
|
export function loadSkiaWeb(): Promise<void> {
|
|
12
|
-
|
|
13
|
-
return import('@shopify/react-native-skia/src/web').then((m) => m.LoadSkiaWeb())
|
|
9
|
+
return Promise.resolve()
|
|
14
10
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { LoadSkiaWeb } from '@shopify/react-native-skia/src/web'
|
|
2
|
+
|
|
3
|
+
// Skia's web target renders through CanvasKit, a WASM build of Skia fetched at runtime — nothing
|
|
4
|
+
// using @shopify/react-native-skia's React components can draw a single frame until this resolves.
|
|
5
|
+
// Kept as a thin wrapper (rather than importing LoadSkiaWeb directly at call sites) purely so this
|
|
6
|
+
// file's `.ts` counterpart, loadSkiaWeb.ts, gives native an identically-shaped async no-op — Metro's
|
|
7
|
+
// platform-extension resolution picks whichever one matches the bundle target, so canvaskit-wasm's
|
|
8
|
+
// `require('fs')' never even gets resolved into a native/iOS/Android bundle.
|
|
9
|
+
export function loadSkiaWeb(): Promise<void> {
|
|
10
|
+
return LoadSkiaWeb()
|
|
11
|
+
}
|