@tastic/hud 0.2.0 → 0.3.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 +73 -23
- package/dist/index.d.ts +73 -23
- package/dist/index.js +229 -75
- package/dist/index.mjs +207 -58
- package/package.json +1 -1
- package/src/CornerActionButtons.tsx +39 -0
- package/src/LabeledDropdown.tsx +175 -0
- package/src/SharedActionBand.tsx +61 -0
- package/src/index.ts +3 -0
package/dist/index.mjs
CHANGED
|
@@ -200,6 +200,25 @@ var styles = StyleSheet.create({
|
|
|
200
200
|
}
|
|
201
201
|
});
|
|
202
202
|
|
|
203
|
+
// src/CornerActionButtons.tsx
|
|
204
|
+
import { IconButton } from "@rific/feedback-press";
|
|
205
|
+
import { StyleSheet as StyleSheet2 } from "react-native";
|
|
206
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
207
|
+
function CornerActionButtons({ onBack, onSettings, fg, insets }) {
|
|
208
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
209
|
+
/* @__PURE__ */ jsx2(IconButton, { icon: "arrow-left", iconColor: fg, size: 24, style: [styles2.back, { top: 8 + insets.top, left: 8 + insets.left }], onPress: onBack, accessibilityLabel: "Back" }),
|
|
210
|
+
/* @__PURE__ */ jsx2(IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles2.topRight, { top: 8 + insets.top, right: 8 + insets.right }], onPress: onSettings, accessibilityLabel: "Settings" })
|
|
211
|
+
] });
|
|
212
|
+
}
|
|
213
|
+
var styles2 = StyleSheet2.create({
|
|
214
|
+
back: {
|
|
215
|
+
position: "absolute"
|
|
216
|
+
},
|
|
217
|
+
topRight: {
|
|
218
|
+
position: "absolute"
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
203
222
|
// src/fonts.ts
|
|
204
223
|
import { Platform as Platform2 } from "react-native";
|
|
205
224
|
var MONO_FONT = Platform2.select({ ios: "Menlo", android: "monospace", default: "monospace" });
|
|
@@ -208,34 +227,34 @@ var MONO_FONT = Platform2.select({ ios: "Menlo", android: "monospace", default:
|
|
|
208
227
|
import { defaultColors, getContrastColor } from "@rific/auto-paper";
|
|
209
228
|
import { TouchableRipple as TouchableRipple2 } from "@rific/feedback-press";
|
|
210
229
|
import { clamp } from "@tastic/core";
|
|
211
|
-
import { ScrollView as ScrollView2, StyleSheet as
|
|
230
|
+
import { ScrollView as ScrollView2, StyleSheet as StyleSheet4, useWindowDimensions as useWindowDimensions2, View as View4 } from "react-native";
|
|
212
231
|
import { Icon as Icon2, Text as Text2 } from "react-native-paper";
|
|
213
232
|
|
|
214
233
|
// src/PopoverBody.tsx
|
|
215
|
-
import { StyleSheet as
|
|
216
|
-
import { Fragment as
|
|
234
|
+
import { StyleSheet as StyleSheet3, View as View2 } from "react-native";
|
|
235
|
+
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
217
236
|
var CARET_SIZE = 7;
|
|
218
237
|
var CARET_RING_OFFSET = 2;
|
|
219
238
|
var CARET_DIP = 1;
|
|
220
239
|
function PopoverBody({ visible, children, align = "center", verticalAlign = "below", caretColor, caretBorderColor, caretBorderWidth = 1, triggerSize = 0 }) {
|
|
221
240
|
if (!visible) return null;
|
|
222
241
|
const above = verticalAlign === "above";
|
|
223
|
-
const alignStyle = align === "left" ?
|
|
224
|
-
const verticalStyle = above ?
|
|
242
|
+
const alignStyle = align === "left" ? styles3.contentLeft : align === "right" ? styles3.contentRight : styles3.contentCenter;
|
|
243
|
+
const verticalStyle = above ? styles3.contentAbove : styles3.contentBelow;
|
|
225
244
|
const ringSize = CARET_SIZE + caretBorderWidth;
|
|
226
245
|
const caretOffset = (halfFootprint) => align === "right" ? { right: triggerSize / 2 - halfFootprint } : { left: triggerSize / 2 - halfFootprint };
|
|
227
246
|
const caretFill = above ? "borderTopColor" : "borderBottomColor";
|
|
228
247
|
const caretWidthProp = above ? "borderTopWidth" : "borderBottomWidth";
|
|
229
248
|
const caretEdge = (size) => above ? { bottom: -size + CARET_DIP } : { top: -size + CARET_DIP };
|
|
230
|
-
return /* @__PURE__ */
|
|
249
|
+
return /* @__PURE__ */ jsxs3(View2, { style: [styles3.content, alignStyle, verticalStyle], children: [
|
|
231
250
|
children,
|
|
232
|
-
caretColor && /* @__PURE__ */
|
|
233
|
-
/* @__PURE__ */
|
|
234
|
-
/* @__PURE__ */
|
|
251
|
+
caretColor && /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
252
|
+
/* @__PURE__ */ jsx3(View2, { style: [styles3.caret, caretOffset(ringSize), caretEdge(ringSize), { [caretFill]: caretBorderColor ?? caretColor, [caretWidthProp]: ringSize, borderLeftWidth: ringSize, borderRightWidth: ringSize }] }),
|
|
253
|
+
/* @__PURE__ */ jsx3(View2, { style: [styles3.caret, caretOffset(CARET_SIZE), caretEdge(CARET_SIZE - CARET_RING_OFFSET), { [caretFill]: caretColor, [caretWidthProp]: CARET_SIZE, borderLeftWidth: CARET_SIZE, borderRightWidth: CARET_SIZE }] })
|
|
235
254
|
] })
|
|
236
255
|
] });
|
|
237
256
|
}
|
|
238
|
-
var
|
|
257
|
+
var styles3 = StyleSheet3.create({
|
|
239
258
|
caret: {
|
|
240
259
|
borderLeftColor: "transparent",
|
|
241
260
|
borderRightColor: "transparent",
|
|
@@ -316,7 +335,7 @@ function useAutoAlign(open, contentWidth, contentHeight) {
|
|
|
316
335
|
}
|
|
317
336
|
|
|
318
337
|
// src/InlineColorPicker.tsx
|
|
319
|
-
import { jsx as
|
|
338
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
320
339
|
var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
|
|
321
340
|
var DEFAULT_SIZE = 48;
|
|
322
341
|
var SWATCH_SIZE = 28;
|
|
@@ -342,13 +361,13 @@ function InlineColorPicker({ id, host, value, onChange, swatches = defaultColors
|
|
|
342
361
|
const align = alignOverride ?? autoAlign;
|
|
343
362
|
const contrastColor = getContrastColor(value);
|
|
344
363
|
const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : 0.4);
|
|
345
|
-
return /* @__PURE__ */
|
|
346
|
-
/* @__PURE__ */
|
|
347
|
-
/* @__PURE__ */
|
|
364
|
+
return /* @__PURE__ */ jsxs4(View4, { style: [styles4.anchor, open && styles4.anchorOpen], children: [
|
|
365
|
+
/* @__PURE__ */ jsx4(TouchableRipple2, { onPress: () => host.toggle(id), borderless: true, style: [styles4.trigger, { backgroundColor: value, borderRadius: size / 2, height: size, width: size }], children: /* @__PURE__ */ jsx4(View4, { ref: triggerRef, collapsable: false, style: styles4.triggerMeasure, children: tag ? /* @__PURE__ */ jsx4(Text2, { style: [styles4.tagLabel, { color: contrastColor, fontFamily: labelFontFamily, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ jsx4(Icon2, { source: icon, size: size * 0.6, color: contrastColor }) }) }),
|
|
366
|
+
/* @__PURE__ */ jsx4(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: value, caretBorderWidth: SWATCHES_BORDER_WIDTH, triggerSize: size, children: /* @__PURE__ */ jsx4(ScrollView2, { style: [styles4.swatches, { backgroundColor: menuBg, borderColor: value, width: swatchesWidth, maxHeight }], contentContainerStyle: styles4.swatchesContent, showsVerticalScrollIndicator: false, children: swatches.map((swatch) => {
|
|
348
367
|
const selected = swatch.value.toLowerCase() === value.toLowerCase();
|
|
349
368
|
const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
|
|
350
369
|
const swappable = taken && allowSwapTaken;
|
|
351
|
-
return /* @__PURE__ */
|
|
370
|
+
return /* @__PURE__ */ jsx4(
|
|
352
371
|
TouchableRipple2,
|
|
353
372
|
{
|
|
354
373
|
disabled: taken && !swappable,
|
|
@@ -357,15 +376,15 @@ function InlineColorPicker({ id, host, value, onChange, swatches = defaultColors
|
|
|
357
376
|
if (autoDismiss) host.close();
|
|
358
377
|
},
|
|
359
378
|
borderless: true,
|
|
360
|
-
style: [
|
|
361
|
-
children: selected ? /* @__PURE__ */
|
|
379
|
+
style: [styles4.swatch, { backgroundColor: swatch.value }, selected && styles4.swatchSelected, taken && !swappable && styles4.swatchTaken],
|
|
380
|
+
children: selected ? /* @__PURE__ */ jsx4(Icon2, { source: "check", size: 16, color: getContrastColor(swatch.value) }) : swappable ? /* @__PURE__ */ jsx4(Icon2, { source: "swap-horizontal", size: 16, color: getContrastColor(swatch.value) }) : taken ? /* @__PURE__ */ jsx4(Icon2, { source: "close", size: 16, color: getContrastColor(swatch.value) }) : /* @__PURE__ */ jsx4(View4, {})
|
|
362
381
|
},
|
|
363
382
|
swatch.value
|
|
364
383
|
);
|
|
365
384
|
}) }) })
|
|
366
385
|
] });
|
|
367
386
|
}
|
|
368
|
-
var
|
|
387
|
+
var styles4 = StyleSheet4.create({
|
|
369
388
|
anchor: {
|
|
370
389
|
position: "relative"
|
|
371
390
|
},
|
|
@@ -436,31 +455,126 @@ var styles3 = StyleSheet3.create({
|
|
|
436
455
|
}
|
|
437
456
|
});
|
|
438
457
|
|
|
458
|
+
// src/LabeledDropdown.tsx
|
|
459
|
+
import { getContrastColor as getContrastColor2 } from "@rific/auto-paper";
|
|
460
|
+
import { TouchableRipple as TouchableRipple3 } from "@rific/feedback-press";
|
|
461
|
+
import { ScrollView as ScrollView3, StyleSheet as StyleSheet5, View as View5 } from "react-native";
|
|
462
|
+
import { Icon as Icon3, Text as Text3 } from "react-native-paper";
|
|
463
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
464
|
+
var TRIGGER_HEIGHT = 28;
|
|
465
|
+
var POPOVER_WIDTH = 180;
|
|
466
|
+
var ROW_HEIGHT = 36;
|
|
467
|
+
var LIST_PADDING = 8;
|
|
468
|
+
var LABELED_DROPDOWN_POPOVER_WIDTH = POPOVER_WIDTH;
|
|
469
|
+
function getLabeledDropdownContentHeight(optionCount) {
|
|
470
|
+
return LIST_PADDING * 2 + optionCount * ROW_HEIGHT;
|
|
471
|
+
}
|
|
472
|
+
function LabeledDropdown({ id, host, options, value, onChange, color, dark, align: forcedAlign, alignOverride }) {
|
|
473
|
+
const menuBg = dark ? "#000000" : "#FFFFFF";
|
|
474
|
+
const fg = dark ? "#FFFFFF" : "#000000";
|
|
475
|
+
const open = host.openId === id;
|
|
476
|
+
const selected = options.find((o) => o.value === value) ?? null;
|
|
477
|
+
const contentHeight = LIST_PADDING * 2 + options.length * ROW_HEIGHT;
|
|
478
|
+
const auto = useAutoAlign(open, POPOVER_WIDTH, contentHeight);
|
|
479
|
+
const placement = alignOverride ?? auto;
|
|
480
|
+
const { maxHeight, measured, triggerRef, verticalAlign } = placement;
|
|
481
|
+
const align = forcedAlign ?? placement.align;
|
|
482
|
+
const handleSelect = (option) => {
|
|
483
|
+
onChange(option.value);
|
|
484
|
+
host.close();
|
|
485
|
+
};
|
|
486
|
+
return /* @__PURE__ */ jsxs5(View5, { style: [styles5.anchor, open && styles5.anchorOpen], children: [
|
|
487
|
+
/* @__PURE__ */ jsx5(TouchableRipple3, { onPress: () => host.toggle(id), style: styles5.trigger, children: /* @__PURE__ */ jsxs5(View5, { ref: triggerRef, collapsable: false, style: styles5.triggerInner, children: [
|
|
488
|
+
/* @__PURE__ */ jsx5(Text3, { style: [styles5.triggerLabel, { color }], numberOfLines: 1, children: (selected?.label ?? "").toUpperCase() }),
|
|
489
|
+
/* @__PURE__ */ jsx5(Icon3, { source: "menu-down", size: 16, color })
|
|
490
|
+
] }) }),
|
|
491
|
+
/* @__PURE__ */ jsx5(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: 2, triggerSize: TRIGGER_HEIGHT, children: /* @__PURE__ */ jsx5(ScrollView3, { style: [styles5.menu, { backgroundColor: menuBg, borderColor: color, width: POPOVER_WIDTH, maxHeight }], contentContainerStyle: styles5.menuContent, showsVerticalScrollIndicator: false, children: options.map((option) => {
|
|
492
|
+
const isSelected = option.value === value;
|
|
493
|
+
const onColor = getContrastColor2(color);
|
|
494
|
+
return /* @__PURE__ */ jsx5(TouchableRipple3, { onPress: () => handleSelect(option), style: [styles5.row, isSelected && { backgroundColor: color }], children: /* @__PURE__ */ jsxs5(View5, { style: styles5.rowInner, children: [
|
|
495
|
+
option.icon && /* @__PURE__ */ jsx5(Icon3, { source: option.icon, size: 18, color: isSelected ? onColor : fg }),
|
|
496
|
+
/* @__PURE__ */ jsx5(Text3, { style: [styles5.rowLabel, { color: isSelected ? onColor : fg }], numberOfLines: 1, children: option.label })
|
|
497
|
+
] }) }, option.value);
|
|
498
|
+
}) }) })
|
|
499
|
+
] });
|
|
500
|
+
}
|
|
501
|
+
var styles5 = StyleSheet5.create({
|
|
502
|
+
anchor: {
|
|
503
|
+
position: "relative"
|
|
504
|
+
},
|
|
505
|
+
// React Native Web gives every position:'relative' view its own stacking context, so the
|
|
506
|
+
// elevation has to live on the anchor itself — see PopoverBody's own stacking-context notes.
|
|
507
|
+
anchorOpen: {
|
|
508
|
+
zIndex: 100
|
|
509
|
+
},
|
|
510
|
+
menu: {
|
|
511
|
+
borderRadius: 12,
|
|
512
|
+
borderWidth: 2,
|
|
513
|
+
elevation: 8,
|
|
514
|
+
shadowColor: "#000000",
|
|
515
|
+
shadowOffset: { height: 2, width: 0 },
|
|
516
|
+
shadowOpacity: 0.3,
|
|
517
|
+
shadowRadius: 8
|
|
518
|
+
},
|
|
519
|
+
menuContent: {
|
|
520
|
+
padding: LIST_PADDING
|
|
521
|
+
},
|
|
522
|
+
row: {
|
|
523
|
+
borderRadius: 8,
|
|
524
|
+
height: ROW_HEIGHT
|
|
525
|
+
},
|
|
526
|
+
rowInner: {
|
|
527
|
+
alignItems: "center",
|
|
528
|
+
flexDirection: "row",
|
|
529
|
+
flex: 1,
|
|
530
|
+
gap: 8,
|
|
531
|
+
paddingHorizontal: 12
|
|
532
|
+
},
|
|
533
|
+
rowLabel: {
|
|
534
|
+
flex: 1,
|
|
535
|
+
fontSize: 14
|
|
536
|
+
},
|
|
537
|
+
trigger: {
|
|
538
|
+
alignSelf: "flex-start"
|
|
539
|
+
},
|
|
540
|
+
triggerInner: {
|
|
541
|
+
alignItems: "center",
|
|
542
|
+
flexDirection: "row",
|
|
543
|
+
gap: 2,
|
|
544
|
+
height: TRIGGER_HEIGHT
|
|
545
|
+
},
|
|
546
|
+
triggerLabel: {
|
|
547
|
+
fontSize: 13,
|
|
548
|
+
fontWeight: "700",
|
|
549
|
+
letterSpacing: 1
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
|
|
439
553
|
// src/loadSkiaWeb.ts
|
|
440
554
|
function loadSkiaWeb() {
|
|
441
555
|
return Promise.resolve();
|
|
442
556
|
}
|
|
443
557
|
|
|
444
558
|
// src/PressAwayOverlay.tsx
|
|
445
|
-
import { Pressable, StyleSheet as
|
|
446
|
-
import { jsx as
|
|
559
|
+
import { Pressable, StyleSheet as StyleSheet6 } from "react-native";
|
|
560
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
447
561
|
function PressAwayOverlay({ active, onPress, style }) {
|
|
448
562
|
if (!active) return null;
|
|
449
|
-
return /* @__PURE__ */
|
|
563
|
+
return /* @__PURE__ */ jsx6(Pressable, { accessibilityLabel: "Dismiss", accessibilityRole: "button", onPress, style: [StyleSheet6.absoluteFill, style] });
|
|
450
564
|
}
|
|
451
565
|
|
|
452
566
|
// src/ReadyButton.tsx
|
|
453
|
-
import { TouchableRipple as
|
|
454
|
-
import { StyleSheet as
|
|
455
|
-
import { Text as
|
|
456
|
-
import { jsx as
|
|
567
|
+
import { TouchableRipple as TouchableRipple4 } from "@rific/feedback-press";
|
|
568
|
+
import { StyleSheet as StyleSheet7 } from "react-native";
|
|
569
|
+
import { Text as Text4 } from "react-native-paper";
|
|
570
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
457
571
|
function ReadyButton({ color, ready, onToggleReady, style, labelFontFamily = MONO_FONT }) {
|
|
458
|
-
const readyButtonStyle = [
|
|
572
|
+
const readyButtonStyle = [styles6.readyButton, { borderColor: color }, ready && { backgroundColor: color }, style];
|
|
459
573
|
const labelTextStyle = { fontFamily: labelFontFamily, fontWeight: "bold" };
|
|
460
574
|
const labelColorStyle = { color: ready ? "#000000" : color };
|
|
461
|
-
return /* @__PURE__ */
|
|
575
|
+
return /* @__PURE__ */ jsx7(TouchableRipple4, { onPress: onToggleReady, borderless: true, style: readyButtonStyle, children: /* @__PURE__ */ jsx7(Text4, { variant: "labelLarge", style: [labelTextStyle, labelColorStyle], children: ready ? "READY \u2713" : "READY?" }) });
|
|
462
576
|
}
|
|
463
|
-
var
|
|
577
|
+
var styles6 = StyleSheet7.create({
|
|
464
578
|
readyButton: {
|
|
465
579
|
alignItems: "center",
|
|
466
580
|
borderRadius: 12,
|
|
@@ -474,20 +588,20 @@ var styles4 = StyleSheet5.create({
|
|
|
474
588
|
|
|
475
589
|
// src/SectionedDropdown.tsx
|
|
476
590
|
import { getBlendedColor, getColorRoles } from "@rific/auto-paper";
|
|
477
|
-
import { IconButton, TouchableRipple as
|
|
478
|
-
import { ScrollView as
|
|
479
|
-
import { Icon as
|
|
591
|
+
import { IconButton as IconButton2, TouchableRipple as TouchableRipple5 } from "@rific/feedback-press";
|
|
592
|
+
import { ScrollView as ScrollView4, StyleSheet as StyleSheet8, View as View6 } from "react-native";
|
|
593
|
+
import { Icon as Icon4, Text as Text5 } from "react-native-paper";
|
|
480
594
|
|
|
481
595
|
// src/TriggerGaugeHost.tsx
|
|
482
596
|
import { lazy, Suspense } from "react";
|
|
483
|
-
import { jsx as
|
|
597
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
484
598
|
var LazyTriggerGauge = lazy(() => loadSkiaWeb().then(() => import("./TriggerGauge-2OJOOF6I.mjs").then((m) => ({ default: m.TriggerGauge }))));
|
|
485
599
|
function TriggerGaugeHost(props) {
|
|
486
|
-
return /* @__PURE__ */
|
|
600
|
+
return /* @__PURE__ */ jsx8(Suspense, { fallback: null, children: /* @__PURE__ */ jsx8(LazyTriggerGauge, { ...props }) });
|
|
487
601
|
}
|
|
488
602
|
|
|
489
603
|
// src/SectionedDropdown.tsx
|
|
490
|
-
import { Fragment as
|
|
604
|
+
import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
491
605
|
var MENU_BORDER_WIDTH = 1;
|
|
492
606
|
var TRIGGER_SIZE = 44;
|
|
493
607
|
var MENU_MIN_WIDTH = 200;
|
|
@@ -533,17 +647,17 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
|
|
|
533
647
|
}
|
|
534
648
|
gaugeOffset += section.options.length;
|
|
535
649
|
}
|
|
536
|
-
const anchorStyle = [
|
|
650
|
+
const anchorStyle = [styles7.anchor, open && styles7.anchorOpen];
|
|
537
651
|
const menuStyle = { backgroundColor: menuBg, borderColor: menuBorderColor, maxHeight };
|
|
538
652
|
const dividerStyle = { backgroundColor: menuBorderColor };
|
|
539
|
-
return /* @__PURE__ */
|
|
540
|
-
/* @__PURE__ */
|
|
541
|
-
/* @__PURE__ */
|
|
542
|
-
/* @__PURE__ */
|
|
653
|
+
return /* @__PURE__ */ jsxs6(View6, { style: anchorStyle, children: [
|
|
654
|
+
/* @__PURE__ */ jsxs6(View6, { ref: triggerRef, collapsable: false, style: styles7.triggerBox, children: [
|
|
655
|
+
/* @__PURE__ */ jsx9(TriggerGaugeHost, { segments: gaugeSegments, litIndices: gaugeLitIndices, size: TRIGGER_SIZE, accentColor, mutedColor }),
|
|
656
|
+
/* @__PURE__ */ jsx9(IconButton2, { icon: triggerIcon, iconColor: triggerIconColor, size: triggerIconSize, accessibilityLabel, onPress: () => host.toggle(id) })
|
|
543
657
|
] }),
|
|
544
|
-
/* @__PURE__ */
|
|
545
|
-
sectionIndex > 0 && /* @__PURE__ */
|
|
546
|
-
section.kind === "single" ? /* @__PURE__ */
|
|
658
|
+
/* @__PURE__ */ jsx9(PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: menuBorderColor, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: TRIGGER_SIZE, children: /* @__PURE__ */ jsx9(ScrollView4, { style: [styles7.menu, menuStyle], contentContainerStyle: styles7.menuContent, showsVerticalScrollIndicator: false, children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs6(View6, { children: [
|
|
659
|
+
sectionIndex > 0 && /* @__PURE__ */ jsx9(View6, { style: [styles7.divider, dividerStyle] }),
|
|
660
|
+
section.kind === "single" ? /* @__PURE__ */ jsx9(
|
|
547
661
|
SingleSection,
|
|
548
662
|
{
|
|
549
663
|
section,
|
|
@@ -556,7 +670,7 @@ function SectionedDropdown({ id, host, icon, accessibilityLabel, sections, accen
|
|
|
556
670
|
if (autoDismiss) host.close();
|
|
557
671
|
}
|
|
558
672
|
}
|
|
559
|
-
) : /* @__PURE__ */
|
|
673
|
+
) : /* @__PURE__ */ jsx9(MultiSection, { section, accentColor, onAccentColor: resolvedOnAccentColor, mutedColor, labelFontFamily, allClearLabels })
|
|
560
674
|
] }, section.id)) }) })
|
|
561
675
|
] });
|
|
562
676
|
}
|
|
@@ -568,17 +682,17 @@ function SingleSection({
|
|
|
568
682
|
labelFontFamily,
|
|
569
683
|
onSelect
|
|
570
684
|
}) {
|
|
571
|
-
return /* @__PURE__ */
|
|
685
|
+
return /* @__PURE__ */ jsx9(Fragment4, { children: section.options.map((option) => {
|
|
572
686
|
const selected = option.value === section.value;
|
|
573
687
|
const rowColor = selected ? onAccentColor : mutedColor;
|
|
574
|
-
const itemStyle = [
|
|
688
|
+
const itemStyle = [styles7.item, selected && { backgroundColor: accentColor }];
|
|
575
689
|
const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
|
|
576
690
|
const descriptionStyle = { fontFamily: labelFontFamily, color: rowColor };
|
|
577
|
-
return /* @__PURE__ */
|
|
578
|
-
option.icon && /* @__PURE__ */
|
|
579
|
-
/* @__PURE__ */
|
|
580
|
-
/* @__PURE__ */
|
|
581
|
-
option.description && /* @__PURE__ */
|
|
691
|
+
return /* @__PURE__ */ jsx9(TouchableRipple5, { onPress: () => onSelect(option.value), style: itemStyle, children: /* @__PURE__ */ jsxs6(View6, { style: styles7.itemRow, children: [
|
|
692
|
+
option.icon && /* @__PURE__ */ jsx9(Icon4, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
|
|
693
|
+
/* @__PURE__ */ jsxs6(View6, { style: styles7.itemLabelColumn, children: [
|
|
694
|
+
/* @__PURE__ */ jsx9(Text5, { variant: "labelLarge", style: labelStyle, children: option.label }),
|
|
695
|
+
option.description && /* @__PURE__ */ jsx9(Text5, { variant: "labelSmall", style: descriptionStyle, children: option.description })
|
|
582
696
|
] })
|
|
583
697
|
] }) }, option.value);
|
|
584
698
|
}) });
|
|
@@ -592,23 +706,23 @@ function MultiSection({
|
|
|
592
706
|
allClearLabels
|
|
593
707
|
}) {
|
|
594
708
|
const allSelected = section.value.length === section.options.length;
|
|
595
|
-
return /* @__PURE__ */
|
|
709
|
+
return /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
596
710
|
section.options.map((option, i) => {
|
|
597
711
|
const selected = section.value.includes(option.value);
|
|
598
712
|
const rowColor = selected ? onAccentColor : mutedColor;
|
|
599
713
|
const prevSelected = selected && i > 0 && section.value.includes(section.options[i - 1].value);
|
|
600
714
|
const nextSelected = selected && i < section.options.length - 1 && section.value.includes(section.options[i + 1].value);
|
|
601
|
-
const itemStyle = [
|
|
715
|
+
const itemStyle = [styles7.item, selected && { backgroundColor: accentColor }, prevSelected && { borderTopLeftRadius: 0, borderTopRightRadius: 0 }, nextSelected && { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }];
|
|
602
716
|
const labelStyle = { fontFamily: labelFontFamily, color: rowColor, fontWeight: selected ? "bold" : "normal" };
|
|
603
|
-
return /* @__PURE__ */
|
|
604
|
-
option.icon && /* @__PURE__ */
|
|
605
|
-
/* @__PURE__ */
|
|
717
|
+
return /* @__PURE__ */ jsx9(TouchableRipple5, { onPress: () => section.onChange(selected ? section.value.filter((v) => v !== option.value) : [...section.value, option.value]), style: itemStyle, children: /* @__PURE__ */ jsxs6(View6, { style: styles7.itemRow, children: [
|
|
718
|
+
option.icon && /* @__PURE__ */ jsx9(Icon4, { source: option.icon, size: MENU_ITEM_ICON_SIZE, color: rowColor }),
|
|
719
|
+
/* @__PURE__ */ jsx9(Text5, { variant: "labelLarge", style: [styles7.itemLabelFlex, labelStyle], children: option.label })
|
|
606
720
|
] }) }, option.value);
|
|
607
721
|
}),
|
|
608
|
-
section.allClear && /* @__PURE__ */
|
|
722
|
+
section.allClear && /* @__PURE__ */ jsx9(TouchableRipple5, { onPress: () => section.onChange(allSelected ? [] : section.options.map((o) => o.value)), style: styles7.item, children: /* @__PURE__ */ jsx9(Text5, { variant: "labelLarge", style: [styles7.allClearLabel, { fontFamily: labelFontFamily, color: accentColor }], children: allSelected ? allClearLabels.clear : allClearLabels.all }) })
|
|
609
723
|
] });
|
|
610
724
|
}
|
|
611
|
-
var
|
|
725
|
+
var styles7 = StyleSheet8.create({
|
|
612
726
|
allClearLabel: {
|
|
613
727
|
fontWeight: "bold",
|
|
614
728
|
textAlign: "center"
|
|
@@ -625,7 +739,7 @@ var styles5 = StyleSheet6.create({
|
|
|
625
739
|
zIndex: 100
|
|
626
740
|
},
|
|
627
741
|
divider: {
|
|
628
|
-
height:
|
|
742
|
+
height: StyleSheet8.hairlineWidth,
|
|
629
743
|
marginVertical: 4
|
|
630
744
|
},
|
|
631
745
|
item: {
|
|
@@ -672,6 +786,36 @@ var styles5 = StyleSheet6.create({
|
|
|
672
786
|
}
|
|
673
787
|
});
|
|
674
788
|
|
|
789
|
+
// src/SharedActionBand.tsx
|
|
790
|
+
import { IconButton as IconButton3 } from "@rific/feedback-press";
|
|
791
|
+
import { StyleSheet as StyleSheet9, View as View7 } from "react-native";
|
|
792
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
793
|
+
function SharedActionBand({ onBack, onSettings, onRandomize, onReset, fg, popoverOpen, children }) {
|
|
794
|
+
return /* @__PURE__ */ jsxs7(View7, { style: [styles8.column, popoverOpen && styles8.columnOpen], children: [
|
|
795
|
+
/* @__PURE__ */ jsxs7(View7, { style: styles8.actionsRow, children: [
|
|
796
|
+
/* @__PURE__ */ jsx10(IconButton3, { icon: "arrow-left", iconColor: fg, size: 20, onPress: onBack, accessibilityLabel: "Back" }),
|
|
797
|
+
onRandomize && /* @__PURE__ */ jsx10(IconButton3, { icon: "dice-multiple", iconColor: fg, size: 18, onPress: onRandomize, accessibilityLabel: "Randomize match settings" }),
|
|
798
|
+
onReset && /* @__PURE__ */ jsx10(IconButton3, { icon: "restore", iconColor: fg, size: 18, onPress: onReset, accessibilityLabel: "Reset match settings to defaults" }),
|
|
799
|
+
/* @__PURE__ */ jsx10(IconButton3, { icon: "cog", iconColor: fg, size: 20, onPress: onSettings, accessibilityLabel: "Settings" })
|
|
800
|
+
] }),
|
|
801
|
+
children
|
|
802
|
+
] });
|
|
803
|
+
}
|
|
804
|
+
var styles8 = StyleSheet9.create({
|
|
805
|
+
actionsRow: {
|
|
806
|
+
alignItems: "center",
|
|
807
|
+
flexDirection: "row",
|
|
808
|
+
gap: 16
|
|
809
|
+
},
|
|
810
|
+
column: {
|
|
811
|
+
alignItems: "center",
|
|
812
|
+
gap: 8
|
|
813
|
+
},
|
|
814
|
+
columnOpen: {
|
|
815
|
+
zIndex: 100
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
|
|
675
819
|
// src/usePopoverHost.ts
|
|
676
820
|
import { useCallback as useCallback2, useState as useState3 } from "react";
|
|
677
821
|
function usePopoverHost() {
|
|
@@ -684,13 +828,18 @@ function usePopoverHost() {
|
|
|
684
828
|
}
|
|
685
829
|
export {
|
|
686
830
|
BaseSettingsDialog,
|
|
831
|
+
CornerActionButtons,
|
|
687
832
|
InlineColorPicker,
|
|
833
|
+
LABELED_DROPDOWN_POPOVER_WIDTH,
|
|
834
|
+
LabeledDropdown,
|
|
688
835
|
MONO_FONT,
|
|
689
836
|
PopoverBody,
|
|
690
837
|
PressAwayOverlay,
|
|
691
838
|
ReadyButton,
|
|
692
839
|
SectionedDropdown,
|
|
840
|
+
SharedActionBand,
|
|
693
841
|
TriggerGaugeHost,
|
|
842
|
+
getLabeledDropdownContentHeight,
|
|
694
843
|
loadSkiaWeb,
|
|
695
844
|
useAutoAlign,
|
|
696
845
|
usePopoverHost
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tastic/hud",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
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",
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { IconButton } from '@rific/feedback-press'
|
|
2
|
+
import { StyleSheet } from 'react-native'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
onBack: () => void
|
|
6
|
+
onSettings: () => void
|
|
7
|
+
// Full-contrast foreground — same convention as every other component keying off a `dark` prop
|
|
8
|
+
// (BaseSettingsDialog, SectionedDropdown, etc.), just resolved by the caller since this component
|
|
9
|
+
// never reads theme state itself.
|
|
10
|
+
fg: string
|
|
11
|
+
// Safe-area insets already remapped onto the screen's real visual edges (see
|
|
12
|
+
// @tastic/split-screen's rotateInsets) — this component just adds the fixed 8px margin on top,
|
|
13
|
+
// it doesn't know anything about physical device orientation itself.
|
|
14
|
+
insets: { top: number; left: number; right: number }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Back (top-left) + Settings (top-right) icon buttons, fixed at the screen's rotated corners — the
|
|
18
|
+
// default reachability answer for a two-player screen: correct any time both players share one
|
|
19
|
+
// screen but only one of them (or neither, in vs-CPU) is on the far side of a rotated zone. Not the
|
|
20
|
+
// right answer for a screen where the top corners themselves fall *inside* one player's own rotated
|
|
21
|
+
// zone (two-player face-to-face) — see SharedActionBand for that case instead, which callers select
|
|
22
|
+
// between based on their own game-mode/orientation state (this component has no opinion on which).
|
|
23
|
+
export function CornerActionButtons({ onBack, onSettings, fg, insets }: Props) {
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
<IconButton icon='arrow-left' iconColor={fg} size={24} style={[styles.back, { top: 8 + insets.top, left: 8 + insets.left }]} onPress={onBack} accessibilityLabel='Back' />
|
|
27
|
+
<IconButton icon='cog' iconColor={fg} size={24} style={[styles.topRight, { top: 8 + insets.top, right: 8 + insets.right }]} onPress={onSettings} accessibilityLabel='Settings' />
|
|
28
|
+
</>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const styles = StyleSheet.create({
|
|
33
|
+
back: {
|
|
34
|
+
position: 'absolute'
|
|
35
|
+
},
|
|
36
|
+
topRight: {
|
|
37
|
+
position: 'absolute'
|
|
38
|
+
}
|
|
39
|
+
})
|