@pictor-sdk/ui 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +99 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +95 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/src/composer/TextComposer.tsx +64 -22
- package/src/composer/keyboardLayout.ts +83 -0
- package/src/gestures/LayerGestureSurface.tsx +54 -9
package/dist/index.mjs
CHANGED
|
@@ -803,6 +803,7 @@ function LayerGestureSurface({ onSnap }) {
|
|
|
803
803
|
const focalY = useSharedValue3(0.5);
|
|
804
804
|
const pointers = useSharedValue3(0);
|
|
805
805
|
const active = useSharedValue3(false);
|
|
806
|
+
const livePointers = useSharedValue3(0);
|
|
806
807
|
const snapCX = useSharedValue3(false);
|
|
807
808
|
const snapCY = useSharedValue3(false);
|
|
808
809
|
const snapR = useSharedValue3(false);
|
|
@@ -977,7 +978,23 @@ function LayerGestureSurface({ onSnap }) {
|
|
|
977
978
|
deleted
|
|
978
979
|
);
|
|
979
980
|
};
|
|
980
|
-
const
|
|
981
|
+
const releaseIfEmpty = () => {
|
|
982
|
+
"worklet";
|
|
983
|
+
if (livePointers.value > 0) return;
|
|
984
|
+
endIfIdle();
|
|
985
|
+
};
|
|
986
|
+
const pan = Gesture4.Pan().minPointers(1).maxPointers(2).onTouchesDown((e) => {
|
|
987
|
+
"worklet";
|
|
988
|
+
livePointers.value = e.allTouches.length;
|
|
989
|
+
}).onTouchesUp((e) => {
|
|
990
|
+
"worklet";
|
|
991
|
+
livePointers.value = Math.max(0, e.allTouches.length - e.changedTouches.length);
|
|
992
|
+
releaseIfEmpty();
|
|
993
|
+
}).onTouchesCancelled(() => {
|
|
994
|
+
"worklet";
|
|
995
|
+
livePointers.value = 0;
|
|
996
|
+
releaseIfEmpty();
|
|
997
|
+
}).onBegin((e) => {
|
|
981
998
|
"worklet";
|
|
982
999
|
if (active.value) return;
|
|
983
1000
|
rawPanX.value = 0;
|
|
@@ -1024,7 +1041,7 @@ function LayerGestureSurface({ onSnap }) {
|
|
|
1024
1041
|
recompute(false);
|
|
1025
1042
|
}).onFinalize(() => {
|
|
1026
1043
|
"worklet";
|
|
1027
|
-
|
|
1044
|
+
releaseIfEmpty();
|
|
1028
1045
|
});
|
|
1029
1046
|
const pinch = Gesture4.Pinch().onUpdate((e) => {
|
|
1030
1047
|
"worklet";
|
|
@@ -1033,6 +1050,9 @@ function LayerGestureSurface({ onSnap }) {
|
|
|
1033
1050
|
focalY.value = e.focalY / metricsHeight;
|
|
1034
1051
|
syncPointers(e.numberOfPointers);
|
|
1035
1052
|
recompute(false);
|
|
1053
|
+
}).onFinalize(() => {
|
|
1054
|
+
"worklet";
|
|
1055
|
+
releaseIfEmpty();
|
|
1036
1056
|
});
|
|
1037
1057
|
const rotate = Gesture4.Rotation().enabled(config.rotationEnabled).onUpdate((e) => {
|
|
1038
1058
|
"worklet";
|
|
@@ -1041,6 +1061,9 @@ function LayerGestureSurface({ onSnap }) {
|
|
|
1041
1061
|
focalY.value = e.anchorY / metricsHeight;
|
|
1042
1062
|
syncPointers(e.numberOfPointers);
|
|
1043
1063
|
recompute(false);
|
|
1064
|
+
}).onFinalize(() => {
|
|
1065
|
+
"worklet";
|
|
1066
|
+
releaseIfEmpty();
|
|
1044
1067
|
});
|
|
1045
1068
|
const tap = Gesture4.Tap().onEnd((e) => {
|
|
1046
1069
|
"worklet";
|
|
@@ -3499,17 +3522,21 @@ import {
|
|
|
3499
3522
|
TextInput,
|
|
3500
3523
|
View as View18
|
|
3501
3524
|
} from "react-native";
|
|
3525
|
+
import { useRenderer as useRenderer4 } from "@pictor-sdk/skia";
|
|
3502
3526
|
|
|
3503
3527
|
// src/composer/keyboardLayout.ts
|
|
3504
3528
|
var HIDDEN_KEYBOARD = { height: 0 };
|
|
3505
3529
|
var MAX_DURATION = 400;
|
|
3506
3530
|
var FALLBACK_DURATION = 180;
|
|
3507
|
-
function composerInsets(frame, safeAreaBottom = 0) {
|
|
3531
|
+
function composerInsets(frame, safeAreaBottom = 0, safeAreaTop = 0) {
|
|
3508
3532
|
const height = Number.isFinite(frame.height) ? Math.max(0, frame.height) : 0;
|
|
3509
3533
|
const visible = height > 0;
|
|
3510
3534
|
const raw = frame.duration;
|
|
3511
3535
|
const duration = raw === void 0 || !Number.isFinite(raw) || raw <= 0 ? FALLBACK_DURATION : Math.min(MAX_DURATION, raw);
|
|
3512
3536
|
return {
|
|
3537
|
+
// The keyboard never covers the top, so this one is the safe area and
|
|
3538
|
+
// nothing else.
|
|
3539
|
+
top: Number.isFinite(safeAreaTop) ? Math.max(0, safeAreaTop) : 0,
|
|
3513
3540
|
bottom: visible ? height : Math.max(0, safeAreaBottom),
|
|
3514
3541
|
keyboardVisible: visible,
|
|
3515
3542
|
duration
|
|
@@ -3529,6 +3556,31 @@ function fitCanvasRect(container, document) {
|
|
|
3529
3556
|
top: (container.height - height) / 2
|
|
3530
3557
|
};
|
|
3531
3558
|
}
|
|
3559
|
+
function composerInputBox(request) {
|
|
3560
|
+
const canvas = fitCanvasRect(request.container, request.document);
|
|
3561
|
+
const fontSize = Math.max(1, request.fontSize * canvas.width);
|
|
3562
|
+
const lineHeight = Math.max(1, fontSize * request.lineHeight);
|
|
3563
|
+
const scale = request.document.width > 0 ? canvas.width / request.document.width : 0;
|
|
3564
|
+
const measuredHeight = request.measured && request.measured.height > 0 ? request.measured.height * scale : 0;
|
|
3565
|
+
const estimated = lineHeight * Math.max(1, request.lines);
|
|
3566
|
+
const height = Math.min(
|
|
3567
|
+
Math.max(lineHeight, measuredHeight > 0 ? measuredHeight : estimated),
|
|
3568
|
+
Math.max(lineHeight, canvas.height)
|
|
3569
|
+
);
|
|
3570
|
+
const centre = canvas.top + request.centerY * canvas.height;
|
|
3571
|
+
const top = Math.min(
|
|
3572
|
+
Math.max(0, centre - height / 2),
|
|
3573
|
+
Math.max(0, request.container.height - height)
|
|
3574
|
+
);
|
|
3575
|
+
return {
|
|
3576
|
+
left: canvas.left,
|
|
3577
|
+
width: Math.max(1, canvas.width),
|
|
3578
|
+
top,
|
|
3579
|
+
height: Math.max(1, height),
|
|
3580
|
+
fontSize,
|
|
3581
|
+
lineHeight
|
|
3582
|
+
};
|
|
3583
|
+
}
|
|
3532
3584
|
|
|
3533
3585
|
// src/composer/useTextSession.ts
|
|
3534
3586
|
import {
|
|
@@ -3676,26 +3728,45 @@ function TextComposer({ session, insets, onDone, onCancel }) {
|
|
|
3676
3728
|
const store = useEditorStore5();
|
|
3677
3729
|
const { document } = useEditor22();
|
|
3678
3730
|
const [container, setContainer] = useState10({ width: 0, height: 0 });
|
|
3731
|
+
const renderer = useRenderer4();
|
|
3679
3732
|
const input = useRef13(null);
|
|
3680
3733
|
const [selectionRange, setSelectionRange] = useState10(
|
|
3681
3734
|
void 0
|
|
3682
3735
|
);
|
|
3683
3736
|
const data = session.data;
|
|
3684
3737
|
const span = session.span;
|
|
3685
|
-
const
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3738
|
+
const layerId = session.layer?.id;
|
|
3739
|
+
const [measured, setMeasured] = useState10(null);
|
|
3740
|
+
useEffect8(() => {
|
|
3741
|
+
if (!layerId) {
|
|
3742
|
+
setMeasured(null);
|
|
3743
|
+
return;
|
|
3744
|
+
}
|
|
3745
|
+
const refresh = () => setMeasured(renderer.textMeasures.get(layerId) ?? null);
|
|
3746
|
+
const unsubscribe = renderer.textMeasures.subscribe(refresh);
|
|
3747
|
+
refresh();
|
|
3748
|
+
return unsubscribe;
|
|
3749
|
+
}, [renderer, layerId]);
|
|
3750
|
+
const layout = useMemo11(
|
|
3751
|
+
() => composerInputBox({
|
|
3752
|
+
container,
|
|
3753
|
+
document: document.size,
|
|
3754
|
+
fontSize: span?.fontSize ?? 0.08,
|
|
3755
|
+
lineHeight: span?.lineHeight ?? 1.2,
|
|
3756
|
+
centerY: session.layer?.transform.y ?? 0.42,
|
|
3757
|
+
measured,
|
|
3758
|
+
lines: session.value.split("\n").length
|
|
3759
|
+
}),
|
|
3760
|
+
[
|
|
3761
|
+
container,
|
|
3762
|
+
document.size,
|
|
3763
|
+
span?.fontSize,
|
|
3764
|
+
span?.lineHeight,
|
|
3765
|
+
session.layer?.transform.y,
|
|
3766
|
+
session.value,
|
|
3767
|
+
measured
|
|
3768
|
+
]
|
|
3769
|
+
);
|
|
3699
3770
|
const fonts = useMemo11(() => fontChoices(store.registry.fonts), [store.registry.fonts]);
|
|
3700
3771
|
useEffect8(() => {
|
|
3701
3772
|
const handle = setTimeout(() => input.current?.focus(), 50);
|
|
@@ -3834,7 +3905,7 @@ function TextComposer({ session, insets, onDone, onCancel }) {
|
|
|
3834
3905
|
position: "absolute",
|
|
3835
3906
|
left: 0,
|
|
3836
3907
|
right: 0,
|
|
3837
|
-
top:
|
|
3908
|
+
top: insets.top,
|
|
3838
3909
|
height: 56,
|
|
3839
3910
|
flexDirection: "row",
|
|
3840
3911
|
alignItems: "center",
|
|
@@ -4025,7 +4096,8 @@ function TextComposer({ session, insets, onDone, onCancel }) {
|
|
|
4025
4096
|
);
|
|
4026
4097
|
}
|
|
4027
4098
|
function TextComposerHost({
|
|
4028
|
-
Composer = TextComposer
|
|
4099
|
+
Composer = TextComposer,
|
|
4100
|
+
safeArea
|
|
4029
4101
|
}) {
|
|
4030
4102
|
const { setActiveTool } = useEditor22();
|
|
4031
4103
|
const session = useTextSession();
|
|
@@ -4054,7 +4126,10 @@ function TextComposerHost({
|
|
|
4054
4126
|
hide.remove();
|
|
4055
4127
|
};
|
|
4056
4128
|
}, []);
|
|
4057
|
-
const insets = useMemo11(
|
|
4129
|
+
const insets = useMemo11(
|
|
4130
|
+
() => composerInsets(frame, safeArea?.bottom ?? 0, safeArea?.top ?? 0),
|
|
4131
|
+
[frame, safeArea?.bottom, safeArea?.top]
|
|
4132
|
+
);
|
|
4058
4133
|
const finish = useCallback6(
|
|
4059
4134
|
(commit) => {
|
|
4060
4135
|
if (commit) session.commit();
|