@pictor-sdk/ui 0.2.0 → 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/README.md +1 -1
- package/dist/index.d.mts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +105 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +101 -24
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/composer/TextComposer.tsx +64 -22
- package/src/composer/keyboardLayout.ts +83 -0
- package/src/gestures/LayerGestureSurface.tsx +61 -13
package/dist/index.mjs
CHANGED
|
@@ -745,10 +745,12 @@ function LayerGestureSurface({ onSnap }) {
|
|
|
745
745
|
);
|
|
746
746
|
const aspect = document.size.height / document.size.width;
|
|
747
747
|
const [measureVersion, setMeasureVersion] = useState2(0);
|
|
748
|
-
useEffect2(
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
748
|
+
useEffect2(() => {
|
|
749
|
+
const refresh = () => setMeasureVersion((v) => v + 1);
|
|
750
|
+
const unsubscribe = renderer.textMeasures.subscribe(refresh);
|
|
751
|
+
refresh();
|
|
752
|
+
return unsubscribe;
|
|
753
|
+
}, [renderer]);
|
|
752
754
|
const hitCandidates = useMemo5(
|
|
753
755
|
() => document.layers.map((layer, index) => ({ layer, index })).filter(
|
|
754
756
|
({ layer, index }) => !layer.hidden && !layer.locked && isDirectManipulationLayer(layer, index, profile.interaction?.layers)
|
|
@@ -801,6 +803,7 @@ function LayerGestureSurface({ onSnap }) {
|
|
|
801
803
|
const focalY = useSharedValue3(0.5);
|
|
802
804
|
const pointers = useSharedValue3(0);
|
|
803
805
|
const active = useSharedValue3(false);
|
|
806
|
+
const livePointers = useSharedValue3(0);
|
|
804
807
|
const snapCX = useSharedValue3(false);
|
|
805
808
|
const snapCY = useSharedValue3(false);
|
|
806
809
|
const snapR = useSharedValue3(false);
|
|
@@ -975,7 +978,23 @@ function LayerGestureSurface({ onSnap }) {
|
|
|
975
978
|
deleted
|
|
976
979
|
);
|
|
977
980
|
};
|
|
978
|
-
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) => {
|
|
979
998
|
"worklet";
|
|
980
999
|
if (active.value) return;
|
|
981
1000
|
rawPanX.value = 0;
|
|
@@ -1022,7 +1041,7 @@ function LayerGestureSurface({ onSnap }) {
|
|
|
1022
1041
|
recompute(false);
|
|
1023
1042
|
}).onFinalize(() => {
|
|
1024
1043
|
"worklet";
|
|
1025
|
-
|
|
1044
|
+
releaseIfEmpty();
|
|
1026
1045
|
});
|
|
1027
1046
|
const pinch = Gesture4.Pinch().onUpdate((e) => {
|
|
1028
1047
|
"worklet";
|
|
@@ -1031,6 +1050,9 @@ function LayerGestureSurface({ onSnap }) {
|
|
|
1031
1050
|
focalY.value = e.focalY / metricsHeight;
|
|
1032
1051
|
syncPointers(e.numberOfPointers);
|
|
1033
1052
|
recompute(false);
|
|
1053
|
+
}).onFinalize(() => {
|
|
1054
|
+
"worklet";
|
|
1055
|
+
releaseIfEmpty();
|
|
1034
1056
|
});
|
|
1035
1057
|
const rotate = Gesture4.Rotation().enabled(config.rotationEnabled).onUpdate((e) => {
|
|
1036
1058
|
"worklet";
|
|
@@ -1039,6 +1061,9 @@ function LayerGestureSurface({ onSnap }) {
|
|
|
1039
1061
|
focalY.value = e.anchorY / metricsHeight;
|
|
1040
1062
|
syncPointers(e.numberOfPointers);
|
|
1041
1063
|
recompute(false);
|
|
1064
|
+
}).onFinalize(() => {
|
|
1065
|
+
"worklet";
|
|
1066
|
+
releaseIfEmpty();
|
|
1042
1067
|
});
|
|
1043
1068
|
const tap = Gesture4.Tap().onEnd((e) => {
|
|
1044
1069
|
"worklet";
|
|
@@ -3497,17 +3522,21 @@ import {
|
|
|
3497
3522
|
TextInput,
|
|
3498
3523
|
View as View18
|
|
3499
3524
|
} from "react-native";
|
|
3525
|
+
import { useRenderer as useRenderer4 } from "@pictor-sdk/skia";
|
|
3500
3526
|
|
|
3501
3527
|
// src/composer/keyboardLayout.ts
|
|
3502
3528
|
var HIDDEN_KEYBOARD = { height: 0 };
|
|
3503
3529
|
var MAX_DURATION = 400;
|
|
3504
3530
|
var FALLBACK_DURATION = 180;
|
|
3505
|
-
function composerInsets(frame, safeAreaBottom = 0) {
|
|
3531
|
+
function composerInsets(frame, safeAreaBottom = 0, safeAreaTop = 0) {
|
|
3506
3532
|
const height = Number.isFinite(frame.height) ? Math.max(0, frame.height) : 0;
|
|
3507
3533
|
const visible = height > 0;
|
|
3508
3534
|
const raw = frame.duration;
|
|
3509
3535
|
const duration = raw === void 0 || !Number.isFinite(raw) || raw <= 0 ? FALLBACK_DURATION : Math.min(MAX_DURATION, raw);
|
|
3510
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,
|
|
3511
3540
|
bottom: visible ? height : Math.max(0, safeAreaBottom),
|
|
3512
3541
|
keyboardVisible: visible,
|
|
3513
3542
|
duration
|
|
@@ -3527,6 +3556,31 @@ function fitCanvasRect(container, document) {
|
|
|
3527
3556
|
top: (container.height - height) / 2
|
|
3528
3557
|
};
|
|
3529
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
|
+
}
|
|
3530
3584
|
|
|
3531
3585
|
// src/composer/useTextSession.ts
|
|
3532
3586
|
import {
|
|
@@ -3674,26 +3728,45 @@ function TextComposer({ session, insets, onDone, onCancel }) {
|
|
|
3674
3728
|
const store = useEditorStore5();
|
|
3675
3729
|
const { document } = useEditor22();
|
|
3676
3730
|
const [container, setContainer] = useState10({ width: 0, height: 0 });
|
|
3731
|
+
const renderer = useRenderer4();
|
|
3677
3732
|
const input = useRef13(null);
|
|
3678
3733
|
const [selectionRange, setSelectionRange] = useState10(
|
|
3679
3734
|
void 0
|
|
3680
3735
|
);
|
|
3681
3736
|
const data = session.data;
|
|
3682
3737
|
const span = session.span;
|
|
3683
|
-
const
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
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
|
+
);
|
|
3697
3770
|
const fonts = useMemo11(() => fontChoices(store.registry.fonts), [store.registry.fonts]);
|
|
3698
3771
|
useEffect8(() => {
|
|
3699
3772
|
const handle = setTimeout(() => input.current?.focus(), 50);
|
|
@@ -3832,7 +3905,7 @@ function TextComposer({ session, insets, onDone, onCancel }) {
|
|
|
3832
3905
|
position: "absolute",
|
|
3833
3906
|
left: 0,
|
|
3834
3907
|
right: 0,
|
|
3835
|
-
top:
|
|
3908
|
+
top: insets.top,
|
|
3836
3909
|
height: 56,
|
|
3837
3910
|
flexDirection: "row",
|
|
3838
3911
|
alignItems: "center",
|
|
@@ -4023,7 +4096,8 @@ function TextComposer({ session, insets, onDone, onCancel }) {
|
|
|
4023
4096
|
);
|
|
4024
4097
|
}
|
|
4025
4098
|
function TextComposerHost({
|
|
4026
|
-
Composer = TextComposer
|
|
4099
|
+
Composer = TextComposer,
|
|
4100
|
+
safeArea
|
|
4027
4101
|
}) {
|
|
4028
4102
|
const { setActiveTool } = useEditor22();
|
|
4029
4103
|
const session = useTextSession();
|
|
@@ -4052,7 +4126,10 @@ function TextComposerHost({
|
|
|
4052
4126
|
hide.remove();
|
|
4053
4127
|
};
|
|
4054
4128
|
}, []);
|
|
4055
|
-
const insets = useMemo11(
|
|
4129
|
+
const insets = useMemo11(
|
|
4130
|
+
() => composerInsets(frame, safeArea?.bottom ?? 0, safeArea?.top ?? 0),
|
|
4131
|
+
[frame, safeArea?.bottom, safeArea?.top]
|
|
4132
|
+
);
|
|
4056
4133
|
const finish = useCallback6(
|
|
4057
4134
|
(commit) => {
|
|
4058
4135
|
if (commit) session.commit();
|