@swiftbrowser/react 0.17.7 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +243 -14
- package/dist/appStorage.d.ts +43 -0
- package/dist/appStorage.d.ts.map +1 -0
- package/dist/attributedText.d.ts +76 -0
- package/dist/attributedText.d.ts.map +1 -0
- package/dist/audioRecorder.d.ts +52 -0
- package/dist/audioRecorder.d.ts.map +1 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/components/charts.d.ts +5 -0
- package/dist/components/charts.d.ts.map +1 -1
- package/dist/components/containers.d.ts +5 -0
- package/dist/components/containers.d.ts.map +1 -1
- package/dist/components/controls.d.ts +84 -8
- package/dist/components/controls.d.ts.map +1 -1
- package/dist/components/geometry.d.ts +18 -0
- package/dist/components/geometry.d.ts.map +1 -1
- package/dist/components/images.d.ts.map +1 -1
- package/dist/components/lists.d.ts.map +1 -1
- package/dist/components/media.d.ts +18 -10
- package/dist/components/media.d.ts.map +1 -1
- package/dist/components/stacks.d.ts +70 -4
- package/dist/components/stacks.d.ts.map +1 -1
- package/dist/environment.d.ts +2 -1
- package/dist/environment.d.ts.map +1 -1
- package/dist/fileManager.d.ts +58 -0
- package/dist/fileManager.d.ts.map +1 -0
- package/dist/host/root.d.ts +16 -2
- package/dist/host/root.d.ts.map +1 -1
- package/dist/host/tree.d.ts +9 -0
- package/dist/host/tree.d.ts.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1200 -369
- package/dist/index.js.map +1 -1
- package/dist/measureText.d.ts +32 -0
- package/dist/measureText.d.ts.map +1 -0
- package/dist/modifiers.d.ts +81 -5
- package/dist/modifiers.d.ts.map +1 -1
- package/dist/mount.d.ts +5 -0
- package/dist/mount.d.ts.map +1 -1
- package/dist/presentation.d.ts +5 -1
- package/dist/presentation.d.ts.map +1 -1
- package/dist/scrollReader.d.ts +34 -8
- package/dist/scrollReader.d.ts.map +1 -1
- package/dist/undo.d.ts +108 -0
- package/dist/undo.d.ts.map +1 -0
- package/dist/visualEffect.d.ts +10 -14
- package/dist/visualEffect.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { createContext, useContext, createElement, useRef, useLayoutEffect, useState, Children, useEffect, isValidElement, Fragment, useId, cloneElement,
|
|
1
|
+
import { createContext, useContext, createElement, useSyncExternalStore, useRef, useLayoutEffect, useState, Children, useEffect, isValidElement, Fragment, useId, cloneElement, useCallback } from "react";
|
|
2
2
|
import { DefaultEventPriority, ConcurrentRoot } from "react-reconciler/constants";
|
|
3
3
|
import Reconciler from "react-reconciler";
|
|
4
4
|
import { horizontalSizeClassFor } from "@swiftbrowser/web/src/protocol";
|
|
5
5
|
import { BrowserHistory } from "@swiftbrowser/web/src/browserHistory";
|
|
6
6
|
import { watchKeyboard } from "@swiftbrowser/web/src/deviceMode";
|
|
7
|
+
import { browserEnvironment, loadableURL, performHostRequest } from "@swiftbrowser/web/src/hostRequests";
|
|
7
8
|
import { Renderer } from "@swiftbrowser/web/src/renderer";
|
|
8
9
|
import { jsx, jsxs, Fragment as Fragment$1 } from "react/jsx-runtime";
|
|
9
10
|
import { applyTypeSizeVariables } from "@swiftbrowser/web/src/typography";
|
|
10
|
-
import {
|
|
11
|
+
import { fileURL } from "@swiftbrowser/web/src/files";
|
|
11
12
|
import { registerHostContent, notifyHostContentChange, unregisterHostContent } from "@swiftbrowser/web/src/hostContent";
|
|
12
13
|
import { createRoot } from "react-dom/client";
|
|
13
14
|
import { flushSync } from "react-dom";
|
|
@@ -211,6 +212,9 @@ class HostTree {
|
|
|
211
212
|
retired = [];
|
|
212
213
|
/** `withAnimation`: stamped on the next `commit`. */
|
|
213
214
|
pendingAnimation;
|
|
215
|
+
/** `withAnimation(_:completionCriteria:_:completion:)`: stamped on the next `commit` with its serial; the callback waits in `completions`. */
|
|
216
|
+
pendingCompletion;
|
|
217
|
+
completions = /* @__PURE__ */ new Map();
|
|
214
218
|
/** The root's `colorScheme` last sent (`.preferredColorScheme`); undefined while none was. */
|
|
215
219
|
committedColorScheme;
|
|
216
220
|
createInstance(props) {
|
|
@@ -253,6 +257,8 @@ class HostTree {
|
|
|
253
257
|
flush() {
|
|
254
258
|
const animation = this.pendingAnimation;
|
|
255
259
|
this.pendingAnimation = void 0;
|
|
260
|
+
const completion = this.pendingCompletion;
|
|
261
|
+
this.pendingCompletion = void 0;
|
|
256
262
|
const effective = /* @__PURE__ */ new Map();
|
|
257
263
|
const rootEffective = { props: {}, children: [], hoisted: [] };
|
|
258
264
|
effective.set(this.root, rootEffective);
|
|
@@ -340,15 +346,32 @@ class HostTree {
|
|
|
340
346
|
this.committedColorScheme = next === null ? void 0 : next;
|
|
341
347
|
}
|
|
342
348
|
if (ops.length === 0) return ops;
|
|
343
|
-
|
|
349
|
+
const commit = { op: "commit" };
|
|
350
|
+
if (animation) commit.animation = animation;
|
|
351
|
+
if (completion) {
|
|
352
|
+
commit.completion = completion.serial;
|
|
353
|
+
if (completion.criteria === "removed") commit.completionCriteria = "removed";
|
|
354
|
+
this.completions.set(completion.serial, completion.callback);
|
|
355
|
+
}
|
|
356
|
+
ops.push(commit);
|
|
344
357
|
return ops;
|
|
345
358
|
}
|
|
359
|
+
/** Whether a pass is still animating with this completion serial. */
|
|
360
|
+
awaitsCompletion(serial) {
|
|
361
|
+
return this.completions.has(serial);
|
|
362
|
+
}
|
|
346
363
|
/** Hand a renderer event to the element's handler for its type. */
|
|
347
364
|
dispatch(event) {
|
|
348
365
|
if (event.type === "batch") {
|
|
349
366
|
for (const each of event.events) this.dispatch(each);
|
|
350
367
|
return;
|
|
351
368
|
}
|
|
369
|
+
if (event.type === "animationcomplete") {
|
|
370
|
+
const callback = this.completions.get(event.serial);
|
|
371
|
+
this.completions.delete(event.serial);
|
|
372
|
+
callback?.();
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
352
375
|
if (!("id" in event)) return;
|
|
353
376
|
const instance = this.live.get(event.id);
|
|
354
377
|
const handler = instance?.on?.[event.type];
|
|
@@ -418,20 +441,207 @@ function createSwiftUIRoot(sink) {
|
|
|
418
441
|
function injectIntoDevTools() {
|
|
419
442
|
return reconciler.injectIntoDevTools() === true;
|
|
420
443
|
}
|
|
421
|
-
|
|
444
|
+
let nextCompletionSerial = 1;
|
|
445
|
+
function withAnimation(first, second, third, fourth) {
|
|
422
446
|
const animation = typeof first === "function" ? { type: "default" } : first ?? { type: "default" };
|
|
423
|
-
|
|
424
|
-
|
|
447
|
+
let body;
|
|
448
|
+
let completion;
|
|
449
|
+
let criteria = "logicallyComplete";
|
|
450
|
+
if (typeof first === "function") {
|
|
451
|
+
body = first;
|
|
452
|
+
} else if (typeof second === "function") {
|
|
453
|
+
body = second;
|
|
454
|
+
completion = third;
|
|
455
|
+
} else {
|
|
456
|
+
criteria = second?.completionCriteria ?? "logicallyComplete";
|
|
457
|
+
body = third;
|
|
458
|
+
completion = fourth;
|
|
459
|
+
}
|
|
460
|
+
const serial = completion ? nextCompletionSerial++ : 0;
|
|
461
|
+
for (const tree of roots) {
|
|
462
|
+
tree.pendingAnimation = animation;
|
|
463
|
+
if (completion) tree.pendingCompletion = { serial, criteria, callback: completion };
|
|
464
|
+
}
|
|
425
465
|
try {
|
|
426
466
|
return reconciler.flushSyncFromReconciler(body);
|
|
427
467
|
} finally {
|
|
428
|
-
|
|
468
|
+
let committed = false;
|
|
469
|
+
for (const tree of roots) {
|
|
470
|
+
tree.pendingAnimation = void 0;
|
|
471
|
+
tree.pendingCompletion = void 0;
|
|
472
|
+
if (completion && tree.awaitsCompletion(serial)) committed = true;
|
|
473
|
+
}
|
|
474
|
+
if (completion && !committed) completion();
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const HIERARCHY = { primary: 1, secondary: 2, tertiary: 3, quaternary: 4 };
|
|
478
|
+
function rgb(r2, g, b, a = 1) {
|
|
479
|
+
return { r: r2, g, b, a };
|
|
480
|
+
}
|
|
481
|
+
function hex(value) {
|
|
482
|
+
const digits = value.replace("#", "");
|
|
483
|
+
const wide = digits.length === 3 || digits.length === 4 ? [...digits].map((d) => d + d).join("") : digits;
|
|
484
|
+
const n = (at) => parseInt(wide.slice(at, at + 2), 16) / 255;
|
|
485
|
+
return { r: n(0), g: n(2), b: n(4), a: wide.length >= 8 ? n(6) : 1 };
|
|
486
|
+
}
|
|
487
|
+
function shapeStyle(value) {
|
|
488
|
+
if (typeof value !== "string") return value;
|
|
489
|
+
if (value in HIERARCHY) return { hierarchical: HIERARCHY[value] };
|
|
490
|
+
if (value === "white") return rgb(1, 1, 1);
|
|
491
|
+
if (value === "black") return rgb(0, 0, 0);
|
|
492
|
+
if (value.startsWith("#")) return hex(value);
|
|
493
|
+
return { name: value };
|
|
494
|
+
}
|
|
495
|
+
function color(value) {
|
|
496
|
+
return shapeStyle(value);
|
|
497
|
+
}
|
|
498
|
+
function opacity(value, a) {
|
|
499
|
+
const style = shapeStyle(value);
|
|
500
|
+
if ("r" in style) return { ...style, a: style.a * a };
|
|
501
|
+
if ("name" in style) return { ...style, a: (style.a ?? 1) * a };
|
|
502
|
+
if ("named" in style) return { ...style, opacity: style.opacity * a };
|
|
503
|
+
return { ...style, opacity: (style.opacity ?? 1) * a };
|
|
504
|
+
}
|
|
505
|
+
function gradient(value) {
|
|
506
|
+
return { colorGradient: color(value) };
|
|
507
|
+
}
|
|
508
|
+
const ColorValues = {
|
|
509
|
+
red: { name: "red" },
|
|
510
|
+
orange: { name: "orange" },
|
|
511
|
+
yellow: { name: "yellow" },
|
|
512
|
+
green: { name: "green" },
|
|
513
|
+
mint: { name: "mint" },
|
|
514
|
+
teal: { name: "teal" },
|
|
515
|
+
cyan: { name: "cyan" },
|
|
516
|
+
blue: { name: "blue" },
|
|
517
|
+
indigo: { name: "indigo" },
|
|
518
|
+
purple: { name: "purple" },
|
|
519
|
+
pink: { name: "pink" },
|
|
520
|
+
brown: { name: "brown" },
|
|
521
|
+
gray: { name: "gray" },
|
|
522
|
+
white: rgb(1, 1, 1),
|
|
523
|
+
black: rgb(0, 0, 0),
|
|
524
|
+
clear: { name: "clear" },
|
|
525
|
+
primary: { name: "primary" },
|
|
526
|
+
secondary: { name: "secondary" },
|
|
527
|
+
accent: { name: "accent" },
|
|
528
|
+
/** `Color(uiColor:)` / `Color(.name)`: a UIKit semantic color by its iOS name. */
|
|
529
|
+
system: (name) => ({ name }),
|
|
530
|
+
rgb,
|
|
531
|
+
hex,
|
|
532
|
+
opacity,
|
|
533
|
+
gradient
|
|
534
|
+
};
|
|
535
|
+
const UNIT_POINTS = {
|
|
536
|
+
center: { x: 0.5, y: 0.5 },
|
|
537
|
+
top: { x: 0.5, y: 0 },
|
|
538
|
+
bottom: { x: 0.5, y: 1 },
|
|
539
|
+
leading: { x: 0, y: 0.5 },
|
|
540
|
+
trailing: { x: 1, y: 0.5 },
|
|
541
|
+
topLeading: { x: 0, y: 0 },
|
|
542
|
+
topTrailing: { x: 1, y: 0 },
|
|
543
|
+
bottomLeading: { x: 0, y: 1 },
|
|
544
|
+
bottomTrailing: { x: 1, y: 1 }
|
|
545
|
+
};
|
|
546
|
+
function unitPoint(value) {
|
|
547
|
+
return typeof value === "string" ? UNIT_POINTS[value] : value;
|
|
548
|
+
}
|
|
549
|
+
function stops$1(spec) {
|
|
550
|
+
if (spec.stops) return spec.stops.map((stop) => ({ color: color(stop.color), location: stop.location }));
|
|
551
|
+
const colors = spec.colors ?? [];
|
|
552
|
+
const last = Math.max(colors.length - 1, 1);
|
|
553
|
+
return colors.map((value, index) => ({ color: color(value), location: index / last }));
|
|
554
|
+
}
|
|
555
|
+
function LinearGradient(spec) {
|
|
556
|
+
return { linearGradient: { stops: stops$1(spec), start: unitPoint(spec.startPoint), end: unitPoint(spec.endPoint) } };
|
|
557
|
+
}
|
|
558
|
+
function RadialGradient(spec) {
|
|
559
|
+
return {
|
|
560
|
+
radialGradient: { stops: stops$1(spec), center: unitPoint(spec.center), startRadius: spec.startRadius, endRadius: spec.endRadius }
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
function AngularGradient(spec) {
|
|
564
|
+
return {
|
|
565
|
+
angularGradient: {
|
|
566
|
+
stops: stops$1(spec),
|
|
567
|
+
center: unitPoint(spec.center),
|
|
568
|
+
startAngle: spec.startAngle ?? 0,
|
|
569
|
+
endAngle: spec.endAngle ?? 360
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
function font(value) {
|
|
574
|
+
return typeof value === "string" ? { textStyle: value } : value;
|
|
575
|
+
}
|
|
576
|
+
function system(first, second, third) {
|
|
577
|
+
const result = typeof first === "number" ? { size: first } : { textStyle: first };
|
|
578
|
+
const weight = typeof first === "number" ? second : third;
|
|
579
|
+
const design = typeof first === "number" ? third : second;
|
|
580
|
+
if (weight) result.weight = weight;
|
|
581
|
+
if (design) result.design = design;
|
|
582
|
+
return result;
|
|
583
|
+
}
|
|
584
|
+
function weighted(value, weight) {
|
|
585
|
+
return { ...font(value), weight };
|
|
586
|
+
}
|
|
587
|
+
const Font = { system, weighted, bold: (value) => weighted(value, "bold") };
|
|
588
|
+
function frame(value) {
|
|
589
|
+
const dimension = (n) => n === void 0 ? void 0 : n === Infinity ? "infinity" : n;
|
|
590
|
+
const result = { alignment: value.alignment ?? "center" };
|
|
591
|
+
if (value.width !== void 0) result.width = value.width;
|
|
592
|
+
if (value.height !== void 0) result.height = value.height;
|
|
593
|
+
const minWidth = dimension(value.minWidth);
|
|
594
|
+
const maxWidth = dimension(value.maxWidth);
|
|
595
|
+
const minHeight = dimension(value.minHeight);
|
|
596
|
+
const maxHeight = dimension(value.maxHeight);
|
|
597
|
+
if (minWidth !== void 0) result.minWidth = minWidth;
|
|
598
|
+
if (maxWidth !== void 0) result.maxWidth = maxWidth;
|
|
599
|
+
if (minHeight !== void 0) result.minHeight = minHeight;
|
|
600
|
+
if (maxHeight !== void 0) result.maxHeight = maxHeight;
|
|
601
|
+
return result;
|
|
602
|
+
}
|
|
603
|
+
const DEFAULT_PADDING = 16;
|
|
604
|
+
function padding(value) {
|
|
605
|
+
if (value === true) return insets(["all"], DEFAULT_PADDING);
|
|
606
|
+
if (typeof value === "number") return insets(["all"], value);
|
|
607
|
+
if (Array.isArray(value)) {
|
|
608
|
+
const [edges, amount] = value;
|
|
609
|
+
return insets(Array.isArray(edges) ? edges : [edges], amount ?? DEFAULT_PADDING);
|
|
429
610
|
}
|
|
611
|
+
return value;
|
|
612
|
+
}
|
|
613
|
+
function insets(edges, amount) {
|
|
614
|
+
const result = {};
|
|
615
|
+
for (const edge of edges) {
|
|
616
|
+
if (edge === "all" || edge === "vertical" || edge === "top") result.top = amount;
|
|
617
|
+
if (edge === "all" || edge === "vertical" || edge === "bottom") result.bottom = amount;
|
|
618
|
+
if (edge === "all" || edge === "horizontal" || edge === "leading") result.leading = amount;
|
|
619
|
+
if (edge === "all" || edge === "horizontal" || edge === "trailing") result.trailing = amount;
|
|
620
|
+
}
|
|
621
|
+
return result;
|
|
622
|
+
}
|
|
623
|
+
let installed = null;
|
|
624
|
+
function installTextMeasurer(measurer, remove = false) {
|
|
625
|
+
if (remove) {
|
|
626
|
+
if (installed === measurer) installed = null;
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
installed = measurer;
|
|
630
|
+
}
|
|
631
|
+
function canMeasureText() {
|
|
632
|
+
return installed !== null;
|
|
633
|
+
}
|
|
634
|
+
function measureText(text, font$1 = "body", width) {
|
|
635
|
+
if (!installed) throw new Error("measureText: nothing measures text here yet (mount the app, or run it in a SwiftBrowser Native host)");
|
|
636
|
+
const constrained = typeof width === "number" && Number.isFinite(width) && width > 0 ? width : null;
|
|
637
|
+
return installed(text, font(font$1), constrained);
|
|
430
638
|
}
|
|
431
639
|
function mount(screen, element, options = {}) {
|
|
432
640
|
let renderer = null;
|
|
433
641
|
const root = createSwiftUIRoot((ops) => renderer?.applyOps(ops));
|
|
434
642
|
renderer = new Renderer(screen, (event) => root.dispatch(event));
|
|
643
|
+
const measurer = (text, font2, width) => renderer.measureText(text, font2, width);
|
|
644
|
+
installTextMeasurer(measurer);
|
|
435
645
|
let pageScheme = options.colorScheme ?? "light";
|
|
436
646
|
let preferred = null;
|
|
437
647
|
const environment = {
|
|
@@ -441,6 +651,7 @@ function mount(screen, element, options = {}) {
|
|
|
441
651
|
};
|
|
442
652
|
renderer.contentHeight = options.contentHeight ?? false;
|
|
443
653
|
if (options.assets) renderer.setAssetCatalog(options.assets);
|
|
654
|
+
if (options.app) browserEnvironment.files?.setApp?.(options.app);
|
|
444
655
|
renderer.setEnvironment({ colorScheme: environment.colorScheme, dynamicTypeSize: environment.dynamicTypeSize });
|
|
445
656
|
if (options.safeArea !== void 0) renderer.setSafeArea(options.safeArea);
|
|
446
657
|
root.setEnvironment(environment);
|
|
@@ -483,6 +694,7 @@ function mount(screen, element, options = {}) {
|
|
|
483
694
|
stopKeyboard?.();
|
|
484
695
|
browserHistory?.dispose();
|
|
485
696
|
screen.removeEventListener("sb:color-scheme", onPreferredScheme);
|
|
697
|
+
installTextMeasurer(measurer, true);
|
|
486
698
|
root.unmount();
|
|
487
699
|
renderer?.dispose();
|
|
488
700
|
}
|
|
@@ -498,11 +710,233 @@ function RegisterDestination({ destination }) {
|
|
|
498
710
|
navigation?.registerDestination(destination);
|
|
499
711
|
return null;
|
|
500
712
|
}
|
|
713
|
+
const EMPTY = { canUndo: false, canRedo: false, undoActionName: "", redoActionName: "" };
|
|
714
|
+
const defaultSchedule = (close) => {
|
|
715
|
+
if (typeof queueMicrotask === "function") queueMicrotask(close);
|
|
716
|
+
else void Promise.resolve().then(close);
|
|
717
|
+
};
|
|
718
|
+
class UndoManager {
|
|
719
|
+
/** Whether registrations in one event (one task of the event loop) form one group; true, as `groupsByEvent`. */
|
|
720
|
+
groupsByEvent = true;
|
|
721
|
+
/** How many groups the undo stack keeps; 0 for no limit, as `levelsOfUndo`. */
|
|
722
|
+
levelsOfUndo = 0;
|
|
723
|
+
undoStack = [];
|
|
724
|
+
redoStack = [];
|
|
725
|
+
/** The group registrations go in: an explicit group, the event's group, or the inverse of what is being undone or redone. */
|
|
726
|
+
open = null;
|
|
727
|
+
/** How deep `beginGrouping` is; 0 when `open` is the event's group or an inverse. */
|
|
728
|
+
depth = 0;
|
|
729
|
+
/** Whether `open` is the event's group, which `schedule` closes. */
|
|
730
|
+
byEvent = false;
|
|
731
|
+
/** The last keyed group closed onto the undo stack, which a registration with its key joins. */
|
|
732
|
+
coalescing = null;
|
|
733
|
+
undoing = false;
|
|
734
|
+
redoing = false;
|
|
735
|
+
snapshot = EMPTY;
|
|
736
|
+
listeners = /* @__PURE__ */ new Set();
|
|
737
|
+
schedule;
|
|
738
|
+
host;
|
|
739
|
+
stopListening = null;
|
|
740
|
+
constructor(options = {}) {
|
|
741
|
+
this.schedule = options.schedule ?? defaultSchedule;
|
|
742
|
+
this.host = options.host === void 0 ? browserEnvironment.undoManager ?? null : options.host;
|
|
743
|
+
if (this.host) {
|
|
744
|
+
this.stopListening = this.host.listen({ undo: () => this.undo(), redo: () => this.redo() });
|
|
745
|
+
this.host.update(this.snapshot);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
get canUndo() {
|
|
749
|
+
return this.undoStack.length > 0;
|
|
750
|
+
}
|
|
751
|
+
get canRedo() {
|
|
752
|
+
return this.redoStack.length > 0;
|
|
753
|
+
}
|
|
754
|
+
/** The name of what `undo` takes back ('' for none): "Move" for an Edit menu's "Undo Move". */
|
|
755
|
+
get undoActionName() {
|
|
756
|
+
return this.undoStack[this.undoStack.length - 1]?.name ?? "";
|
|
757
|
+
}
|
|
758
|
+
get redoActionName() {
|
|
759
|
+
return this.redoStack[this.redoStack.length - 1]?.name ?? "";
|
|
760
|
+
}
|
|
761
|
+
get isUndoing() {
|
|
762
|
+
return this.undoing;
|
|
763
|
+
}
|
|
764
|
+
get isRedoing() {
|
|
765
|
+
return this.redoing;
|
|
766
|
+
}
|
|
767
|
+
/** What the stacks can do, one object per change (for `useSyncExternalStore`). */
|
|
768
|
+
get state() {
|
|
769
|
+
return this.snapshot;
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Registers what undoes the change just made, under `name`. Run by `undo`,
|
|
773
|
+
* the closure registers its own inverse the same way, which lands on the
|
|
774
|
+
* redo stack because the manager is undoing.
|
|
775
|
+
*/
|
|
776
|
+
registerUndo(name, undo, options = {}) {
|
|
777
|
+
const action = { name, undo, target: options.target };
|
|
778
|
+
if (this.undoing || this.redoing) {
|
|
779
|
+
this.open.actions.push(action);
|
|
780
|
+
this.changed();
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
this.redoStack = [];
|
|
784
|
+
this.coalescing = this.coalescing && this.undoStack[this.undoStack.length - 1] === this.coalescing ? this.coalescing : null;
|
|
785
|
+
if (this.open) {
|
|
786
|
+
if (!this.undoStack.includes(this.open)) this.push(this.open);
|
|
787
|
+
this.open.actions.push(action);
|
|
788
|
+
if (!this.open.named) this.open.name = name;
|
|
789
|
+
} else if (this.coalescing && options.key !== void 0 && this.coalescing.key === options.key) {
|
|
790
|
+
this.coalescing.actions.push(action);
|
|
791
|
+
this.coalescing.name = name;
|
|
792
|
+
} else {
|
|
793
|
+
const group = { name, named: false, actions: [action], key: options.key };
|
|
794
|
+
this.push(group);
|
|
795
|
+
this.coalescing = null;
|
|
796
|
+
if (this.groupsByEvent) {
|
|
797
|
+
this.open = group;
|
|
798
|
+
this.byEvent = true;
|
|
799
|
+
this.schedule(() => {
|
|
800
|
+
if (this.open === group && this.byEvent) this.close();
|
|
801
|
+
});
|
|
802
|
+
} else {
|
|
803
|
+
this.coalescing = group.key !== void 0 ? group : null;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
this.changed();
|
|
807
|
+
}
|
|
808
|
+
/** Takes back the top group of changes; its inverses become the top of the redo stack. */
|
|
809
|
+
undo() {
|
|
810
|
+
this.endOpenGroups();
|
|
811
|
+
const group = this.undoStack.pop();
|
|
812
|
+
if (!group) return;
|
|
813
|
+
this.coalescing = null;
|
|
814
|
+
this.run(group, "undo");
|
|
815
|
+
}
|
|
816
|
+
redo() {
|
|
817
|
+
this.endOpenGroups();
|
|
818
|
+
const group = this.redoStack.pop();
|
|
819
|
+
if (!group) return;
|
|
820
|
+
this.run(group, "redo");
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Starts a group: every registration until `endGrouping` undoes as one
|
|
824
|
+
* (`beginUndoGrouping`). Nested calls join the outer group. The group is
|
|
825
|
+
* named `name`, else after its last registration.
|
|
826
|
+
*/
|
|
827
|
+
beginGrouping(name) {
|
|
828
|
+
if (this.undoing || this.redoing) return;
|
|
829
|
+
this.depth += 1;
|
|
830
|
+
if (this.depth > 1) return;
|
|
831
|
+
if (this.open) this.close();
|
|
832
|
+
this.open = { name: name ?? "", named: name !== void 0, actions: [], key: void 0 };
|
|
833
|
+
this.byEvent = false;
|
|
834
|
+
this.coalescing = null;
|
|
835
|
+
}
|
|
836
|
+
/** Ends the group `beginGrouping` started; one that registered nothing is dropped. */
|
|
837
|
+
endGrouping() {
|
|
838
|
+
if (this.depth === 0) return;
|
|
839
|
+
this.depth -= 1;
|
|
840
|
+
if (this.depth === 0) this.close();
|
|
841
|
+
}
|
|
842
|
+
/** `body` as one group named `name`. */
|
|
843
|
+
group(name, body) {
|
|
844
|
+
this.beginGrouping(name);
|
|
845
|
+
try {
|
|
846
|
+
return body();
|
|
847
|
+
} finally {
|
|
848
|
+
this.endGrouping();
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
/** Forgets every registration, or those `target` made (`removeAllActions(withTarget:)`), on both stacks and in the open group. */
|
|
852
|
+
removeAllActions(target) {
|
|
853
|
+
const mine = (action) => target === void 0 || action.target === target;
|
|
854
|
+
const strip = (groups) => groups.filter((group) => {
|
|
855
|
+
group.actions = group.actions.filter((action) => !mine(action));
|
|
856
|
+
return group.actions.length > 0;
|
|
857
|
+
});
|
|
858
|
+
this.undoStack = strip(this.undoStack);
|
|
859
|
+
this.redoStack = strip(this.redoStack);
|
|
860
|
+
if (this.open) this.open.actions = this.open.actions.filter((action) => !mine(action));
|
|
861
|
+
if (this.coalescing && !this.undoStack.includes(this.coalescing)) this.coalescing = null;
|
|
862
|
+
this.changed();
|
|
863
|
+
}
|
|
864
|
+
/** Calls `listener` after each change to `state`; returns what stops it. */
|
|
865
|
+
subscribe = (listener) => {
|
|
866
|
+
this.listeners.add(listener);
|
|
867
|
+
return () => {
|
|
868
|
+
this.listeners.delete(listener);
|
|
869
|
+
};
|
|
870
|
+
};
|
|
871
|
+
/** Stops listening to the host, for a manager made in a test. */
|
|
872
|
+
dispose() {
|
|
873
|
+
this.stopListening?.();
|
|
874
|
+
this.stopListening = null;
|
|
875
|
+
}
|
|
876
|
+
push(group) {
|
|
877
|
+
this.undoStack.push(group);
|
|
878
|
+
if (this.levelsOfUndo > 0 && this.undoStack.length > this.levelsOfUndo) this.undoStack.splice(0, this.undoStack.length - this.levelsOfUndo);
|
|
879
|
+
}
|
|
880
|
+
/** Closes `open`; a keyed group on top stays open to its key. */
|
|
881
|
+
close() {
|
|
882
|
+
const group = this.open;
|
|
883
|
+
this.open = null;
|
|
884
|
+
this.byEvent = false;
|
|
885
|
+
if (group && group.key !== void 0 && this.undoStack[this.undoStack.length - 1] === group) this.coalescing = group;
|
|
886
|
+
}
|
|
887
|
+
endOpenGroups() {
|
|
888
|
+
this.depth = 0;
|
|
889
|
+
if (this.open) this.close();
|
|
890
|
+
}
|
|
891
|
+
run(group, direction) {
|
|
892
|
+
const inverse = { name: group.name, named: true, actions: [], key: void 0 };
|
|
893
|
+
this.open = inverse;
|
|
894
|
+
this.byEvent = false;
|
|
895
|
+
if (direction === "undo") this.undoing = true;
|
|
896
|
+
else this.redoing = true;
|
|
897
|
+
try {
|
|
898
|
+
for (let i = group.actions.length - 1; i >= 0; i -= 1) group.actions[i].undo();
|
|
899
|
+
} finally {
|
|
900
|
+
this.undoing = false;
|
|
901
|
+
this.redoing = false;
|
|
902
|
+
this.open = null;
|
|
903
|
+
if (inverse.actions.length > 0) {
|
|
904
|
+
if (direction === "undo") this.redoStack.push(inverse);
|
|
905
|
+
else this.push(inverse);
|
|
906
|
+
}
|
|
907
|
+
this.changed();
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
changed() {
|
|
911
|
+
const next = { canUndo: this.canUndo, canRedo: this.canRedo, undoActionName: this.undoActionName, redoActionName: this.redoActionName };
|
|
912
|
+
const last = this.snapshot;
|
|
913
|
+
if (next.canUndo === last.canUndo && next.canRedo === last.canRedo && next.undoActionName === last.undoActionName && next.redoActionName === last.redoActionName) return;
|
|
914
|
+
this.snapshot = next;
|
|
915
|
+
this.host?.update(next);
|
|
916
|
+
for (const listener of [...this.listeners]) listener();
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
const UndoManagerContext = createContext(null);
|
|
920
|
+
let shared = null;
|
|
921
|
+
function sharedUndoManager() {
|
|
922
|
+
shared ??= new UndoManager();
|
|
923
|
+
return shared;
|
|
924
|
+
}
|
|
925
|
+
function useUndoManager() {
|
|
926
|
+
const manager = useContext(UndoManagerContext) ?? sharedUndoManager();
|
|
927
|
+
useSyncExternalStore(
|
|
928
|
+
manager.subscribe,
|
|
929
|
+
() => manager.state,
|
|
930
|
+
() => manager.state
|
|
931
|
+
);
|
|
932
|
+
return manager;
|
|
933
|
+
}
|
|
501
934
|
const OWN = [
|
|
502
935
|
EnvironmentContext,
|
|
503
936
|
ViewEnvironmentContext,
|
|
504
937
|
NavigationContext,
|
|
505
|
-
DismissContext
|
|
938
|
+
DismissContext,
|
|
939
|
+
UndoManagerContext
|
|
506
940
|
];
|
|
507
941
|
function useContextBridge(contexts = [], options = {}) {
|
|
508
942
|
const own = options.environment === false ? OWN.filter((context) => context !== EnvironmentContext) : OWN;
|
|
@@ -620,219 +1054,83 @@ function Draggable({ payload, children }) {
|
|
|
620
1054
|
function itemValue(item) {
|
|
621
1055
|
if (item.text !== void 0) {
|
|
622
1056
|
if (item.type === "text/uri-list") {
|
|
623
|
-
try {
|
|
624
|
-
return new URL(item.text.split(/\r?\n/)[0].trim());
|
|
625
|
-
} catch {
|
|
626
|
-
return item.text;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
return item.text;
|
|
630
|
-
}
|
|
631
|
-
const binary = atob(item.data ?? "");
|
|
632
|
-
const bytes = new Uint8Array(binary.length);
|
|
633
|
-
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
634
|
-
return typeof File !== "undefined" ? new File([bytes], item.name ?? "file", { type: item.type }) : bytes;
|
|
635
|
-
}
|
|
636
|
-
function DropDestination({ action, isTargeted, accepts, children }) {
|
|
637
|
-
return sb(
|
|
638
|
-
"dropdestination",
|
|
639
|
-
{ accepts: accepts ?? [] },
|
|
640
|
-
{
|
|
641
|
-
drop: (event) => {
|
|
642
|
-
if (event.phase === "entered") {
|
|
643
|
-
isTargeted?.(true);
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
isTargeted?.(false);
|
|
647
|
-
if (event.phase !== "performed") return;
|
|
648
|
-
let items;
|
|
649
|
-
if (event.source !== void 0) items = active && active.id === event.source ? [active.payload] : [];
|
|
650
|
-
else items = (event.items ?? []).map(itemValue);
|
|
651
|
-
if (items.length > 0) action(items, { x: event.x, y: event.y });
|
|
652
|
-
}
|
|
653
|
-
},
|
|
654
|
-
children
|
|
655
|
-
);
|
|
656
|
-
}
|
|
657
|
-
function FileImporterPresenter(spec) {
|
|
658
|
-
const [presented, setPresented] = readBinding(spec.isPresented);
|
|
659
|
-
const latest = useRef(spec);
|
|
660
|
-
latest.current = spec;
|
|
661
|
-
const choosing = useRef(false);
|
|
662
|
-
useLayoutEffect(() => {
|
|
663
|
-
if (!presented || choosing.current) return;
|
|
664
|
-
const { allowedContentTypes, allowsMultipleSelection } = latest.current;
|
|
665
|
-
const pick = browserEnvironment.pickFiles;
|
|
666
|
-
if (!pick) {
|
|
667
|
-
setPresented(false);
|
|
668
|
-
latest.current.onCancellation?.();
|
|
669
|
-
return;
|
|
670
|
-
}
|
|
671
|
-
choosing.current = true;
|
|
672
|
-
void pick({ accept: allowedContentTypes.join(","), multiple: allowsMultipleSelection === true }).then((files) => {
|
|
673
|
-
choosing.current = false;
|
|
674
|
-
setPresented(false);
|
|
675
|
-
if (files.length > 0) latest.current.onCompletion(files);
|
|
676
|
-
else latest.current.onCancellation?.();
|
|
677
|
-
});
|
|
678
|
-
}, [presented, setPresented]);
|
|
679
|
-
return null;
|
|
680
|
-
}
|
|
681
|
-
const HIERARCHY = { primary: 1, secondary: 2, tertiary: 3, quaternary: 4 };
|
|
682
|
-
function rgb(r2, g, b, a = 1) {
|
|
683
|
-
return { r: r2, g, b, a };
|
|
684
|
-
}
|
|
685
|
-
function hex(value) {
|
|
686
|
-
const digits = value.replace("#", "");
|
|
687
|
-
const wide = digits.length === 3 || digits.length === 4 ? [...digits].map((d) => d + d).join("") : digits;
|
|
688
|
-
const n = (at) => parseInt(wide.slice(at, at + 2), 16) / 255;
|
|
689
|
-
return { r: n(0), g: n(2), b: n(4), a: wide.length >= 8 ? n(6) : 1 };
|
|
690
|
-
}
|
|
691
|
-
function shapeStyle(value) {
|
|
692
|
-
if (typeof value !== "string") return value;
|
|
693
|
-
if (value in HIERARCHY) return { hierarchical: HIERARCHY[value] };
|
|
694
|
-
if (value === "white") return rgb(1, 1, 1);
|
|
695
|
-
if (value === "black") return rgb(0, 0, 0);
|
|
696
|
-
if (value.startsWith("#")) return hex(value);
|
|
697
|
-
return { name: value };
|
|
698
|
-
}
|
|
699
|
-
function color(value) {
|
|
700
|
-
return shapeStyle(value);
|
|
701
|
-
}
|
|
702
|
-
function opacity(value, a) {
|
|
703
|
-
const style = shapeStyle(value);
|
|
704
|
-
if ("r" in style) return { ...style, a: style.a * a };
|
|
705
|
-
if ("name" in style) return { ...style, a: (style.a ?? 1) * a };
|
|
706
|
-
if ("named" in style) return { ...style, opacity: style.opacity * a };
|
|
707
|
-
return { ...style, opacity: (style.opacity ?? 1) * a };
|
|
708
|
-
}
|
|
709
|
-
function gradient(value) {
|
|
710
|
-
return { colorGradient: color(value) };
|
|
711
|
-
}
|
|
712
|
-
const ColorValues = {
|
|
713
|
-
red: { name: "red" },
|
|
714
|
-
orange: { name: "orange" },
|
|
715
|
-
yellow: { name: "yellow" },
|
|
716
|
-
green: { name: "green" },
|
|
717
|
-
mint: { name: "mint" },
|
|
718
|
-
teal: { name: "teal" },
|
|
719
|
-
cyan: { name: "cyan" },
|
|
720
|
-
blue: { name: "blue" },
|
|
721
|
-
indigo: { name: "indigo" },
|
|
722
|
-
purple: { name: "purple" },
|
|
723
|
-
pink: { name: "pink" },
|
|
724
|
-
brown: { name: "brown" },
|
|
725
|
-
gray: { name: "gray" },
|
|
726
|
-
white: rgb(1, 1, 1),
|
|
727
|
-
black: rgb(0, 0, 0),
|
|
728
|
-
clear: { name: "clear" },
|
|
729
|
-
primary: { name: "primary" },
|
|
730
|
-
secondary: { name: "secondary" },
|
|
731
|
-
accent: { name: "accent" },
|
|
732
|
-
/** `Color(uiColor:)` / `Color(.name)`: a UIKit semantic color by its iOS name. */
|
|
733
|
-
system: (name) => ({ name }),
|
|
734
|
-
rgb,
|
|
735
|
-
hex,
|
|
736
|
-
opacity,
|
|
737
|
-
gradient
|
|
738
|
-
};
|
|
739
|
-
const UNIT_POINTS = {
|
|
740
|
-
center: { x: 0.5, y: 0.5 },
|
|
741
|
-
top: { x: 0.5, y: 0 },
|
|
742
|
-
bottom: { x: 0.5, y: 1 },
|
|
743
|
-
leading: { x: 0, y: 0.5 },
|
|
744
|
-
trailing: { x: 1, y: 0.5 },
|
|
745
|
-
topLeading: { x: 0, y: 0 },
|
|
746
|
-
topTrailing: { x: 1, y: 0 },
|
|
747
|
-
bottomLeading: { x: 0, y: 1 },
|
|
748
|
-
bottomTrailing: { x: 1, y: 1 }
|
|
749
|
-
};
|
|
750
|
-
function unitPoint(value) {
|
|
751
|
-
return typeof value === "string" ? UNIT_POINTS[value] : value;
|
|
752
|
-
}
|
|
753
|
-
function stops$1(spec) {
|
|
754
|
-
if (spec.stops) return spec.stops.map((stop) => ({ color: color(stop.color), location: stop.location }));
|
|
755
|
-
const colors = spec.colors ?? [];
|
|
756
|
-
const last = Math.max(colors.length - 1, 1);
|
|
757
|
-
return colors.map((value, index) => ({ color: color(value), location: index / last }));
|
|
758
|
-
}
|
|
759
|
-
function LinearGradient(spec) {
|
|
760
|
-
return { linearGradient: { stops: stops$1(spec), start: unitPoint(spec.startPoint), end: unitPoint(spec.endPoint) } };
|
|
761
|
-
}
|
|
762
|
-
function RadialGradient(spec) {
|
|
763
|
-
return {
|
|
764
|
-
radialGradient: { stops: stops$1(spec), center: unitPoint(spec.center), startRadius: spec.startRadius, endRadius: spec.endRadius }
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
function AngularGradient(spec) {
|
|
768
|
-
return {
|
|
769
|
-
angularGradient: {
|
|
770
|
-
stops: stops$1(spec),
|
|
771
|
-
center: unitPoint(spec.center),
|
|
772
|
-
startAngle: spec.startAngle ?? 0,
|
|
773
|
-
endAngle: spec.endAngle ?? 360
|
|
774
|
-
}
|
|
775
|
-
};
|
|
776
|
-
}
|
|
777
|
-
function font(value) {
|
|
778
|
-
return typeof value === "string" ? { textStyle: value } : value;
|
|
779
|
-
}
|
|
780
|
-
function system(first, second, third) {
|
|
781
|
-
const result = typeof first === "number" ? { size: first } : { textStyle: first };
|
|
782
|
-
const weight = typeof first === "number" ? second : third;
|
|
783
|
-
const design = typeof first === "number" ? third : second;
|
|
784
|
-
if (weight) result.weight = weight;
|
|
785
|
-
if (design) result.design = design;
|
|
786
|
-
return result;
|
|
787
|
-
}
|
|
788
|
-
function weighted(value, weight) {
|
|
789
|
-
return { ...font(value), weight };
|
|
790
|
-
}
|
|
791
|
-
const Font = { system, weighted, bold: (value) => weighted(value, "bold") };
|
|
792
|
-
function frame(value) {
|
|
793
|
-
const dimension = (n) => n === void 0 ? void 0 : n === Infinity ? "infinity" : n;
|
|
794
|
-
const result = { alignment: value.alignment ?? "center" };
|
|
795
|
-
if (value.width !== void 0) result.width = value.width;
|
|
796
|
-
if (value.height !== void 0) result.height = value.height;
|
|
797
|
-
const minWidth = dimension(value.minWidth);
|
|
798
|
-
const maxWidth = dimension(value.maxWidth);
|
|
799
|
-
const minHeight = dimension(value.minHeight);
|
|
800
|
-
const maxHeight = dimension(value.maxHeight);
|
|
801
|
-
if (minWidth !== void 0) result.minWidth = minWidth;
|
|
802
|
-
if (maxWidth !== void 0) result.maxWidth = maxWidth;
|
|
803
|
-
if (minHeight !== void 0) result.minHeight = minHeight;
|
|
804
|
-
if (maxHeight !== void 0) result.maxHeight = maxHeight;
|
|
805
|
-
return result;
|
|
806
|
-
}
|
|
807
|
-
const DEFAULT_PADDING = 16;
|
|
808
|
-
function padding(value) {
|
|
809
|
-
if (value === true) return insets(["all"], DEFAULT_PADDING);
|
|
810
|
-
if (typeof value === "number") return insets(["all"], value);
|
|
811
|
-
if (Array.isArray(value)) {
|
|
812
|
-
const [edges, amount] = value;
|
|
813
|
-
return insets(Array.isArray(edges) ? edges : [edges], amount ?? DEFAULT_PADDING);
|
|
1057
|
+
try {
|
|
1058
|
+
return new URL(item.text.split(/\r?\n/)[0].trim());
|
|
1059
|
+
} catch {
|
|
1060
|
+
return item.text;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
return item.text;
|
|
814
1064
|
}
|
|
815
|
-
|
|
1065
|
+
const binary = atob(item.data ?? "");
|
|
1066
|
+
const bytes = new Uint8Array(binary.length);
|
|
1067
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
1068
|
+
return typeof File !== "undefined" ? new File([bytes], item.name ?? "file", { type: item.type }) : bytes;
|
|
816
1069
|
}
|
|
817
|
-
function
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
1070
|
+
function DropDestination({ action, isTargeted, accepts, children }) {
|
|
1071
|
+
return sb(
|
|
1072
|
+
"dropdestination",
|
|
1073
|
+
{ accepts: accepts ?? [] },
|
|
1074
|
+
{
|
|
1075
|
+
drop: (event) => {
|
|
1076
|
+
if (event.phase === "entered") {
|
|
1077
|
+
isTargeted?.(true);
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
isTargeted?.(false);
|
|
1081
|
+
if (event.phase !== "performed") return;
|
|
1082
|
+
let items;
|
|
1083
|
+
if (event.source !== void 0) items = active && active.id === event.source ? [active.payload] : [];
|
|
1084
|
+
else items = (event.items ?? []).map(itemValue);
|
|
1085
|
+
if (items.length > 0) action(items, { x: event.x, y: event.y });
|
|
1086
|
+
}
|
|
1087
|
+
},
|
|
1088
|
+
children
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
function FileImporterPresenter(spec) {
|
|
1092
|
+
const [presented, setPresented] = readBinding(spec.isPresented);
|
|
1093
|
+
const latest = useRef(spec);
|
|
1094
|
+
latest.current = spec;
|
|
1095
|
+
const choosing = useRef(false);
|
|
1096
|
+
useLayoutEffect(() => {
|
|
1097
|
+
if (!presented || choosing.current) return;
|
|
1098
|
+
const { allowedContentTypes, allowsMultipleSelection } = latest.current;
|
|
1099
|
+
const pick = browserEnvironment.pickFiles;
|
|
1100
|
+
if (!pick) {
|
|
1101
|
+
setPresented(false);
|
|
1102
|
+
latest.current.onCancellation?.();
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
choosing.current = true;
|
|
1106
|
+
void pick({ accept: allowedContentTypes.join(","), multiple: allowsMultipleSelection === true }).then((files) => {
|
|
1107
|
+
choosing.current = false;
|
|
1108
|
+
setPresented(false);
|
|
1109
|
+
if (files.length > 0) latest.current.onCompletion(files);
|
|
1110
|
+
else latest.current.onCancellation?.();
|
|
1111
|
+
});
|
|
1112
|
+
}, [presented, setPresented]);
|
|
1113
|
+
return null;
|
|
826
1114
|
}
|
|
1115
|
+
const isPoint = (value) => typeof value === "object" && value !== null && ("x" in value || "y" in value);
|
|
827
1116
|
function command(request) {
|
|
828
1117
|
return request.axes ? { anchor: request.anchor, serial: request.serial, axes: request.axes } : { anchor: request.anchor, serial: request.serial };
|
|
829
1118
|
}
|
|
1119
|
+
function offsetCommand(request) {
|
|
1120
|
+
const out = { serial: request.serial };
|
|
1121
|
+
if (typeof request.point.x === "number") out.x = request.point.x;
|
|
1122
|
+
if (typeof request.point.y === "number") out.y = request.point.y;
|
|
1123
|
+
return out;
|
|
1124
|
+
}
|
|
830
1125
|
const ScrollRequestContext = createContext(null);
|
|
831
1126
|
let nextSerial = 1;
|
|
832
1127
|
function ScrollViewReader({ children }) {
|
|
833
1128
|
const [request, setRequest] = useState(null);
|
|
834
1129
|
const proxy = {
|
|
835
|
-
scrollTo: (
|
|
1130
|
+
scrollTo: (target, anchor, options) => {
|
|
1131
|
+
if (isPoint(target)) setRequest({ point: target, serial: nextSerial++ });
|
|
1132
|
+
else setRequest({ id: target, anchor: anchor === void 0 ? null : unitPoint(anchor), serial: nextSerial++, ...options?.axes ? { axes: options.axes } : {} });
|
|
1133
|
+
}
|
|
836
1134
|
};
|
|
837
1135
|
return /* @__PURE__ */ jsx(ScrollRequestContext.Provider, { value: request, children: children(proxy) });
|
|
838
1136
|
}
|
|
@@ -840,7 +1138,7 @@ function ScrollTarget({ id, children }) {
|
|
|
840
1138
|
const request = useContext(ScrollRequestContext);
|
|
841
1139
|
const position = useContext(ScrollPositionContext);
|
|
842
1140
|
const on = position ? { scrollposition: () => position.report(id) } : void 0;
|
|
843
|
-
if (request && request.id === id) return sb("styled", { style: { scrollID: true, scrollTo: command(request) } }, on, children);
|
|
1141
|
+
if (request && "id" in request && request.id === id) return sb("styled", { style: { scrollID: true, scrollTo: command(request) } }, on, children);
|
|
844
1142
|
if (on) return sb("styled", { style: { scrollID: true } }, on, children);
|
|
845
1143
|
return styled({ scrollID: true }, children);
|
|
846
1144
|
}
|
|
@@ -855,25 +1153,33 @@ function useScrollPosition(binding, anchor, axes) {
|
|
|
855
1153
|
const point = anchor === void 0 ? null : unitPoint(anchor);
|
|
856
1154
|
if (value !== last.current) {
|
|
857
1155
|
last.current = value;
|
|
858
|
-
if (value
|
|
1156
|
+
if (isPoint(value)) request.current = { point: value, serial: nextSerial++ };
|
|
1157
|
+
else if (value !== null && value !== reported.current) request.current = { id: value, anchor: point, serial: nextSerial++, axes };
|
|
859
1158
|
reported.current = void 0;
|
|
860
1159
|
}
|
|
861
|
-
if (!bound) return null;
|
|
862
|
-
const props = { reportsPosition: true };
|
|
863
|
-
if (point) props["positionAnchor"] = point;
|
|
864
1160
|
const own = request.current;
|
|
865
1161
|
const current = own && (!outer || own.serial > outer.serial) ? own : outer;
|
|
1162
|
+
const offset = useOffsetCommand(bound ? current : outer);
|
|
1163
|
+
if (!bound) return offset ? { props: offset, provide: (children) => children } : null;
|
|
1164
|
+
const props = { reportsPosition: true, ...offset };
|
|
1165
|
+
if (point) props["positionAnchor"] = point;
|
|
866
1166
|
const context = {
|
|
867
1167
|
report: (id) => {
|
|
868
1168
|
reported.current = id;
|
|
869
1169
|
set(id);
|
|
870
1170
|
}
|
|
871
1171
|
};
|
|
1172
|
+
const inner = current && "point" in current ? outer && "id" in outer ? outer : null : current;
|
|
872
1173
|
return {
|
|
873
1174
|
props,
|
|
874
|
-
provide: (children) => /* @__PURE__ */ jsx(ScrollPositionContext.Provider, { value: context, children: /* @__PURE__ */ jsx(ScrollRequestContext.Provider, { value:
|
|
1175
|
+
provide: (children) => /* @__PURE__ */ jsx(ScrollPositionContext.Provider, { value: context, children: /* @__PURE__ */ jsx(ScrollRequestContext.Provider, { value: inner, children }) })
|
|
875
1176
|
};
|
|
876
1177
|
}
|
|
1178
|
+
function useOffsetCommand(request) {
|
|
1179
|
+
const seen = useRef(null);
|
|
1180
|
+
if (request && "point" in request && (!seen.current || request.serial > seen.current.serial)) seen.current = offsetCommand(request);
|
|
1181
|
+
return seen.current ? { scrollOffset: seen.current } : null;
|
|
1182
|
+
}
|
|
877
1183
|
const ScrollSettingsContext = createContext({});
|
|
878
1184
|
function edgesOf(edges) {
|
|
879
1185
|
const list = Array.isArray(edges) ? edges : [edges];
|
|
@@ -966,7 +1272,7 @@ function LazyGroup({ rows, start = 0, wrap: wrap2 }) {
|
|
|
966
1272
|
const axis = useContext(LazyContext);
|
|
967
1273
|
const request = useContext(ScrollRequestContext);
|
|
968
1274
|
const [count, setCount] = useState(LAZY_BATCH);
|
|
969
|
-
const target = request ? rows.findIndex((row) => rowMatches(row, request.id)) : -1;
|
|
1275
|
+
const target = request && "id" in request ? rows.findIndex((row) => rowMatches(row, request.id)) : -1;
|
|
970
1276
|
const needed = target >= 0 ? Math.ceil((target + 1) / LAZY_BATCH) * LAZY_BATCH : 0;
|
|
971
1277
|
useEffect(() => {
|
|
972
1278
|
if (needed > count) setCount(needed);
|
|
@@ -996,17 +1302,23 @@ function rowMatches(row, id) {
|
|
|
996
1302
|
}
|
|
997
1303
|
return false;
|
|
998
1304
|
}
|
|
1305
|
+
const PinnedContext = createContext(null);
|
|
1306
|
+
function pinning(pinnedViews, children) {
|
|
1307
|
+
return pinnedViews && pinnedViews.length > 0 ? /* @__PURE__ */ jsx(PinnedContext.Provider, { value: pinnedViews, children }) : children;
|
|
1308
|
+
}
|
|
999
1309
|
function VStack(props) {
|
|
1000
1310
|
const { own, modifiers } = splitProps(props);
|
|
1001
1311
|
const stack = { spacing: own.spacing ?? null, alignment: own.alignment ?? "center" };
|
|
1002
1312
|
if (own.scrollTargetLayout) stack["scrollTarget"] = true;
|
|
1003
|
-
|
|
1313
|
+
if (own.pinnedViews && own.pinnedViews.length > 0) stack["pinned"] = own.pinnedViews;
|
|
1314
|
+
return applyModifiers(sb("vstack", stack, void 0, pinning(own.pinnedViews, own.children)), modifiers);
|
|
1004
1315
|
}
|
|
1005
1316
|
function HStack(props) {
|
|
1006
1317
|
const { own, modifiers } = splitProps(props);
|
|
1007
1318
|
const stack = { spacing: own.spacing ?? null, alignment: own.alignment ?? "center" };
|
|
1008
1319
|
if (own.scrollTargetLayout) stack["scrollTarget"] = true;
|
|
1009
|
-
|
|
1320
|
+
if (own.pinnedViews && own.pinnedViews.length > 0) stack["pinned"] = own.pinnedViews;
|
|
1321
|
+
return applyModifiers(sb("hstack", stack, void 0, pinning(own.pinnedViews, own.children)), modifiers);
|
|
1010
1322
|
}
|
|
1011
1323
|
function ZStack(props) {
|
|
1012
1324
|
const { own, modifiers } = splitProps(props);
|
|
@@ -1020,15 +1332,45 @@ function Divider(props) {
|
|
|
1020
1332
|
const { modifiers } = splitProps(props);
|
|
1021
1333
|
return applyModifiers(sb("divider", {}), modifiers);
|
|
1022
1334
|
}
|
|
1335
|
+
function scrollGeometryOf(event) {
|
|
1336
|
+
return {
|
|
1337
|
+
contentOffset: { x: event.offset.x, y: event.offset.y },
|
|
1338
|
+
contentSize: { width: event.contentSize.width, height: event.contentSize.height },
|
|
1339
|
+
containerSize: { width: event.containerSize.width, height: event.containerSize.height },
|
|
1340
|
+
contentInsets: { ...event.contentInsets },
|
|
1341
|
+
visibleRect: { x: event.offset.x, y: event.offset.y, width: event.containerSize.width, height: event.containerSize.height }
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
function useScrollGeometryChange(handler) {
|
|
1345
|
+
const last = useRef(null);
|
|
1346
|
+
if (!handler) return { props: {} };
|
|
1347
|
+
return {
|
|
1348
|
+
props: { reportsGeometry: true },
|
|
1349
|
+
handlers: {
|
|
1350
|
+
scrollgeometry: (event) => {
|
|
1351
|
+
const geometry = scrollGeometryOf(event);
|
|
1352
|
+
if (typeof handler === "function") return handler(geometry);
|
|
1353
|
+
const value = handler.of(geometry);
|
|
1354
|
+
const previous = last.current;
|
|
1355
|
+
if (previous && JSON.stringify(previous.value) === JSON.stringify(value)) return;
|
|
1356
|
+
last.current = { value };
|
|
1357
|
+
handler.action(value, previous ? previous.value : value);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1023
1362
|
function ScrollView(props) {
|
|
1024
1363
|
const { own, modifiers } = splitProps(props);
|
|
1025
1364
|
const refresh = useRefreshable(takeModifier(modifiers, "refreshable"));
|
|
1026
1365
|
const ownSettings = takeScrollSettings(modifiers);
|
|
1027
1366
|
const settings = useScrollSettings(ownSettings);
|
|
1028
1367
|
const axes = own.axes ?? "vertical";
|
|
1029
|
-
const
|
|
1368
|
+
const geometry = useScrollGeometryChange(own.onScrollGeometryChange);
|
|
1369
|
+
const scroll = { axes, ...refresh.props, ...geometry.props };
|
|
1370
|
+
const handlers = refresh.handlers || geometry.handlers ? { ...refresh.handlers, ...geometry.handlers } : void 0;
|
|
1030
1371
|
if (own.scrollTargetBehavior) scroll["snap"] = own.scrollTargetBehavior;
|
|
1031
1372
|
if (own.scrollDismissesKeyboard) scroll["dismissesKeyboard"] = true;
|
|
1373
|
+
if (own.scrollIndicators === "hidden") scroll["showsIndicators"] = false;
|
|
1032
1374
|
if (own.defaultScrollAnchor !== void 0) scroll["defaultScrollAnchor"] = unitPoint(own.defaultScrollAnchor);
|
|
1033
1375
|
const edgeEffect = edgeEffectProp(settings);
|
|
1034
1376
|
if (edgeEffect) scroll["edgeEffect"] = edgeEffect;
|
|
@@ -1036,7 +1378,7 @@ function ScrollView(props) {
|
|
|
1036
1378
|
if (position) Object.assign(scroll, position.props);
|
|
1037
1379
|
const content = marginedContent(own.children, settings.margins, axes === "horizontal" ? "horizontal" : "vertical");
|
|
1038
1380
|
const inner = providingScrollSettings(ownSettings, content);
|
|
1039
|
-
return applyModifiers(sb("scrollview", scroll,
|
|
1381
|
+
return applyModifiers(sb("scrollview", scroll, handlers, position ? position.provide(inner) : inner), modifiers);
|
|
1040
1382
|
}
|
|
1041
1383
|
function takeScrollSettings(modifiers) {
|
|
1042
1384
|
let settings = {};
|
|
@@ -1160,17 +1502,15 @@ GridItem.fixed = (value, options = {}) => GridItem({ size: { fixed: value }, spa
|
|
|
1160
1502
|
GridItem.adaptive = (options) => GridItem({ size: { adaptive: options }, spacing: options.spacing, alignment: options.alignment });
|
|
1161
1503
|
function LazyVGrid(props) {
|
|
1162
1504
|
const { own, modifiers } = splitProps(props);
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
);
|
|
1505
|
+
const grid = { columns: own.columns, spacing: own.spacing ?? null, alignment: own.alignment ?? "center" };
|
|
1506
|
+
if (own.pinnedViews && own.pinnedViews.length > 0) grid["pinned"] = own.pinnedViews;
|
|
1507
|
+
return applyModifiers(sb("lazyvgrid", grid, void 0, pinning(own.pinnedViews, own.children)), modifiers);
|
|
1167
1508
|
}
|
|
1168
1509
|
function LazyHGrid(props) {
|
|
1169
1510
|
const { own, modifiers } = splitProps(props);
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
);
|
|
1511
|
+
const grid = { rows: own.rows, spacing: own.spacing ?? null, alignment: own.alignment ?? "center" };
|
|
1512
|
+
if (own.pinnedViews && own.pinnedViews.length > 0) grid["pinned"] = own.pinnedViews;
|
|
1513
|
+
return applyModifiers(sb("lazyhgrid", grid, void 0, pinning(own.pinnedViews, own.children)), modifiers);
|
|
1174
1514
|
}
|
|
1175
1515
|
function Grid(props) {
|
|
1176
1516
|
const { own, modifiers } = splitProps(props);
|
|
@@ -1200,11 +1540,11 @@ function runStyleOf(props, inherited, nested) {
|
|
|
1200
1540
|
if (props.baselineOffset !== void 0) style.baselineOffset = props.baselineOffset;
|
|
1201
1541
|
if (props.link) style.link = props.link;
|
|
1202
1542
|
if (nested) {
|
|
1203
|
-
let
|
|
1204
|
-
if (props.bold)
|
|
1205
|
-
if (props.fontWeight)
|
|
1206
|
-
if (props.monospacedDigit)
|
|
1207
|
-
if (
|
|
1543
|
+
let runFont2 = props.font !== void 0 ? font(props.font) : void 0;
|
|
1544
|
+
if (props.bold) runFont2 = { ...runFont2 ?? {}, weight: "bold" };
|
|
1545
|
+
if (props.fontWeight) runFont2 = { ...runFont2 ?? {}, weight: props.fontWeight };
|
|
1546
|
+
if (props.monospacedDigit) runFont2 = { ...runFont2 ?? {}, monospacedDigit: true };
|
|
1547
|
+
if (runFont2) style.font = { ...style.font ?? {}, ...runFont2 };
|
|
1208
1548
|
if (props.foregroundStyle !== void 0) {
|
|
1209
1549
|
style.foreground = shapeStyle(Array.isArray(props.foregroundStyle) ? props.foregroundStyle[0] : props.foregroundStyle);
|
|
1210
1550
|
}
|
|
@@ -1260,8 +1600,8 @@ function Text(props) {
|
|
|
1260
1600
|
}
|
|
1261
1601
|
const redacted = takeModifier(modifiers, "redacted") ?? environment.redacted;
|
|
1262
1602
|
const content = segments.map((segment) => "text" in segment ? segment.text : "").join("");
|
|
1263
|
-
const
|
|
1264
|
-
if (
|
|
1603
|
+
const plain2 = segments.length <= 1 && (segments.length === 0 || "text" in segments[0] && isPlainStyle(segments[0].style));
|
|
1604
|
+
if (plain2) return applyModifiers(placeholder(sb("text", { text: content }), redacted), modifiers);
|
|
1265
1605
|
const runs = segments.map((segment) => {
|
|
1266
1606
|
const run = { ...segment.style };
|
|
1267
1607
|
if ("text" in segment) run["text"] = segment.text;
|
|
@@ -1401,7 +1741,7 @@ function NavigationLink(props) {
|
|
|
1401
1741
|
else navigation.push(own.value);
|
|
1402
1742
|
};
|
|
1403
1743
|
const style = takeModifier(modifiers, "buttonStyle") ?? environment.buttonStyle;
|
|
1404
|
-
const linkProps = style === "plain"
|
|
1744
|
+
const linkProps = style === "plain" ? { style: "plain" } : {};
|
|
1405
1745
|
return applyModifiers(sb("navlink", linkProps, { tap }, label), modifiers);
|
|
1406
1746
|
}
|
|
1407
1747
|
const SplitSelectionContext = createContext(null);
|
|
@@ -1712,6 +2052,8 @@ function toolbarPlacement(name) {
|
|
|
1712
2052
|
return "largeTitle";
|
|
1713
2053
|
case "bottomBar":
|
|
1714
2054
|
return "bottom";
|
|
2055
|
+
case "keyboard":
|
|
2056
|
+
return "keyboard";
|
|
1715
2057
|
case "secondaryAction":
|
|
1716
2058
|
return "secondary";
|
|
1717
2059
|
default:
|
|
@@ -1774,6 +2116,7 @@ const MODIFIER_KEYS = /* @__PURE__ */ new Set([
|
|
|
1774
2116
|
"buttonStyle",
|
|
1775
2117
|
"controlSize",
|
|
1776
2118
|
"pickerStyle",
|
|
2119
|
+
"toggleStyle",
|
|
1777
2120
|
"labelsHidden",
|
|
1778
2121
|
"navigationTitle",
|
|
1779
2122
|
"navigationBarTitleDisplayMode",
|
|
@@ -1787,11 +2130,14 @@ const MODIFIER_KEYS = /* @__PURE__ */ new Set([
|
|
|
1787
2130
|
"confirmationDialog",
|
|
1788
2131
|
"presentationDetents",
|
|
1789
2132
|
"onScrollVisibilityChange",
|
|
2133
|
+
"onGeometryChange",
|
|
1790
2134
|
"onTapGesture",
|
|
1791
2135
|
"dragGesture",
|
|
1792
2136
|
"magnifyGesture",
|
|
1793
2137
|
"rotateGesture",
|
|
1794
2138
|
"onLongPressGesture",
|
|
2139
|
+
"simultaneousGesture",
|
|
2140
|
+
"highPriorityGesture",
|
|
1795
2141
|
"contextMenu",
|
|
1796
2142
|
"swipeActions",
|
|
1797
2143
|
"onAppear",
|
|
@@ -1814,6 +2160,7 @@ const MODIFIER_KEYS = /* @__PURE__ */ new Set([
|
|
|
1814
2160
|
"position",
|
|
1815
2161
|
"zIndex",
|
|
1816
2162
|
"allowsHitTesting",
|
|
2163
|
+
"contentShape",
|
|
1817
2164
|
"ignoresSafeArea",
|
|
1818
2165
|
"rotation3DEffect",
|
|
1819
2166
|
"gridCellAnchor",
|
|
@@ -1848,6 +2195,7 @@ const MODIFIER_KEYS = /* @__PURE__ */ new Set([
|
|
|
1848
2195
|
"textCase",
|
|
1849
2196
|
"coordinateSpace",
|
|
1850
2197
|
"contentTransition",
|
|
2198
|
+
"scrollDisabled",
|
|
1851
2199
|
"scrollEdgeEffectStyle",
|
|
1852
2200
|
"scrollEdgeEffectHidden",
|
|
1853
2201
|
"contentMargins",
|
|
@@ -1874,6 +2222,72 @@ function takeModifier(modifiers, key) {
|
|
|
1874
2222
|
function styled(style, child) {
|
|
1875
2223
|
return sb("styled", { style }, void 0, child);
|
|
1876
2224
|
}
|
|
2225
|
+
function gesture(key, value, node, mode) {
|
|
2226
|
+
const base = { drag: false, longPress: false, minimumDistance: 10, minimumDuration: 0.5 };
|
|
2227
|
+
if (mode) base["mode"] = mode;
|
|
2228
|
+
switch (key) {
|
|
2229
|
+
case "dragGesture":
|
|
2230
|
+
return createElement(DragGestureElement, { spec: value, mode }, node);
|
|
2231
|
+
case "magnifyGesture":
|
|
2232
|
+
case "rotateGesture": {
|
|
2233
|
+
const pinch = value;
|
|
2234
|
+
if (key === "magnifyGesture") {
|
|
2235
|
+
base["magnify"] = true;
|
|
2236
|
+
base["minimumScaleDelta"] = pinch.minimumScaleDelta ?? 0.01;
|
|
2237
|
+
} else {
|
|
2238
|
+
base["rotate"] = true;
|
|
2239
|
+
base["minimumAngleDelta"] = pinch.minimumAngleDelta ?? 1;
|
|
2240
|
+
}
|
|
2241
|
+
return sb("gesture", base, {
|
|
2242
|
+
pinch: (event) => {
|
|
2243
|
+
const pinchValue = {
|
|
2244
|
+
magnification: event.scale,
|
|
2245
|
+
rotation: event.rotation,
|
|
2246
|
+
magnificationVelocity: event.scaleVelocity,
|
|
2247
|
+
rotationVelocity: event.rotationVelocity,
|
|
2248
|
+
startLocation: event.startLocation,
|
|
2249
|
+
startAnchor: event.startAnchor,
|
|
2250
|
+
location: event.location ?? event.startLocation
|
|
2251
|
+
};
|
|
2252
|
+
if (event.phase === "ended" || event.phase === "cancelled") pinch.onEnded?.(pinchValue);
|
|
2253
|
+
else pinch.onChanged?.(pinchValue);
|
|
2254
|
+
}
|
|
2255
|
+
}, node);
|
|
2256
|
+
}
|
|
2257
|
+
case "onLongPressGesture": {
|
|
2258
|
+
const spec = typeof value === "function" ? { action: value } : value;
|
|
2259
|
+
base["longPress"] = true;
|
|
2260
|
+
base["minimumDuration"] = spec.minimumDuration ?? 0.5;
|
|
2261
|
+
return sb("gesture", base, { longpress: () => spec.action() }, node);
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
function DragGestureElement({ spec, mode, children }) {
|
|
2266
|
+
const spaces = useCoordinateSpaces();
|
|
2267
|
+
const space = spec.coordinateSpace ?? "local";
|
|
2268
|
+
const named = typeof space === "object" ? spaces[space.named] : null;
|
|
2269
|
+
const props = { drag: true, longPress: false, minimumDistance: spec.minimumDistance ?? 10, minimumDuration: 0.5 };
|
|
2270
|
+
if (space !== "local") props["coordinateSpace"] = "global";
|
|
2271
|
+
if (mode) props["mode"] = mode;
|
|
2272
|
+
const inSpace = (point) => named ? { x: point.x - named.x, y: point.y - named.y } : point;
|
|
2273
|
+
return sb(
|
|
2274
|
+
"gesture",
|
|
2275
|
+
props,
|
|
2276
|
+
{
|
|
2277
|
+
drag: (event) => {
|
|
2278
|
+
const dragValue = {
|
|
2279
|
+
translation: event.translation,
|
|
2280
|
+
location: inSpace(event.location),
|
|
2281
|
+
startLocation: inSpace(event.startLocation),
|
|
2282
|
+
velocity: event.velocity
|
|
2283
|
+
};
|
|
2284
|
+
if (event.phase === "ended" || event.phase === "cancelled") spec.onEnded?.(dragValue);
|
|
2285
|
+
else spec.onChanged?.(dragValue);
|
|
2286
|
+
}
|
|
2287
|
+
},
|
|
2288
|
+
children
|
|
2289
|
+
);
|
|
2290
|
+
}
|
|
1877
2291
|
function decorated(role, alignment, content, decoration) {
|
|
1878
2292
|
return sb("decorated", { role, alignment }, void 0, [
|
|
1879
2293
|
createElement(Fragment, { key: "content" }, content),
|
|
@@ -1956,6 +2370,7 @@ const STYLED_KEYS = /* @__PURE__ */ new Set([
|
|
|
1956
2370
|
"position",
|
|
1957
2371
|
"zIndex",
|
|
1958
2372
|
"allowsHitTesting",
|
|
2373
|
+
"contentShape",
|
|
1959
2374
|
"ignoresSafeArea",
|
|
1960
2375
|
"rotation3DEffect",
|
|
1961
2376
|
"gridCellAnchor",
|
|
@@ -1964,12 +2379,14 @@ const STYLED_KEYS = /* @__PURE__ */ new Set([
|
|
|
1964
2379
|
"pointerStyle",
|
|
1965
2380
|
"matchedGeometryEffect",
|
|
1966
2381
|
"containerRelativeFrame",
|
|
1967
|
-
"contentTransition"
|
|
2382
|
+
"contentTransition",
|
|
2383
|
+
"scrollDisabled"
|
|
1968
2384
|
]);
|
|
1969
2385
|
const ENVIRONMENT_KEYS = /* @__PURE__ */ new Set([
|
|
1970
2386
|
"buttonStyle",
|
|
1971
2387
|
"controlSize",
|
|
1972
2388
|
"pickerStyle",
|
|
2389
|
+
"toggleStyle",
|
|
1973
2390
|
"labelsHidden",
|
|
1974
2391
|
"refreshable",
|
|
1975
2392
|
"submitLabel",
|
|
@@ -2095,13 +2512,15 @@ function apply(key, value, node, sides) {
|
|
|
2095
2512
|
case "gridCellColumns":
|
|
2096
2513
|
return styled({ gridCellColumns: value }, node);
|
|
2097
2514
|
case "buttonStyle":
|
|
2098
|
-
return withEnvironment({ buttonStyle: value
|
|
2515
|
+
return withEnvironment({ buttonStyle: value }, node);
|
|
2099
2516
|
case "controlSize":
|
|
2100
2517
|
return withEnvironment({ controlSize: value }, node);
|
|
2101
2518
|
case "symbolRenderingMode":
|
|
2102
2519
|
return withEnvironment({ symbolRenderingMode: value }, node);
|
|
2103
2520
|
case "pickerStyle":
|
|
2104
2521
|
return withEnvironment({ pickerStyle: value }, node);
|
|
2522
|
+
case "toggleStyle":
|
|
2523
|
+
return withEnvironment({ toggleStyle: value }, node);
|
|
2105
2524
|
case "labelsHidden":
|
|
2106
2525
|
return withEnvironment({ labelsHidden: Boolean(value) }, node);
|
|
2107
2526
|
case "navigationTitle":
|
|
@@ -2141,62 +2560,31 @@ function apply(key, value, node, sides) {
|
|
|
2141
2560
|
const spec = typeof value === "function" ? { action: value } : value;
|
|
2142
2561
|
return sb("styled", { style: { scrollVisibility: { threshold: spec.threshold ?? 0.5 } } }, { scrollvisibility: (event) => spec.action(event.visible) }, node);
|
|
2143
2562
|
}
|
|
2144
|
-
case "onTapGesture":
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
const
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
{
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
const dragValue = {
|
|
2154
|
-
translation: event.translation,
|
|
2155
|
-
location: event.location,
|
|
2156
|
-
startLocation: event.startLocation,
|
|
2157
|
-
velocity: event.velocity
|
|
2158
|
-
};
|
|
2159
|
-
if (event.phase === "ended" || event.phase === "cancelled") drag.onEnded?.(dragValue);
|
|
2160
|
-
else drag.onChanged?.(dragValue);
|
|
2161
|
-
}
|
|
2162
|
-
},
|
|
2163
|
-
node
|
|
2164
|
-
);
|
|
2563
|
+
case "onTapGesture": {
|
|
2564
|
+
const spec = typeof value === "function" ? { action: value } : value;
|
|
2565
|
+
const count = Math.floor(spec.count ?? 1);
|
|
2566
|
+
const props = count > 1 ? { count } : {};
|
|
2567
|
+
if ("perform" in spec) {
|
|
2568
|
+
const { perform } = spec;
|
|
2569
|
+
return sb("tapgesture", { ...props, location: true }, { tap: (event) => perform({ x: event.x ?? 0, y: event.y ?? 0 }) }, node);
|
|
2570
|
+
}
|
|
2571
|
+
return sb("tapgesture", props, { tap: () => spec.action() }, node);
|
|
2165
2572
|
}
|
|
2573
|
+
case "onGeometryChange":
|
|
2574
|
+
return createElement(GeometryChangeView, { handler: value }, node);
|
|
2575
|
+
case "dragGesture":
|
|
2166
2576
|
case "magnifyGesture":
|
|
2167
|
-
case "rotateGesture":
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2577
|
+
case "rotateGesture":
|
|
2578
|
+
case "onLongPressGesture":
|
|
2579
|
+
return gesture(key, value, node);
|
|
2580
|
+
case "simultaneousGesture":
|
|
2581
|
+
case "highPriorityGesture": {
|
|
2582
|
+
const mode = key === "simultaneousGesture" ? "simultaneous" : "highPriority";
|
|
2583
|
+
let result = node;
|
|
2584
|
+
for (const [gestureKey, gestureValue] of Object.entries(value)) {
|
|
2585
|
+
if (gestureValue !== void 0) result = gesture(gestureKey, gestureValue, result, mode);
|
|
2176
2586
|
}
|
|
2177
|
-
return
|
|
2178
|
-
pinch: (event) => {
|
|
2179
|
-
const pinchValue = {
|
|
2180
|
-
magnification: event.scale,
|
|
2181
|
-
rotation: event.rotation,
|
|
2182
|
-
magnificationVelocity: event.scaleVelocity,
|
|
2183
|
-
rotationVelocity: event.rotationVelocity,
|
|
2184
|
-
startLocation: event.startLocation,
|
|
2185
|
-
startAnchor: event.startAnchor
|
|
2186
|
-
};
|
|
2187
|
-
if (event.phase === "ended" || event.phase === "cancelled") pinch.onEnded?.(pinchValue);
|
|
2188
|
-
else pinch.onChanged?.(pinchValue);
|
|
2189
|
-
}
|
|
2190
|
-
}, node);
|
|
2191
|
-
}
|
|
2192
|
-
case "onLongPressGesture": {
|
|
2193
|
-
const spec = typeof value === "function" ? { action: value } : value;
|
|
2194
|
-
return sb(
|
|
2195
|
-
"gesture",
|
|
2196
|
-
{ drag: false, longPress: true, minimumDistance: 10, minimumDuration: spec.minimumDuration ?? 0.5 },
|
|
2197
|
-
{ longpress: () => spec.action() },
|
|
2198
|
-
node
|
|
2199
|
-
);
|
|
2587
|
+
return result;
|
|
2200
2588
|
}
|
|
2201
2589
|
case "contextMenu":
|
|
2202
2590
|
return sb("contextmenu", {}, void 0, [createElement(Fragment, { key: "content" }, node), createElement(Fragment, { key: "items" }, value)]);
|
|
@@ -2265,6 +2653,8 @@ function apply(key, value, node, sides) {
|
|
|
2265
2653
|
return styled({ zIndex: value }, node);
|
|
2266
2654
|
case "allowsHitTesting":
|
|
2267
2655
|
return value === false ? styled({ hitTesting: false }, node) : node;
|
|
2656
|
+
case "contentShape":
|
|
2657
|
+
return value === "rectangle" ? styled({ contentShape: "rectangle" }, node) : node;
|
|
2268
2658
|
case "ignoresSafeArea":
|
|
2269
2659
|
return styled(
|
|
2270
2660
|
value === true ? { ignoresSafeArea: "all", ignoresKeyboard: true } : { ignoresSafeArea: value },
|
|
@@ -2387,6 +2777,8 @@ function apply(key, value, node, sides) {
|
|
|
2387
2777
|
const spec = typeof value === "function" ? { transition: value } : value;
|
|
2388
2778
|
return createElement(ScrollTransitionView, { spec }, node);
|
|
2389
2779
|
}
|
|
2780
|
+
case "scrollDisabled":
|
|
2781
|
+
return styled({ scrollDisabled: value === true }, node);
|
|
2390
2782
|
case "contentTransition": {
|
|
2391
2783
|
if (value === "identity") return node;
|
|
2392
2784
|
if (typeof value === "object") {
|
|
@@ -2477,6 +2869,43 @@ function GeometryReader(props) {
|
|
|
2477
2869
|
modifiers
|
|
2478
2870
|
);
|
|
2479
2871
|
}
|
|
2872
|
+
function GeometryChangeView({ handler, children }) {
|
|
2873
|
+
const [reported, setReported] = useState(NO_GEOMETRY);
|
|
2874
|
+
const [needsFrame, setNeedsFrame] = useState(false);
|
|
2875
|
+
const last = useRef(null);
|
|
2876
|
+
const spaces = useCoordinateSpaces();
|
|
2877
|
+
let readFrame = false;
|
|
2878
|
+
const proxy = geometryProxy(reported, spaces, () => {
|
|
2879
|
+
readFrame = true;
|
|
2880
|
+
});
|
|
2881
|
+
const heard = reported !== NO_GEOMETRY;
|
|
2882
|
+
const value = heard && typeof handler !== "function" ? handler.of(proxy) : void 0;
|
|
2883
|
+
useEffect(() => {
|
|
2884
|
+
if (readFrame && !needsFrame) setNeedsFrame(true);
|
|
2885
|
+
});
|
|
2886
|
+
useEffect(() => {
|
|
2887
|
+
if (!heard) return;
|
|
2888
|
+
if (typeof handler === "function") {
|
|
2889
|
+
handler(proxy);
|
|
2890
|
+
return;
|
|
2891
|
+
}
|
|
2892
|
+
const previous = last.current;
|
|
2893
|
+
if (previous && JSON.stringify(previous.value) === JSON.stringify(value)) return;
|
|
2894
|
+
last.current = { value };
|
|
2895
|
+
handler.action(value, previous ? previous.value : value);
|
|
2896
|
+
});
|
|
2897
|
+
return sb(
|
|
2898
|
+
"zstack",
|
|
2899
|
+
{ alignment: "center", reportsSize: true, ...needsFrame ? { reportsFrame: true } : {} },
|
|
2900
|
+
{
|
|
2901
|
+
geometry: (event) => {
|
|
2902
|
+
const next = nextGeometry(event, reported);
|
|
2903
|
+
if (JSON.stringify(next) !== JSON.stringify(reported)) setReported(next);
|
|
2904
|
+
}
|
|
2905
|
+
},
|
|
2906
|
+
children
|
|
2907
|
+
);
|
|
2908
|
+
}
|
|
2480
2909
|
const DEFAULT_VIEW_SPACING = { distance: () => 8 };
|
|
2481
2910
|
function subviewTraits(children) {
|
|
2482
2911
|
return flattenChildren(children).map((element) => {
|
|
@@ -2675,38 +3104,9 @@ function effectStyle(values) {
|
|
|
2675
3104
|
if (values.hueRotation !== 0) style.hueRotation = round(values.hueRotation);
|
|
2676
3105
|
return style;
|
|
2677
3106
|
}
|
|
2678
|
-
function
|
|
2679
|
-
const mix = (a, b) => a + (b - a) * t;
|
|
2680
|
-
const result = {
|
|
2681
|
-
offsetX: mix(from.offsetX, to.offsetX),
|
|
2682
|
-
offsetY: mix(from.offsetY, to.offsetY),
|
|
2683
|
-
scaleX: mix(from.scaleX, to.scaleX),
|
|
2684
|
-
scaleY: mix(from.scaleY, to.scaleY),
|
|
2685
|
-
rotation: mix(from.rotation, to.rotation),
|
|
2686
|
-
opacity: mix(from.opacity, to.opacity),
|
|
2687
|
-
blur: mix(from.blur, to.blur),
|
|
2688
|
-
brightness: mix(from.brightness, to.brightness),
|
|
2689
|
-
contrast: mix(from.contrast, to.contrast),
|
|
2690
|
-
saturation: mix(from.saturation, to.saturation),
|
|
2691
|
-
grayscale: mix(from.grayscale, to.grayscale),
|
|
2692
|
-
hueRotation: mix(from.hueRotation, to.hueRotation)
|
|
2693
|
-
};
|
|
2694
|
-
const scaleAnchor = to.scaleAnchor ?? from.scaleAnchor;
|
|
2695
|
-
if (scaleAnchor) result.scaleAnchor = scaleAnchor;
|
|
2696
|
-
const rotationAnchor = to.rotationAnchor ?? from.rotationAnchor;
|
|
2697
|
-
if (rotationAnchor) result.rotationAnchor = rotationAnchor;
|
|
2698
|
-
const rotation3D = to.rotation3D ?? from.rotation3D;
|
|
2699
|
-
if (rotation3D) result.rotation3D = { ...rotation3D, angle: mix(from.rotation3D?.angle ?? 0, to.rotation3D?.angle ?? 0) };
|
|
2700
|
-
return result;
|
|
2701
|
-
}
|
|
2702
|
-
function EffectBox({
|
|
2703
|
-
render,
|
|
2704
|
-
alwaysFrame,
|
|
2705
|
-
animationToken,
|
|
2706
|
-
children
|
|
2707
|
-
}) {
|
|
3107
|
+
function EffectBox({ render, children }) {
|
|
2708
3108
|
const [reported, setReported] = useState(NO_GEOMETRY);
|
|
2709
|
-
const [needsFrame, setNeedsFrame] = useState(
|
|
3109
|
+
const [needsFrame, setNeedsFrame] = useState(false);
|
|
2710
3110
|
let readFrame = false;
|
|
2711
3111
|
const style = render(reported, () => {
|
|
2712
3112
|
readFrame = true;
|
|
@@ -2714,8 +3114,7 @@ function EffectBox({
|
|
|
2714
3114
|
useEffect(() => {
|
|
2715
3115
|
if (readFrame && !needsFrame) setNeedsFrame(true);
|
|
2716
3116
|
});
|
|
2717
|
-
|
|
2718
|
-
if (animationToken) content = sb("styled", { style: { animation: { type: "default" }, animationToken: animationToken(reported) } }, void 0, content);
|
|
3117
|
+
const content = Object.keys(style).length > 0 ? sb("styled", { style }, void 0, children) : children;
|
|
2719
3118
|
return sb(
|
|
2720
3119
|
"zstack",
|
|
2721
3120
|
{ alignment: "center", reportsSize: true, ...needsFrame ? { reportsFrame: true } : {} },
|
|
@@ -2730,45 +3129,23 @@ function EffectBox({
|
|
|
2730
3129
|
}
|
|
2731
3130
|
function VisualEffectView({ effect, children }) {
|
|
2732
3131
|
const spaces = useCoordinateSpaces();
|
|
2733
|
-
return /* @__PURE__ */ jsx(EffectBox, {
|
|
3132
|
+
return /* @__PURE__ */ jsx(EffectBox, { render: (reported, markRead) => effectStyle(effect(EMPTY_EFFECT, geometryProxy(reported, spaces, markRead)).values), children });
|
|
2734
3133
|
}
|
|
2735
3134
|
const PHASES = {
|
|
2736
3135
|
topLeading: { phase: "topLeading", value: -1, isIdentity: false },
|
|
2737
3136
|
identity: { phase: "identity", value: 0, isIdentity: true },
|
|
2738
3137
|
bottomTrailing: { phase: "bottomTrailing", value: 1, isIdentity: false }
|
|
2739
3138
|
};
|
|
2740
|
-
function scrollProgress(reported, axis) {
|
|
2741
|
-
const view = reported.scrollView;
|
|
2742
|
-
const origin = reported.origin;
|
|
2743
|
-
if (!view || !origin) return 0;
|
|
2744
|
-
const horizontal = (axis ?? view.axis) === "horizontal";
|
|
2745
|
-
const start = horizontal ? origin.x : origin.y;
|
|
2746
|
-
const length = horizontal ? reported.size.width : reported.size.height;
|
|
2747
|
-
const viewStart = horizontal ? view.x : view.y;
|
|
2748
|
-
const viewEnd = viewStart + (horizontal ? view.width : view.height);
|
|
2749
|
-
if (length <= 0) return 0;
|
|
2750
|
-
if (start < viewStart) return -Math.min(1, (viewStart - start) / length);
|
|
2751
|
-
if (start + length > viewEnd) return Math.min(1, (start + length - viewEnd) / length);
|
|
2752
|
-
return 0;
|
|
2753
|
-
}
|
|
2754
|
-
function scrollTransitionValues(spec, progress) {
|
|
2755
|
-
const identity = spec.transition(EMPTY_EFFECT, PHASES.identity).values;
|
|
2756
|
-
const configuration = spec.configuration ?? "interactive";
|
|
2757
|
-
if (configuration === "identity" || progress === 0) return identity;
|
|
2758
|
-
const edge = spec.transition(EMPTY_EFFECT, progress < 0 ? PHASES.topLeading : PHASES.bottomTrailing).values;
|
|
2759
|
-
if (configuration === "animated") return edge;
|
|
2760
|
-
return interpolateEffects(identity, edge, Math.abs(progress));
|
|
2761
|
-
}
|
|
2762
3139
|
function ScrollTransitionView({ spec, children }) {
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
);
|
|
3140
|
+
const phase = (which) => effectStyle(spec.transition(EMPTY_EFFECT, PHASES[which]).values);
|
|
3141
|
+
const transition = {
|
|
3142
|
+
configuration: spec.configuration ?? "interactive",
|
|
3143
|
+
identity: phase("identity"),
|
|
3144
|
+
topLeading: phase("topLeading"),
|
|
3145
|
+
bottomTrailing: phase("bottomTrailing")
|
|
3146
|
+
};
|
|
3147
|
+
if (spec.axis) transition.axis = spec.axis;
|
|
3148
|
+
return sb("styled", { style: { scrollTransition: transition } }, void 0, children);
|
|
2772
3149
|
}
|
|
2773
3150
|
const r = (n) => {
|
|
2774
3151
|
const v = Math.round(n * 100) / 100;
|
|
@@ -2907,12 +3284,398 @@ class Path {
|
|
|
2907
3284
|
return this.svg();
|
|
2908
3285
|
}
|
|
2909
3286
|
}
|
|
3287
|
+
const plain = (text) => text.map((run) => run.text).join("");
|
|
3288
|
+
function attributesOf(run) {
|
|
3289
|
+
if (!run) return {};
|
|
3290
|
+
const { text: _text, ...attributes } = run;
|
|
3291
|
+
return attributes;
|
|
3292
|
+
}
|
|
3293
|
+
function alike(a, b) {
|
|
3294
|
+
return !a.bold === !b.bold && !a.italic === !b.italic && !a.underline === !b.underline && !a.strikethrough === !b.strikethrough && JSON.stringify(a.foregroundColor ?? null) === JSON.stringify(b.foregroundColor ?? null) && JSON.stringify(a.backgroundColor ?? null) === JSON.stringify(b.backgroundColor ?? null) && JSON.stringify(runFont(a.font) ?? null) === JSON.stringify(runFont(b.font) ?? null);
|
|
3295
|
+
}
|
|
3296
|
+
function runFont(font2) {
|
|
3297
|
+
if (!font2) return void 0;
|
|
3298
|
+
const out = {};
|
|
3299
|
+
if (typeof font2.size === "number" && Number.isFinite(font2.size) && font2.size > 0) out.size = font2.size;
|
|
3300
|
+
if (font2.weight) out.weight = font2.weight;
|
|
3301
|
+
if (font2.design) out.design = font2.design;
|
|
3302
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
3303
|
+
}
|
|
3304
|
+
function normalized(text) {
|
|
3305
|
+
const runs = [];
|
|
3306
|
+
for (const run of text) {
|
|
3307
|
+
if (!run.text) continue;
|
|
3308
|
+
const last = runs[runs.length - 1];
|
|
3309
|
+
if (last && alike(last, run)) runs[runs.length - 1] = { ...last, text: last.text + run.text };
|
|
3310
|
+
else runs.push(run);
|
|
3311
|
+
}
|
|
3312
|
+
return runs;
|
|
3313
|
+
}
|
|
3314
|
+
function slice(text, start, end = Infinity) {
|
|
3315
|
+
const runs = [];
|
|
3316
|
+
let at = 0;
|
|
3317
|
+
for (const run of text) {
|
|
3318
|
+
const from = Math.max(start, at);
|
|
3319
|
+
const to = Math.min(end, at + run.text.length);
|
|
3320
|
+
if (from < to) runs.push({ ...run, text: run.text.slice(from - at, to - at) });
|
|
3321
|
+
at += run.text.length;
|
|
3322
|
+
}
|
|
3323
|
+
return runs;
|
|
3324
|
+
}
|
|
3325
|
+
function attributesAt(text, offset) {
|
|
3326
|
+
let at = 0;
|
|
3327
|
+
for (const run of text) {
|
|
3328
|
+
if (offset < at + run.text.length) return attributesOf(run);
|
|
3329
|
+
at += run.text.length;
|
|
3330
|
+
}
|
|
3331
|
+
return attributesOf([...text].reverse().find((run) => run.text.length > 0));
|
|
3332
|
+
}
|
|
3333
|
+
function replacingCharacters(text, next) {
|
|
3334
|
+
const old = plain(text);
|
|
3335
|
+
if (old === next) return text;
|
|
3336
|
+
let head = 0;
|
|
3337
|
+
const shorter = Math.min(old.length, next.length);
|
|
3338
|
+
while (head < shorter && old[head] === next[head]) head++;
|
|
3339
|
+
let tail = 0;
|
|
3340
|
+
while (tail < shorter - head && old[old.length - 1 - tail] === next[next.length - 1 - tail]) tail++;
|
|
3341
|
+
const removedEnd = old.length - tail;
|
|
3342
|
+
const typed = next.slice(head, next.length - tail);
|
|
3343
|
+
const attributes = attributesAt(text, removedEnd > head || head === 0 ? head : head - 1);
|
|
3344
|
+
return normalized([...slice(text, 0, head), { ...attributes, text: typed }, ...slice(text, removedEnd)]);
|
|
3345
|
+
}
|
|
3346
|
+
function settingAttributes(text, start, end, change) {
|
|
3347
|
+
const inside = slice(text, start, end).map((run) => ({ ...run, ...change }));
|
|
3348
|
+
return normalized([...slice(text, 0, start), ...inside, ...slice(text, end)]);
|
|
3349
|
+
}
|
|
3350
|
+
function every(text, start, end, key) {
|
|
3351
|
+
const inside = slice(text, start, end);
|
|
3352
|
+
return inside.length > 0 && inside.every((run) => run[key] === true);
|
|
3353
|
+
}
|
|
3354
|
+
const AttributedText = {
|
|
3355
|
+
plain,
|
|
3356
|
+
normalized,
|
|
3357
|
+
slice,
|
|
3358
|
+
attributesAt,
|
|
3359
|
+
replacingCharacters,
|
|
3360
|
+
settingAttributes,
|
|
3361
|
+
every,
|
|
3362
|
+
/** A text of one run. */
|
|
3363
|
+
of: (text, attributes = {}) => normalized([{ ...attributes, text }])
|
|
3364
|
+
};
|
|
3365
|
+
const BOLD_WEIGHTS = /* @__PURE__ */ new Set(["semibold", "bold", "heavy", "black"]);
|
|
3366
|
+
function fromProtocolRuns(runs) {
|
|
3367
|
+
return normalized(
|
|
3368
|
+
runs.map((run) => {
|
|
3369
|
+
const out = { text: run.text ?? "" };
|
|
3370
|
+
const font2 = run.font ?? {};
|
|
3371
|
+
const bold = typeof font2.weight === "string" && BOLD_WEIGHTS.has(font2.weight);
|
|
3372
|
+
if (bold) out.bold = true;
|
|
3373
|
+
const own = runFont({
|
|
3374
|
+
...typeof font2.size === "number" ? { size: font2.size } : {},
|
|
3375
|
+
...typeof font2.weight === "string" && !bold ? { weight: font2.weight } : {},
|
|
3376
|
+
...typeof font2.design === "string" ? { design: font2.design } : {}
|
|
3377
|
+
});
|
|
3378
|
+
if (own) out.font = own;
|
|
3379
|
+
if (run.italic) out.italic = true;
|
|
3380
|
+
if (run.underline) out.underline = true;
|
|
3381
|
+
if (run.strikethrough) out.strikethrough = true;
|
|
3382
|
+
if (run.foreground !== void 0) out.foregroundColor = run.foreground;
|
|
3383
|
+
if (run.background !== void 0) out.backgroundColor = run.background;
|
|
3384
|
+
return out;
|
|
3385
|
+
})
|
|
3386
|
+
);
|
|
3387
|
+
}
|
|
3388
|
+
function protocolRuns(text) {
|
|
3389
|
+
return text.map((run) => {
|
|
3390
|
+
const out = { text: run.text };
|
|
3391
|
+
const font2 = { ...runFont(run.font) ?? {} };
|
|
3392
|
+
if (run.bold && !font2.weight) font2.weight = "bold";
|
|
3393
|
+
if (Object.keys(font2).length > 0) out.font = font2;
|
|
3394
|
+
if (run.italic) out.italic = true;
|
|
3395
|
+
if (run.underline) out.underline = {};
|
|
3396
|
+
if (run.strikethrough) out.strikethrough = {};
|
|
3397
|
+
if (run.foregroundColor !== void 0) out.foreground = color(run.foregroundColor);
|
|
3398
|
+
if (run.backgroundColor !== void 0) out.background = color(run.backgroundColor);
|
|
3399
|
+
return out;
|
|
3400
|
+
});
|
|
3401
|
+
}
|
|
3402
|
+
function useAudioRecorder(options = {}) {
|
|
3403
|
+
const interval = options.meterInterval ?? 50;
|
|
3404
|
+
const host = browserEnvironment.audioRecorder;
|
|
3405
|
+
const [status, setStatus] = useState("idle");
|
|
3406
|
+
const [time, setTime] = useState(0);
|
|
3407
|
+
const [levels, setLevels] = useState([]);
|
|
3408
|
+
const [error, setError] = useState(null);
|
|
3409
|
+
const statusRef = useRef(status);
|
|
3410
|
+
statusRef.current = status;
|
|
3411
|
+
const fail = (reason) => setError(reason instanceof Error ? reason.message : String(reason));
|
|
3412
|
+
useEffect(() => {
|
|
3413
|
+
if (status !== "recording" || !host) return;
|
|
3414
|
+
let reading = false;
|
|
3415
|
+
let live = true;
|
|
3416
|
+
const timer = setInterval(() => {
|
|
3417
|
+
if (reading) return;
|
|
3418
|
+
reading = true;
|
|
3419
|
+
host.meter().then((meter) => {
|
|
3420
|
+
if (!live) return;
|
|
3421
|
+
setTime(meter.time);
|
|
3422
|
+
setLevels((previous) => [...previous, meter.level]);
|
|
3423
|
+
}).catch(() => void 0).finally(() => {
|
|
3424
|
+
reading = false;
|
|
3425
|
+
});
|
|
3426
|
+
}, interval);
|
|
3427
|
+
return () => {
|
|
3428
|
+
live = false;
|
|
3429
|
+
clearInterval(timer);
|
|
3430
|
+
};
|
|
3431
|
+
}, [status, host, interval]);
|
|
3432
|
+
useEffect(
|
|
3433
|
+
() => () => {
|
|
3434
|
+
if (statusRef.current !== "idle") void host?.cancel().catch(() => void 0);
|
|
3435
|
+
},
|
|
3436
|
+
[host]
|
|
3437
|
+
);
|
|
3438
|
+
const record = useCallback(async () => {
|
|
3439
|
+
if (!host) {
|
|
3440
|
+
setError("unsupported: this page cannot record");
|
|
3441
|
+
return;
|
|
3442
|
+
}
|
|
3443
|
+
if (statusRef.current !== "idle") return;
|
|
3444
|
+
setError(null);
|
|
3445
|
+
setTime(0);
|
|
3446
|
+
setLevels([]);
|
|
3447
|
+
setStatus("starting");
|
|
3448
|
+
try {
|
|
3449
|
+
await host.start();
|
|
3450
|
+
setStatus("recording");
|
|
3451
|
+
} catch (reason) {
|
|
3452
|
+
fail(reason);
|
|
3453
|
+
setStatus("idle");
|
|
3454
|
+
}
|
|
3455
|
+
}, [host]);
|
|
3456
|
+
const pause = useCallback(async () => {
|
|
3457
|
+
if (!host || statusRef.current !== "recording") return;
|
|
3458
|
+
await host.pause().catch(fail);
|
|
3459
|
+
setStatus("paused");
|
|
3460
|
+
}, [host]);
|
|
3461
|
+
const resume = useCallback(async () => {
|
|
3462
|
+
if (!host || statusRef.current !== "paused") return;
|
|
3463
|
+
await host.resume().catch(fail);
|
|
3464
|
+
setStatus("recording");
|
|
3465
|
+
}, [host]);
|
|
3466
|
+
const stop = useCallback(async () => {
|
|
3467
|
+
if (!host || statusRef.current !== "recording" && statusRef.current !== "paused") return null;
|
|
3468
|
+
setStatus("stopping");
|
|
3469
|
+
try {
|
|
3470
|
+
const recording = await host.stop();
|
|
3471
|
+
setTime(recording.duration);
|
|
3472
|
+
return recording;
|
|
3473
|
+
} catch (reason) {
|
|
3474
|
+
fail(reason);
|
|
3475
|
+
return null;
|
|
3476
|
+
} finally {
|
|
3477
|
+
setStatus("idle");
|
|
3478
|
+
}
|
|
3479
|
+
}, [host]);
|
|
3480
|
+
const cancel = useCallback(async () => {
|
|
3481
|
+
if (!host || statusRef.current === "idle") return;
|
|
3482
|
+
await host.cancel().catch(fail);
|
|
3483
|
+
setStatus("idle");
|
|
3484
|
+
setTime(0);
|
|
3485
|
+
setLevels([]);
|
|
3486
|
+
}, [host]);
|
|
3487
|
+
const discard = useCallback(async (url) => {
|
|
3488
|
+
await host?.discard(url).catch(fail);
|
|
3489
|
+
}, [host]);
|
|
3490
|
+
const trim = useCallback(
|
|
3491
|
+
async (url, keep) => {
|
|
3492
|
+
if (!host) throw new Error("unsupported: this page cannot cut audio");
|
|
3493
|
+
try {
|
|
3494
|
+
return await host.trim(url, keep);
|
|
3495
|
+
} catch (reason) {
|
|
3496
|
+
fail(reason);
|
|
3497
|
+
throw reason;
|
|
3498
|
+
}
|
|
3499
|
+
},
|
|
3500
|
+
[host]
|
|
3501
|
+
);
|
|
3502
|
+
return { available: host !== void 0, status, time, levels, error, record, pause, resume, stop, cancel, discard, trim };
|
|
3503
|
+
}
|
|
3504
|
+
const stores = /* @__PURE__ */ new WeakMap();
|
|
3505
|
+
function entryFor(store, key) {
|
|
3506
|
+
let entries = stores.get(store);
|
|
3507
|
+
if (!entries) {
|
|
3508
|
+
entries = /* @__PURE__ */ new Map();
|
|
3509
|
+
stores.set(store, entries);
|
|
3510
|
+
if (store === pageStorage) watchOtherTabs();
|
|
3511
|
+
}
|
|
3512
|
+
let entry = entries.get(key);
|
|
3513
|
+
if (!entry) {
|
|
3514
|
+
let raw = null;
|
|
3515
|
+
try {
|
|
3516
|
+
raw = store.getItem(key);
|
|
3517
|
+
} catch {
|
|
3518
|
+
}
|
|
3519
|
+
entry = { raw, value: void 0, decoded: false, decode: void 0, listeners: /* @__PURE__ */ new Set() };
|
|
3520
|
+
entries.set(key, entry);
|
|
3521
|
+
}
|
|
3522
|
+
return entry;
|
|
3523
|
+
}
|
|
3524
|
+
function valueOf(entry, absent) {
|
|
3525
|
+
if (entry.raw === null) return absent;
|
|
3526
|
+
if (!entry.decoded) {
|
|
3527
|
+
try {
|
|
3528
|
+
const parsed = JSON.parse(entry.raw);
|
|
3529
|
+
entry.value = entry.decode ? entry.decode(parsed) : parsed;
|
|
3530
|
+
} catch {
|
|
3531
|
+
entry.raw = null;
|
|
3532
|
+
return absent;
|
|
3533
|
+
}
|
|
3534
|
+
entry.decoded = true;
|
|
3535
|
+
}
|
|
3536
|
+
return entry.value;
|
|
3537
|
+
}
|
|
3538
|
+
function notify(entry) {
|
|
3539
|
+
for (const listener of [...entry.listeners]) listener();
|
|
3540
|
+
}
|
|
3541
|
+
function invalidate(entry, raw) {
|
|
3542
|
+
if (entry.raw === raw && entry.decoded) return;
|
|
3543
|
+
entry.raw = raw;
|
|
3544
|
+
entry.decoded = false;
|
|
3545
|
+
notify(entry);
|
|
3546
|
+
}
|
|
3547
|
+
function readPageStorage() {
|
|
3548
|
+
try {
|
|
3549
|
+
const storage = globalThis.localStorage;
|
|
3550
|
+
return storage && typeof storage.getItem === "function" ? storage : null;
|
|
3551
|
+
} catch {
|
|
3552
|
+
return null;
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
const memory = /* @__PURE__ */ new Map();
|
|
3556
|
+
const memoryStorage = {
|
|
3557
|
+
getItem: (key) => memory.get(key) ?? null,
|
|
3558
|
+
setItem: (key, value) => void memory.set(key, value),
|
|
3559
|
+
removeItem: (key) => void memory.delete(key)
|
|
3560
|
+
};
|
|
3561
|
+
const pageStorage = readPageStorage();
|
|
3562
|
+
const appStorage = pageStorage ?? memoryStorage;
|
|
3563
|
+
let watching = false;
|
|
3564
|
+
function watchOtherTabs() {
|
|
3565
|
+
if (watching || !pageStorage) return;
|
|
3566
|
+
watching = true;
|
|
3567
|
+
const target = globalThis;
|
|
3568
|
+
if (typeof target.addEventListener !== "function") return;
|
|
3569
|
+
target.addEventListener("storage", (event) => {
|
|
3570
|
+
if (event.storageArea !== pageStorage) return;
|
|
3571
|
+
const entries = stores.get(pageStorage);
|
|
3572
|
+
if (!entries) return;
|
|
3573
|
+
if (event.key === null) {
|
|
3574
|
+
for (const entry of entries.values()) invalidate(entry, null);
|
|
3575
|
+
} else {
|
|
3576
|
+
const entry = entries.get(event.key);
|
|
3577
|
+
if (entry) invalidate(entry, event.newValue);
|
|
3578
|
+
}
|
|
3579
|
+
});
|
|
3580
|
+
}
|
|
3581
|
+
function useAppStorage(key, defaultValue, options = {}) {
|
|
3582
|
+
const { store = appStorage, decode, onError } = options;
|
|
3583
|
+
const [fallback] = useState(defaultValue);
|
|
3584
|
+
const entry = entryFor(store, key);
|
|
3585
|
+
entry.decode = decode;
|
|
3586
|
+
const subscribe = useCallback(
|
|
3587
|
+
(listener) => {
|
|
3588
|
+
const subscribed = entryFor(store, key);
|
|
3589
|
+
subscribed.listeners.add(listener);
|
|
3590
|
+
return () => void subscribed.listeners.delete(listener);
|
|
3591
|
+
},
|
|
3592
|
+
[store, key]
|
|
3593
|
+
);
|
|
3594
|
+
const read = useCallback(() => valueOf(entryFor(store, key), fallback), [store, key, fallback]);
|
|
3595
|
+
const value = useSyncExternalStore(subscribe, read, read);
|
|
3596
|
+
const set = useCallback(
|
|
3597
|
+
(next) => {
|
|
3598
|
+
const target = entryFor(store, key);
|
|
3599
|
+
const current = valueOf(target, fallback);
|
|
3600
|
+
const value2 = typeof next === "function" ? next(current) : next;
|
|
3601
|
+
const raw = value2 === void 0 ? null : JSON.stringify(value2);
|
|
3602
|
+
try {
|
|
3603
|
+
if (raw === null) store.removeItem(key);
|
|
3604
|
+
else store.setItem(key, raw);
|
|
3605
|
+
} catch (error) {
|
|
3606
|
+
onError?.(error);
|
|
3607
|
+
}
|
|
3608
|
+
target.raw = raw;
|
|
3609
|
+
target.value = value2;
|
|
3610
|
+
target.decoded = raw !== null;
|
|
3611
|
+
notify(target);
|
|
3612
|
+
},
|
|
3613
|
+
[store, key, fallback, onError]
|
|
3614
|
+
);
|
|
3615
|
+
return [value, set];
|
|
3616
|
+
}
|
|
3617
|
+
const NONE = {
|
|
3618
|
+
documents: "file:///data/Documents",
|
|
3619
|
+
applicationSupport: "file:///data/Library/Application%20Support",
|
|
3620
|
+
caches: "file:///data/Library/Caches",
|
|
3621
|
+
temporary: "file:///tmp"
|
|
3622
|
+
};
|
|
3623
|
+
async function bytesOf(contents) {
|
|
3624
|
+
if (typeof contents === "string") return new TextEncoder().encode(contents);
|
|
3625
|
+
if (contents instanceof Uint8Array) return contents;
|
|
3626
|
+
if (contents instanceof ArrayBuffer) return new Uint8Array(contents);
|
|
3627
|
+
const blob = contents;
|
|
3628
|
+
if (typeof blob.bytes === "function") return await blob.bytes();
|
|
3629
|
+
return new Uint8Array(await blob.arrayBuffer());
|
|
3630
|
+
}
|
|
3631
|
+
function createFileManager(host) {
|
|
3632
|
+
const required = () => {
|
|
3633
|
+
const files = host();
|
|
3634
|
+
if (!files) throw new Error("unsupported: this page keeps no files");
|
|
3635
|
+
return files;
|
|
3636
|
+
};
|
|
3637
|
+
const directories = () => host()?.directories ?? NONE;
|
|
3638
|
+
return {
|
|
3639
|
+
get available() {
|
|
3640
|
+
return host() !== void 0;
|
|
3641
|
+
},
|
|
3642
|
+
get documentsDirectory() {
|
|
3643
|
+
return directories().documents;
|
|
3644
|
+
},
|
|
3645
|
+
get applicationSupportDirectory() {
|
|
3646
|
+
return directories().applicationSupport;
|
|
3647
|
+
},
|
|
3648
|
+
get cachesDirectory() {
|
|
3649
|
+
return directories().caches;
|
|
3650
|
+
},
|
|
3651
|
+
get temporaryDirectory() {
|
|
3652
|
+
return directories().temporary;
|
|
3653
|
+
},
|
|
3654
|
+
url: (directory, path) => fileURL(directory, path),
|
|
3655
|
+
contents: async (url) => required().read(url),
|
|
3656
|
+
text: async (url) => new TextDecoder().decode(await required().read(url)),
|
|
3657
|
+
write: async (url, contents) => required().write(url, await bytesOf(contents)),
|
|
3658
|
+
contentsOfDirectory: async (url) => required().list(url),
|
|
3659
|
+
createDirectory: async (url) => required().createDirectory(url),
|
|
3660
|
+
fileExists: async (url) => await required().attributes(url) !== null,
|
|
3661
|
+
attributesOfItem: async (url) => {
|
|
3662
|
+
const attributes = await required().attributes(url);
|
|
3663
|
+
if (!attributes) throw new Error(`notFound: nothing at ${url}`);
|
|
3664
|
+
return attributes;
|
|
3665
|
+
},
|
|
3666
|
+
moveItem: async (from, to) => required().move(from, to),
|
|
3667
|
+
copyItem: async (from, to) => required().copy(from, to),
|
|
3668
|
+
removeItem: async (url) => required().remove(url),
|
|
3669
|
+
loadURL: async (url) => required().loadURL(url)
|
|
3670
|
+
};
|
|
3671
|
+
}
|
|
3672
|
+
const fileManager = createFileManager(() => browserEnvironment.files);
|
|
2910
3673
|
function Button(props) {
|
|
2911
3674
|
const { own, modifiers } = splitProps(props);
|
|
2912
3675
|
const environment = useViewEnvironment();
|
|
2913
3676
|
const ownStyle = takeModifier(modifiers, "buttonStyle");
|
|
2914
3677
|
const ownSize = takeModifier(modifiers, "controlSize");
|
|
2915
|
-
const style = ownStyle
|
|
3678
|
+
const style = ownStyle ?? environment.buttonStyle ?? "automatic";
|
|
2916
3679
|
const controlSize = ownSize ?? environment.controlSize;
|
|
2917
3680
|
const buttonProps = { role: own.role ?? null, style };
|
|
2918
3681
|
if (controlSize !== void 0 && controlSize !== "regular") buttonProps["controlSize"] = controlSize;
|
|
@@ -2930,9 +3693,13 @@ function Button(props) {
|
|
|
2930
3693
|
}
|
|
2931
3694
|
function Toggle(props) {
|
|
2932
3695
|
const { own, modifiers } = splitProps(props);
|
|
3696
|
+
const environment = useViewEnvironment();
|
|
3697
|
+
const style = takeModifier(modifiers, "toggleStyle") ?? environment.toggleStyle ?? "automatic";
|
|
2933
3698
|
const [isOn, setOn] = readBinding(own.isOn, own.onChange);
|
|
2934
3699
|
const label = own.title !== void 0 ? /* @__PURE__ */ jsx(Text, { children: own.title }) : own.children;
|
|
2935
|
-
|
|
3700
|
+
const toggleProps = { isOn };
|
|
3701
|
+
if (style !== "automatic") toggleProps["style"] = style;
|
|
3702
|
+
return applyModifiers(sb("toggle", toggleProps, { toggle: (event) => setOn(event.value) }, label), modifiers);
|
|
2936
3703
|
}
|
|
2937
3704
|
function Picker(props) {
|
|
2938
3705
|
const { own, modifiers } = splitProps(props);
|
|
@@ -3052,6 +3819,7 @@ function TextField(props) {
|
|
|
3052
3819
|
secure: own.secure ?? false
|
|
3053
3820
|
};
|
|
3054
3821
|
if (own.onSubmit) fieldProps["submit"] = true;
|
|
3822
|
+
if (own.onDeleteBackward) fieldProps["deleteBackward"] = true;
|
|
3055
3823
|
if (own.axis) fieldProps["axis"] = own.axis;
|
|
3056
3824
|
const submitLabel = ownSubmitLabel ?? environment.submitLabel;
|
|
3057
3825
|
if (submitLabel) fieldProps["submitLabel"] = submitLabel;
|
|
@@ -3059,12 +3827,13 @@ function TextField(props) {
|
|
|
3059
3827
|
if (hints.contentType) fieldProps["contentType"] = hints.contentType;
|
|
3060
3828
|
if (hints.autocapitalization) fieldProps["autocapitalization"] = hints.autocapitalization;
|
|
3061
3829
|
if (hints.autocorrectionDisabled) fieldProps["autocorrectionDisabled"] = true;
|
|
3062
|
-
const focus =
|
|
3830
|
+
const focus = focusBinding(own);
|
|
3063
3831
|
if (focus) fieldProps["focused"] = focus[0];
|
|
3064
3832
|
return applyModifiers(
|
|
3065
3833
|
sb("textfield", fieldProps, {
|
|
3066
3834
|
text: (event) => setText(event.value),
|
|
3067
3835
|
submit: () => own.onSubmit?.(),
|
|
3836
|
+
deletebackward: () => own.onDeleteBackward?.(),
|
|
3068
3837
|
focus: (event) => focus?.[1](event.value)
|
|
3069
3838
|
}),
|
|
3070
3839
|
modifiers
|
|
@@ -3073,6 +3842,19 @@ function TextField(props) {
|
|
|
3073
3842
|
function SecureField(props) {
|
|
3074
3843
|
return /* @__PURE__ */ jsx(TextField, { ...props, secure: true });
|
|
3075
3844
|
}
|
|
3845
|
+
function focusBinding(props) {
|
|
3846
|
+
if (props.focused === void 0) return null;
|
|
3847
|
+
if (props.focusedValue === void 0) return readBinding(props.focused);
|
|
3848
|
+
const value = props.focusedValue;
|
|
3849
|
+
const [current, set] = readBinding(props.focused);
|
|
3850
|
+
return [
|
|
3851
|
+
current === value,
|
|
3852
|
+
(on) => {
|
|
3853
|
+
if (on) set(value);
|
|
3854
|
+
else if (current === value) set(null);
|
|
3855
|
+
}
|
|
3856
|
+
];
|
|
3857
|
+
}
|
|
3076
3858
|
function ProgressView(props) {
|
|
3077
3859
|
const { own, modifiers } = splitProps(props);
|
|
3078
3860
|
if (own.value === void 0) {
|
|
@@ -3125,11 +3907,25 @@ function DatePicker(props) {
|
|
|
3125
3907
|
function TextEditor(props) {
|
|
3126
3908
|
const { own, modifiers } = splitProps(props);
|
|
3127
3909
|
const [text, setText] = readBinding(own.text, own.onChange);
|
|
3128
|
-
const focus =
|
|
3129
|
-
const
|
|
3910
|
+
const focus = focusBinding(own);
|
|
3911
|
+
const selection = own.selection !== void 0 ? readBinding(own.selection) : null;
|
|
3912
|
+
const rich = typeof text !== "string";
|
|
3913
|
+
const editor = { text: rich ? AttributedText.plain(text) : text };
|
|
3914
|
+
if (rich) editor["runs"] = protocolRuns(text);
|
|
3130
3915
|
if (focus) editor["focused"] = focus[0];
|
|
3916
|
+
if (selection) editor["selection"] = { start: selection[0].start, end: selection[0].end };
|
|
3917
|
+
if (own.onDeleteBackward) editor["deleteBackward"] = true;
|
|
3131
3918
|
return applyModifiers(
|
|
3132
|
-
sb("texteditor", editor, {
|
|
3919
|
+
sb("texteditor", editor, {
|
|
3920
|
+
text: (event) => setText(rich ? AttributedText.replacingCharacters(text, event.value) : event.value),
|
|
3921
|
+
deletebackward: () => own.onDeleteBackward?.(),
|
|
3922
|
+
// The native editor's own Bold, Italic or Underline over the text (iOS 26's edit menu): the runs it now shows.
|
|
3923
|
+
runs: (event) => {
|
|
3924
|
+
if (rich) setText(fromProtocolRuns(event.value));
|
|
3925
|
+
},
|
|
3926
|
+
focus: (event) => focus?.[1](event.value),
|
|
3927
|
+
selection: (event) => selection?.[1](event.value)
|
|
3928
|
+
}),
|
|
3133
3929
|
modifiers
|
|
3134
3930
|
);
|
|
3135
3931
|
}
|
|
@@ -3184,8 +3980,11 @@ function Form(props) {
|
|
|
3184
3980
|
}
|
|
3185
3981
|
function Section(props) {
|
|
3186
3982
|
const { own, modifiers } = splitProps(props);
|
|
3187
|
-
const
|
|
3188
|
-
|
|
3983
|
+
const pinned = useContext(PinnedContext);
|
|
3984
|
+
let header = supplement(own.header);
|
|
3985
|
+
let footer = supplement(own.footer);
|
|
3986
|
+
if (pinned?.includes("sectionHeaders") && header.text !== null && !header.view) header = { text: null, view: /* @__PURE__ */ jsx(Text, { children: header.text }) };
|
|
3987
|
+
if (pinned?.includes("sectionFooters") && footer.text !== null && !footer.view) footer = { text: null, view: /* @__PURE__ */ jsx(Text, { children: footer.text }) };
|
|
3189
3988
|
const children = [];
|
|
3190
3989
|
if (header.view) children.push(sb("sectionheader", {}, void 0, header.view));
|
|
3191
3990
|
if (own.children !== void 0) children.push(wrapRows(own.children));
|
|
@@ -3441,7 +4240,9 @@ function AsyncImage(props) {
|
|
|
3441
4240
|
loaded.set(own.url, "failed");
|
|
3442
4241
|
if (live) setState("failed");
|
|
3443
4242
|
};
|
|
3444
|
-
|
|
4243
|
+
const src = loadableURL(own.url);
|
|
4244
|
+
if (typeof src === "string") image.src = src;
|
|
4245
|
+
else void src.then((resolved) => live && (image.src = resolved));
|
|
3445
4246
|
return () => {
|
|
3446
4247
|
live = false;
|
|
3447
4248
|
};
|
|
@@ -3720,6 +4521,11 @@ function Chart(props) {
|
|
|
3720
4521
|
const centers = [...new Set(marks.filter((m) => m.kind === "bar").map((m) => ux(m.props.x)))].sort((a, b) => a - b);
|
|
3721
4522
|
for (let i = 1; i < centers.length; i++) continuousBand = Math.min(continuousBand, centers[i] - centers[i - 1]);
|
|
3722
4523
|
}
|
|
4524
|
+
const positions = [];
|
|
4525
|
+
for (const mark of marks) {
|
|
4526
|
+
const key = mark.kind === "bar" ? mark.props.positionBy : void 0;
|
|
4527
|
+
if (key !== void 0 && !positions.includes(key)) positions.push(key);
|
|
4528
|
+
}
|
|
3723
4529
|
const encoded = [];
|
|
3724
4530
|
const items = [];
|
|
3725
4531
|
const annotate = (mark, rect) => {
|
|
@@ -3759,7 +4565,12 @@ function Chart(props) {
|
|
|
3759
4565
|
switch (mark.kind) {
|
|
3760
4566
|
case "bar": {
|
|
3761
4567
|
const style = common(mark);
|
|
3762
|
-
|
|
4568
|
+
let [b0, b1] = xScale.kind === "band" ? xScale.band(p.x) : [ux(p.x) - continuousBand / 2, ux(p.x) + continuousBand / 2];
|
|
4569
|
+
if (p.positionBy !== void 0 && positions.length > 1) {
|
|
4570
|
+
const slot = (b1 - b0) * 0.8 / positions.length;
|
|
4571
|
+
const start = b0 + (b1 - b0) * 0.1 + slot * positions.indexOf(p.positionBy);
|
|
4572
|
+
[b0, b1] = [start - slot / 16, start + slot + slot / 16];
|
|
4573
|
+
}
|
|
3763
4574
|
const center = round2((b0 + b1) / 2);
|
|
3764
4575
|
const half = round2((b1 - b0) * 0.8 / 2);
|
|
3765
4576
|
const top = uy(Math.max(asNumber(p.y), p.yStart ?? 0));
|
|
@@ -4298,12 +5109,12 @@ function TimelineView({ schedule, children }) {
|
|
|
4298
5109
|
};
|
|
4299
5110
|
}
|
|
4300
5111
|
if (typeof schedule === "object" && "periodic" in schedule) {
|
|
4301
|
-
const
|
|
5112
|
+
const every2 = Math.max(schedule.periodic.by, 0.01) * 1e3;
|
|
4302
5113
|
const from = schedule.periodic.from?.getTime() ?? Date.now();
|
|
4303
5114
|
let timer;
|
|
4304
5115
|
const tick = () => {
|
|
4305
5116
|
const now = Date.now();
|
|
4306
|
-
const next = from + Math.ceil((now - from + 1) /
|
|
5117
|
+
const next = from + Math.ceil((now - from + 1) / every2) * every2;
|
|
4307
5118
|
timer = setTimeout(() => {
|
|
4308
5119
|
setDate(new Date(next));
|
|
4309
5120
|
tick();
|
|
@@ -4335,7 +5146,10 @@ class AVPlayer {
|
|
|
4335
5146
|
timeListeners = /* @__PURE__ */ new Set();
|
|
4336
5147
|
time = 0;
|
|
4337
5148
|
serial = 0;
|
|
5149
|
+
/** @internal The `report` interval for the views showing this player. */
|
|
5150
|
+
report;
|
|
4338
5151
|
constructor(url = null, options = {}) {
|
|
5152
|
+
this.report = options.report;
|
|
4339
5153
|
this.state = {
|
|
4340
5154
|
url,
|
|
4341
5155
|
rate: 0,
|
|
@@ -4537,6 +5351,7 @@ function videoElement(player, state, options) {
|
|
|
4537
5351
|
gravity: options.gravity ?? "resizeAspect",
|
|
4538
5352
|
controls: options.controls,
|
|
4539
5353
|
seek: state.seek,
|
|
5354
|
+
...player.report !== void 0 && player.report > 0 ? { report: player.report } : {},
|
|
4540
5355
|
...state.size ? { width: state.size.width, height: state.size.height } : {}
|
|
4541
5356
|
},
|
|
4542
5357
|
{ video: (event) => player.handle(event) }
|
|
@@ -4692,6 +5507,7 @@ export {
|
|
|
4692
5507
|
AngularGradient,
|
|
4693
5508
|
AreaMark,
|
|
4694
5509
|
AsyncImage,
|
|
5510
|
+
AttributedText,
|
|
4695
5511
|
AudioPlayer,
|
|
4696
5512
|
BarMark,
|
|
4697
5513
|
Button,
|
|
@@ -4716,6 +5532,7 @@ export {
|
|
|
4716
5532
|
ForEach,
|
|
4717
5533
|
Form,
|
|
4718
5534
|
Gauge,
|
|
5535
|
+
GeometryChangeView,
|
|
4719
5536
|
GeometryReader,
|
|
4720
5537
|
GraphicsContext,
|
|
4721
5538
|
Grid,
|
|
@@ -4748,6 +5565,7 @@ export {
|
|
|
4748
5565
|
Path,
|
|
4749
5566
|
PhaseAnimator,
|
|
4750
5567
|
Picker,
|
|
5568
|
+
PinnedContext,
|
|
4751
5569
|
PointMark,
|
|
4752
5570
|
ProgressView,
|
|
4753
5571
|
RadialGradient,
|
|
@@ -4777,6 +5595,8 @@ export {
|
|
|
4777
5595
|
ToolbarItem,
|
|
4778
5596
|
ToolbarItemGroup,
|
|
4779
5597
|
ToolbarSpacer,
|
|
5598
|
+
UndoManager,
|
|
5599
|
+
UndoManagerContext,
|
|
4780
5600
|
UnevenRoundedRectangle,
|
|
4781
5601
|
VStack,
|
|
4782
5602
|
VideoPlayer,
|
|
@@ -4784,9 +5604,14 @@ export {
|
|
|
4784
5604
|
VisualEffect,
|
|
4785
5605
|
ZStack,
|
|
4786
5606
|
answerLayout,
|
|
5607
|
+
appStorage,
|
|
5608
|
+
canMeasureText,
|
|
4787
5609
|
color,
|
|
5610
|
+
createFileManager,
|
|
4788
5611
|
createSwiftUIRoot,
|
|
5612
|
+
fileManager,
|
|
4789
5613
|
flattenChildren,
|
|
5614
|
+
focusBinding,
|
|
4790
5615
|
font,
|
|
4791
5616
|
frame,
|
|
4792
5617
|
geometryProxy,
|
|
@@ -4794,7 +5619,9 @@ export {
|
|
|
4794
5619
|
hex,
|
|
4795
5620
|
injectIntoDevTools,
|
|
4796
5621
|
insets,
|
|
5622
|
+
installTextMeasurer,
|
|
4797
5623
|
keyframeValues,
|
|
5624
|
+
measureText,
|
|
4798
5625
|
mount,
|
|
4799
5626
|
moveItems,
|
|
4800
5627
|
nextGeometry,
|
|
@@ -4807,12 +5634,15 @@ export {
|
|
|
4807
5634
|
providingScrollSettings,
|
|
4808
5635
|
readBinding,
|
|
4809
5636
|
rgb,
|
|
5637
|
+
scrollGeometryOf,
|
|
4810
5638
|
shapeStyle,
|
|
4811
5639
|
system,
|
|
4812
5640
|
takeScrollSettings,
|
|
4813
5641
|
timeTicks,
|
|
4814
5642
|
unitPoint,
|
|
4815
5643
|
useAVPlayer,
|
|
5644
|
+
useAppStorage,
|
|
5645
|
+
useAudioRecorder,
|
|
4816
5646
|
useColorScheme,
|
|
4817
5647
|
useContextBridge,
|
|
4818
5648
|
useCoordinateSpaces,
|
|
@@ -4824,6 +5654,7 @@ export {
|
|
|
4824
5654
|
useRedacted,
|
|
4825
5655
|
useRefreshable,
|
|
4826
5656
|
useReportSplitSelection,
|
|
5657
|
+
useUndoManager,
|
|
4827
5658
|
useViewEnvironment,
|
|
4828
5659
|
weighted,
|
|
4829
5660
|
withAnimation
|