canvu-react 0.4.40 → 0.4.42
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/native.cjs +107 -3
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +24 -1
- package/dist/native.d.ts +24 -1
- package/dist/native.js +107 -3
- package/dist/native.js.map +1 -1
- package/package.json +1 -1
package/dist/native.d.cts
CHANGED
|
@@ -189,6 +189,19 @@ declare const DEFAULT_NATIVE_VECTOR_TOOLS: readonly NativeVectorToolDefinition[]
|
|
|
189
189
|
*/
|
|
190
190
|
declare function NativeVectorToolbar({ value, onChange, tools, overflowToolIds, overflowMenuAccessibilityLabel, disabled, disabledToolIds, showToolLockToggle, toolLocked, onToolLockedChange, density, accessibilityLabel, style, contentContainerStyle, overflowPanelStyle, toolButtonStyle, activeToolButtonStyle, toolLabelStyle, activeToolLabelStyle, renderToolIcon, renderToolLockIcon, renderOverflowIcon, renderOverflowChevronIcon, renderToolButton, }: NativeVectorToolbarProps): react_jsx_runtime.JSX.Element;
|
|
191
191
|
|
|
192
|
+
type NativeRemotePresenceHitTarget = "cursor" | "label";
|
|
193
|
+
type NativeRemotePresenceHit = {
|
|
194
|
+
readonly peer: RemotePresencePeer;
|
|
195
|
+
readonly followId: string;
|
|
196
|
+
readonly target: NativeRemotePresenceHitTarget;
|
|
197
|
+
readonly screenX: number;
|
|
198
|
+
readonly screenY: number;
|
|
199
|
+
readonly worldX: number;
|
|
200
|
+
readonly worldY: number;
|
|
201
|
+
readonly clientId?: string;
|
|
202
|
+
readonly peerId?: string;
|
|
203
|
+
};
|
|
204
|
+
|
|
192
205
|
type NativeCustomShapePlacementOptions = {
|
|
193
206
|
readonly toolId: string;
|
|
194
207
|
readonly createItem: (args: {
|
|
@@ -215,6 +228,15 @@ type NativeWorldPointerDownDetail = {
|
|
|
215
228
|
readonly screenX: number;
|
|
216
229
|
readonly screenY: number;
|
|
217
230
|
};
|
|
231
|
+
/**
|
|
232
|
+
* Detail emitted when a native pointer/touch begins on a remote realtime
|
|
233
|
+
* participant label or cursor.
|
|
234
|
+
*
|
|
235
|
+
* Pass `followId` into your `followedPeerId` state for
|
|
236
|
+
* `useRealtimePeerFollow(...)`. For realtime-session peers this is the
|
|
237
|
+
* connection `clientId`, matching the session peer `id`.
|
|
238
|
+
*/
|
|
239
|
+
type NativeRemotePresencePressDetail = NativeRemotePresenceHit;
|
|
218
240
|
/**
|
|
219
241
|
* Optional override for a link item inserted from the native link tool.
|
|
220
242
|
*
|
|
@@ -287,6 +309,7 @@ type NativeVectorViewportProps = {
|
|
|
287
309
|
readonly onLinkToolRequest?: (detail: NativeLinkToolRequestDetail) => void;
|
|
288
310
|
readonly linkToolDialogLabels?: NativeLinkToolDialogLabels;
|
|
289
311
|
readonly onWorldPointerDown?: (detail: NativeWorldPointerDownDetail) => void;
|
|
312
|
+
readonly onRemotePresencePress?: (detail: NativeRemotePresencePressDetail) => void;
|
|
290
313
|
readonly onWorldPointerMove?: (world: {
|
|
291
314
|
readonly x: number;
|
|
292
315
|
readonly y: number;
|
|
@@ -434,4 +457,4 @@ type SvgNode = SvgRectNode | SvgEllipseNode | SvgCircleNode | SvgLineNode | SvgP
|
|
|
434
457
|
*/
|
|
435
458
|
declare function parseSvgFragment(xml: string): SvgNode[];
|
|
436
459
|
|
|
437
|
-
export { CanvuLinkData, DEFAULT_NATIVE_OVERFLOW_TOOL_IDS, DEFAULT_NATIVE_VECTOR_TOOLS, NATIVE_STYLE_PALETTE, type NativeCustomShapePlacementOptions, NativeInteractionOverlay, type NativeInteractionOverlayProps, type NativeLinkToolDialogLabels, type NativeLinkToolInsertOptions, type NativeLinkToolRequestDetail, NativeSceneRenderer, type NativeSceneRendererProps, NativeShapeRenderer, type NativeShapeRendererProps, type NativeStyleColor, NativeVectorStyleInspector, type NativeVectorStyleInspectorProps, type NativeVectorStyleToolId, type NativeVectorToolDefinition, NativeVectorToolbar, type NativeVectorToolbarDensity, type NativeVectorToolbarProps, type NativeVectorToolbarRenderOverflowInput, type NativeVectorToolbarRenderToolInput, type NativeVectorToolbarRenderToolLockInput, NativeVectorViewport, type NativeVectorViewportHandle, type NativeVectorViewportProps, type NativeWorldPointerDownDetail, type PlacementPreview, type SvgNode, VectorSceneItem, nativeStyleColorWithOpacity, normalizeNativeStyleHex, parseSvgFragment };
|
|
460
|
+
export { CanvuLinkData, DEFAULT_NATIVE_OVERFLOW_TOOL_IDS, DEFAULT_NATIVE_VECTOR_TOOLS, NATIVE_STYLE_PALETTE, type NativeCustomShapePlacementOptions, NativeInteractionOverlay, type NativeInteractionOverlayProps, type NativeLinkToolDialogLabels, type NativeLinkToolInsertOptions, type NativeLinkToolRequestDetail, type NativeRemotePresencePressDetail, NativeSceneRenderer, type NativeSceneRendererProps, NativeShapeRenderer, type NativeShapeRendererProps, type NativeStyleColor, NativeVectorStyleInspector, type NativeVectorStyleInspectorProps, type NativeVectorStyleToolId, type NativeVectorToolDefinition, NativeVectorToolbar, type NativeVectorToolbarDensity, type NativeVectorToolbarProps, type NativeVectorToolbarRenderOverflowInput, type NativeVectorToolbarRenderToolInput, type NativeVectorToolbarRenderToolLockInput, NativeVectorViewport, type NativeVectorViewportHandle, type NativeVectorViewportProps, type NativeWorldPointerDownDetail, type PlacementPreview, type SvgNode, VectorSceneItem, nativeStyleColorWithOpacity, normalizeNativeStyleHex, parseSvgFragment };
|
package/dist/native.d.ts
CHANGED
|
@@ -189,6 +189,19 @@ declare const DEFAULT_NATIVE_VECTOR_TOOLS: readonly NativeVectorToolDefinition[]
|
|
|
189
189
|
*/
|
|
190
190
|
declare function NativeVectorToolbar({ value, onChange, tools, overflowToolIds, overflowMenuAccessibilityLabel, disabled, disabledToolIds, showToolLockToggle, toolLocked, onToolLockedChange, density, accessibilityLabel, style, contentContainerStyle, overflowPanelStyle, toolButtonStyle, activeToolButtonStyle, toolLabelStyle, activeToolLabelStyle, renderToolIcon, renderToolLockIcon, renderOverflowIcon, renderOverflowChevronIcon, renderToolButton, }: NativeVectorToolbarProps): react_jsx_runtime.JSX.Element;
|
|
191
191
|
|
|
192
|
+
type NativeRemotePresenceHitTarget = "cursor" | "label";
|
|
193
|
+
type NativeRemotePresenceHit = {
|
|
194
|
+
readonly peer: RemotePresencePeer;
|
|
195
|
+
readonly followId: string;
|
|
196
|
+
readonly target: NativeRemotePresenceHitTarget;
|
|
197
|
+
readonly screenX: number;
|
|
198
|
+
readonly screenY: number;
|
|
199
|
+
readonly worldX: number;
|
|
200
|
+
readonly worldY: number;
|
|
201
|
+
readonly clientId?: string;
|
|
202
|
+
readonly peerId?: string;
|
|
203
|
+
};
|
|
204
|
+
|
|
192
205
|
type NativeCustomShapePlacementOptions = {
|
|
193
206
|
readonly toolId: string;
|
|
194
207
|
readonly createItem: (args: {
|
|
@@ -215,6 +228,15 @@ type NativeWorldPointerDownDetail = {
|
|
|
215
228
|
readonly screenX: number;
|
|
216
229
|
readonly screenY: number;
|
|
217
230
|
};
|
|
231
|
+
/**
|
|
232
|
+
* Detail emitted when a native pointer/touch begins on a remote realtime
|
|
233
|
+
* participant label or cursor.
|
|
234
|
+
*
|
|
235
|
+
* Pass `followId` into your `followedPeerId` state for
|
|
236
|
+
* `useRealtimePeerFollow(...)`. For realtime-session peers this is the
|
|
237
|
+
* connection `clientId`, matching the session peer `id`.
|
|
238
|
+
*/
|
|
239
|
+
type NativeRemotePresencePressDetail = NativeRemotePresenceHit;
|
|
218
240
|
/**
|
|
219
241
|
* Optional override for a link item inserted from the native link tool.
|
|
220
242
|
*
|
|
@@ -287,6 +309,7 @@ type NativeVectorViewportProps = {
|
|
|
287
309
|
readonly onLinkToolRequest?: (detail: NativeLinkToolRequestDetail) => void;
|
|
288
310
|
readonly linkToolDialogLabels?: NativeLinkToolDialogLabels;
|
|
289
311
|
readonly onWorldPointerDown?: (detail: NativeWorldPointerDownDetail) => void;
|
|
312
|
+
readonly onRemotePresencePress?: (detail: NativeRemotePresencePressDetail) => void;
|
|
290
313
|
readonly onWorldPointerMove?: (world: {
|
|
291
314
|
readonly x: number;
|
|
292
315
|
readonly y: number;
|
|
@@ -434,4 +457,4 @@ type SvgNode = SvgRectNode | SvgEllipseNode | SvgCircleNode | SvgLineNode | SvgP
|
|
|
434
457
|
*/
|
|
435
458
|
declare function parseSvgFragment(xml: string): SvgNode[];
|
|
436
459
|
|
|
437
|
-
export { CanvuLinkData, DEFAULT_NATIVE_OVERFLOW_TOOL_IDS, DEFAULT_NATIVE_VECTOR_TOOLS, NATIVE_STYLE_PALETTE, type NativeCustomShapePlacementOptions, NativeInteractionOverlay, type NativeInteractionOverlayProps, type NativeLinkToolDialogLabels, type NativeLinkToolInsertOptions, type NativeLinkToolRequestDetail, NativeSceneRenderer, type NativeSceneRendererProps, NativeShapeRenderer, type NativeShapeRendererProps, type NativeStyleColor, NativeVectorStyleInspector, type NativeVectorStyleInspectorProps, type NativeVectorStyleToolId, type NativeVectorToolDefinition, NativeVectorToolbar, type NativeVectorToolbarDensity, type NativeVectorToolbarProps, type NativeVectorToolbarRenderOverflowInput, type NativeVectorToolbarRenderToolInput, type NativeVectorToolbarRenderToolLockInput, NativeVectorViewport, type NativeVectorViewportHandle, type NativeVectorViewportProps, type NativeWorldPointerDownDetail, type PlacementPreview, type SvgNode, VectorSceneItem, nativeStyleColorWithOpacity, normalizeNativeStyleHex, parseSvgFragment };
|
|
460
|
+
export { CanvuLinkData, DEFAULT_NATIVE_OVERFLOW_TOOL_IDS, DEFAULT_NATIVE_VECTOR_TOOLS, NATIVE_STYLE_PALETTE, type NativeCustomShapePlacementOptions, NativeInteractionOverlay, type NativeInteractionOverlayProps, type NativeLinkToolDialogLabels, type NativeLinkToolInsertOptions, type NativeLinkToolRequestDetail, type NativeRemotePresencePressDetail, NativeSceneRenderer, type NativeSceneRendererProps, NativeShapeRenderer, type NativeShapeRendererProps, type NativeStyleColor, NativeVectorStyleInspector, type NativeVectorStyleInspectorProps, type NativeVectorStyleToolId, type NativeVectorToolDefinition, NativeVectorToolbar, type NativeVectorToolbarDensity, type NativeVectorToolbarProps, type NativeVectorToolbarRenderOverflowInput, type NativeVectorToolbarRenderToolInput, type NativeVectorToolbarRenderToolLockInput, NativeVectorViewport, type NativeVectorViewportHandle, type NativeVectorViewportProps, type NativeWorldPointerDownDetail, type PlacementPreview, type SvgNode, VectorSceneItem, nativeStyleColorWithOpacity, normalizeNativeStyleHex, parseSvgFragment };
|
package/dist/native.js
CHANGED
|
@@ -4672,6 +4672,84 @@ function resizeItemByHandle(item, start, handle, currentWorld) {
|
|
|
4672
4672
|
}
|
|
4673
4673
|
return { ...item, x: nb.x, y: nb.y, bounds: nb };
|
|
4674
4674
|
}
|
|
4675
|
+
|
|
4676
|
+
// src/native/native-remote-presence-hit-test.ts
|
|
4677
|
+
var REMOTE_CURSOR_SCREEN_PX2 = 22;
|
|
4678
|
+
var REMOTE_LABEL_SCREEN_PX2 = 12;
|
|
4679
|
+
var REMOTE_LABEL_OFFSET_X = 14;
|
|
4680
|
+
var REMOTE_LABEL_BASELINE_OFFSET_Y = 18;
|
|
4681
|
+
var REMOTE_LABEL_AVERAGE_CHAR_PX = 7;
|
|
4682
|
+
var REMOTE_LABEL_MAX_WIDTH_PX = 180;
|
|
4683
|
+
var REMOTE_LABEL_MIN_HIT_WIDTH_PX = 44;
|
|
4684
|
+
var REMOTE_LABEL_HIT_PADDING_X = 10;
|
|
4685
|
+
var REMOTE_LABEL_HIT_PADDING_Y = 10;
|
|
4686
|
+
var REMOTE_CURSOR_MIN_HIT_SIZE_PX = 36;
|
|
4687
|
+
function pointInScreenRect(point, rect) {
|
|
4688
|
+
return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
|
|
4689
|
+
}
|
|
4690
|
+
function displayLabelForPeer(peer) {
|
|
4691
|
+
const displayName = peer.displayName?.trim();
|
|
4692
|
+
if (displayName) return displayName;
|
|
4693
|
+
return null;
|
|
4694
|
+
}
|
|
4695
|
+
function followIdForPeer(peer) {
|
|
4696
|
+
return peer.clientId ?? peer.peerId ?? peer.id;
|
|
4697
|
+
}
|
|
4698
|
+
function estimateLabelWidth(label) {
|
|
4699
|
+
return Math.max(
|
|
4700
|
+
REMOTE_LABEL_MIN_HIT_WIDTH_PX,
|
|
4701
|
+
Math.min(REMOTE_LABEL_MAX_WIDTH_PX, label.length * REMOTE_LABEL_AVERAGE_CHAR_PX)
|
|
4702
|
+
);
|
|
4703
|
+
}
|
|
4704
|
+
function buildHit(peer, camera, point, target) {
|
|
4705
|
+
const world = camera.screenToWorld(point.x, point.y);
|
|
4706
|
+
return {
|
|
4707
|
+
peer,
|
|
4708
|
+
followId: followIdForPeer(peer),
|
|
4709
|
+
target,
|
|
4710
|
+
screenX: point.x,
|
|
4711
|
+
screenY: point.y,
|
|
4712
|
+
worldX: world.worldX,
|
|
4713
|
+
worldY: world.worldY,
|
|
4714
|
+
...peer.clientId ? { clientId: peer.clientId } : {},
|
|
4715
|
+
...peer.peerId ? { peerId: peer.peerId } : {}
|
|
4716
|
+
};
|
|
4717
|
+
}
|
|
4718
|
+
function hitTestNativeRemotePresence(peers, camera, point) {
|
|
4719
|
+
for (let index = peers.length - 1; index >= 0; index -= 1) {
|
|
4720
|
+
const peer = peers[index];
|
|
4721
|
+
if (!peer || peer.isSelf || !peer.cursor) continue;
|
|
4722
|
+
const cursorScreen = camera.worldToScreen(peer.cursor.x, peer.cursor.y);
|
|
4723
|
+
const label = displayLabelForPeer(peer);
|
|
4724
|
+
if (label) {
|
|
4725
|
+
const labelX = cursorScreen.screenX + REMOTE_LABEL_OFFSET_X;
|
|
4726
|
+
const labelBaselineY = cursorScreen.screenY + REMOTE_LABEL_BASELINE_OFFSET_Y;
|
|
4727
|
+
const labelRect = {
|
|
4728
|
+
x: labelX - REMOTE_LABEL_HIT_PADDING_X,
|
|
4729
|
+
y: labelBaselineY - REMOTE_LABEL_SCREEN_PX2 - REMOTE_LABEL_HIT_PADDING_Y,
|
|
4730
|
+
width: estimateLabelWidth(label) + REMOTE_LABEL_HIT_PADDING_X * 2,
|
|
4731
|
+
height: REMOTE_LABEL_SCREEN_PX2 + REMOTE_LABEL_HIT_PADDING_Y * 2
|
|
4732
|
+
};
|
|
4733
|
+
if (pointInScreenRect(point, labelRect)) {
|
|
4734
|
+
return buildHit(peer, camera, point, "label");
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
const cursorHitSize = Math.max(
|
|
4738
|
+
REMOTE_CURSOR_MIN_HIT_SIZE_PX,
|
|
4739
|
+
REMOTE_CURSOR_SCREEN_PX2
|
|
4740
|
+
);
|
|
4741
|
+
const cursorRect = {
|
|
4742
|
+
x: cursorScreen.screenX - (cursorHitSize - REMOTE_CURSOR_SCREEN_PX2) / 2,
|
|
4743
|
+
y: cursorScreen.screenY - (cursorHitSize - REMOTE_CURSOR_SCREEN_PX2) / 2,
|
|
4744
|
+
width: cursorHitSize,
|
|
4745
|
+
height: cursorHitSize
|
|
4746
|
+
};
|
|
4747
|
+
if (pointInScreenRect(point, cursorRect)) {
|
|
4748
|
+
return buildHit(peer, camera, point, "cursor");
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
return null;
|
|
4752
|
+
}
|
|
4675
4753
|
var DEFAULT_NATIVE_LINK_TOOL_DIALOG_LABELS = {
|
|
4676
4754
|
title: "Add link",
|
|
4677
4755
|
description: "Paste the link you want to add to the board.",
|
|
@@ -4689,6 +4767,8 @@ var MARKER_TOOL_STYLE = {
|
|
|
4689
4767
|
};
|
|
4690
4768
|
var NATIVE_VIEWPORT_OVERLAY_Z_INDEX = 40;
|
|
4691
4769
|
var NATIVE_VIEWPORT_OVERLAY_ELEVATION = 40;
|
|
4770
|
+
var NATIVE_VIEWPORT_EXTERNAL_OVERLAY_Z_INDEX = 20;
|
|
4771
|
+
var NATIVE_VIEWPORT_EXTERNAL_OVERLAY_ELEVATION = 20;
|
|
4692
4772
|
function isPlacementTool(toolId) {
|
|
4693
4773
|
return toolId === "rect" || toolId === "ellipse" || toolId === "architectural-cloud" || toolId === "line" || toolId === "arrow";
|
|
4694
4774
|
}
|
|
@@ -4766,6 +4846,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
4766
4846
|
onLinkToolRequest,
|
|
4767
4847
|
linkToolDialogLabels,
|
|
4768
4848
|
onWorldPointerDown,
|
|
4849
|
+
onRemotePresencePress,
|
|
4769
4850
|
onWorldPointerMove,
|
|
4770
4851
|
onWorldPointerLeave,
|
|
4771
4852
|
onPlacementPreviewChange,
|
|
@@ -4789,6 +4870,8 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
4789
4870
|
onLinkToolRequestRef.current = onLinkToolRequest;
|
|
4790
4871
|
const onWorldPointerDownRef = useRef(onWorldPointerDown);
|
|
4791
4872
|
onWorldPointerDownRef.current = onWorldPointerDown;
|
|
4873
|
+
const onRemotePresencePressRef = useRef(onRemotePresencePress);
|
|
4874
|
+
onRemotePresencePressRef.current = onRemotePresencePress;
|
|
4792
4875
|
const onWorldPointerMoveRef = useRef(onWorldPointerMove);
|
|
4793
4876
|
onWorldPointerMoveRef.current = onWorldPointerMove;
|
|
4794
4877
|
const onWorldPointerLeaveRef = useRef(onWorldPointerLeave);
|
|
@@ -4809,6 +4892,8 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
4809
4892
|
itemsRef.current = items;
|
|
4810
4893
|
const selectedIdsRef = useRef(selectedIds);
|
|
4811
4894
|
selectedIdsRef.current = selectedIds;
|
|
4895
|
+
const remotePresenceRef = useRef(remotePresence);
|
|
4896
|
+
remotePresenceRef.current = remotePresence;
|
|
4812
4897
|
const dragStateRef = useRef({ kind: "idle" });
|
|
4813
4898
|
const [placementPreview, setPlacementPreviewState] = useState(null);
|
|
4814
4899
|
const setRealtimePlacementPreview = useCallback(
|
|
@@ -4968,13 +5053,19 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
4968
5053
|
const sx = point.x;
|
|
4969
5054
|
const sy = point.y;
|
|
4970
5055
|
updateToolCursorPoint(point);
|
|
5056
|
+
const cam = cameraRef.current;
|
|
5057
|
+
if (!cam) return;
|
|
5058
|
+
const remotePresenceHit = interactive && onRemotePresencePressRef.current ? hitTestNativeRemotePresence(remotePresenceRef.current, cam, point) : null;
|
|
5059
|
+
if (remotePresenceHit) {
|
|
5060
|
+
dragStateRef.current = { kind: "idle" };
|
|
5061
|
+
onRemotePresencePressRef.current?.(remotePresenceHit);
|
|
5062
|
+
return;
|
|
5063
|
+
}
|
|
4971
5064
|
if (!interactive) {
|
|
4972
5065
|
dragStateRef.current = { kind: "pan" };
|
|
4973
5066
|
return;
|
|
4974
5067
|
}
|
|
4975
5068
|
const tool = toolIdRef.current;
|
|
4976
|
-
const cam = cameraRef.current;
|
|
4977
|
-
if (!cam) return;
|
|
4978
5069
|
const { worldX, worldY } = screenToWorld(sx, sy);
|
|
4979
5070
|
onWorldPointerMoveRef.current?.({ x: worldX, y: worldY });
|
|
4980
5071
|
if (tool === "hand") {
|
|
@@ -5750,7 +5841,20 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
5750
5841
|
]
|
|
5751
5842
|
}
|
|
5752
5843
|
),
|
|
5753
|
-
overlay ? /* @__PURE__ */ jsx(
|
|
5844
|
+
overlay ? /* @__PURE__ */ jsx(
|
|
5845
|
+
View,
|
|
5846
|
+
{
|
|
5847
|
+
pointerEvents: "box-none",
|
|
5848
|
+
style: [
|
|
5849
|
+
StyleSheet.absoluteFill,
|
|
5850
|
+
{
|
|
5851
|
+
zIndex: NATIVE_VIEWPORT_EXTERNAL_OVERLAY_Z_INDEX,
|
|
5852
|
+
elevation: NATIVE_VIEWPORT_EXTERNAL_OVERLAY_ELEVATION
|
|
5853
|
+
}
|
|
5854
|
+
],
|
|
5855
|
+
children: overlay
|
|
5856
|
+
}
|
|
5857
|
+
) : null,
|
|
5754
5858
|
interactive && showStyleInspector && activeStyleToolId ? /* @__PURE__ */ jsx(
|
|
5755
5859
|
View,
|
|
5756
5860
|
{
|