@swan-io/lake 8.5.1 → 8.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swan-io/lake",
3
- "version": "8.5.1",
3
+ "version": "8.6.0",
4
4
  "engines": {
5
5
  "node": ">=20.9.0",
6
6
  "yarn": "^1.22.0"
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useState } from "react";
3
- import { Clipboard, StyleSheet, View } from "react-native";
3
+ import { StyleSheet, View } from "react-native";
4
4
  import { match } from "ts-pattern";
5
5
  import { visuallyHiddenStyle } from "../constants/commonStyles";
6
6
  import { colors, spacings } from "../constants/design";
7
+ import { setClipboardText } from "../utils/clipboard";
7
8
  import { isNotNullish, isNullish } from "../utils/nullish";
8
9
  import { Box } from "./Box";
9
10
  import { Icon } from "./Icon";
@@ -122,7 +123,7 @@ export const CopyableRegularTextCell = ({ variant = "regular", text, textToCopy,
122
123
  const clipboardText = textToCopy !== null && textToCopy !== void 0 ? textToCopy : text;
123
124
  const onPress = useCallback((event) => {
124
125
  event.preventDefault();
125
- Clipboard.setString(clipboardText);
126
+ setClipboardText(clipboardText);
126
127
  setVisibleState("copied");
127
128
  }, [clipboardText]);
128
129
  return (_jsxs(View, { style: styles.cell, children: [_jsx(LakeTooltip, { placement: "right", onHide: () => setVisibleState("copy"), togglableOnFocus: true, content: visibleState === "copy" ? copyWording : copiedWording, containerStyle: styles.iconContainer, children: _jsx(Pressable, { role: "button", "aria-label": copyWording, onPress: onPress, style: ({ hovered }) => [styles.icon, hovered && styles.underline], children: ({ hovered }) => (_jsx(Icon, { name: hovered ? "copy-filled" : "copy-regular", color: "currentColor", size: 14 })) }) }), _jsx(Space, { width: 4 }), _jsx(LakeText, { tooltip: tooltip, color: colors.gray[900], style: styles.regularText, variant: variant, children: text })] }));
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
- import { Clipboard } from "react-native";
3
+ import { setClipboardText } from "../utils/clipboard";
4
4
  import { Icon } from "./Icon";
5
5
  import { LakeTooltip } from "./LakeTooltip";
6
6
  import { Pressable } from "./Pressable";
@@ -10,7 +10,7 @@ export const LakeCopyButton = ({ color, copiedText, copyText, size = copyButtond
10
10
  return (_jsx(LakeTooltip, { describedBy: "copy", onHide: () => setVisibleState("copy"), togglableOnFocus: true, content: visibleState === "copy" ? copyText : copiedText, children: _jsx(Pressable, { onPress: event => {
11
11
  event.stopPropagation();
12
12
  event.preventDefault();
13
- Clipboard.setString(valueToCopy);
13
+ setClipboardText(valueToCopy);
14
14
  setVisibleState("copied");
15
15
  }, children: _jsx(Icon, { color: color, size: size, name: "copy-regular" }) }) }));
16
16
  };
@@ -9,6 +9,7 @@ type Props<T extends string> = {
9
9
  selected: T;
10
10
  items: ReadonlyArray<Item<T>>;
11
11
  onValueChange: (value: T) => void;
12
+ minItemWidth?: number;
12
13
  };
13
- export declare const SegmentedControl: <T extends string>({ selected, items, onValueChange, }: Props<T>) => import("react/jsx-runtime").JSX.Element;
14
+ export declare const SegmentedControl: <T extends string>({ selected, items, onValueChange, minItemWidth, }: Props<T>) => import("react/jsx-runtime").JSX.Element;
14
15
  export {};
@@ -80,11 +80,11 @@ const styles = StyleSheet.create({
80
80
  bottom: 0,
81
81
  },
82
82
  });
83
- export const SegmentedControl = ({ selected, items, onValueChange, }) => {
83
+ export const SegmentedControl = ({ selected, items, onValueChange, minItemWidth = 250, }) => {
84
84
  const selectedItemIndex = items.findIndex(item => item.id === selected);
85
- const selectedItem = items.find(item => item.id === selected);
85
+ const selectedItem = items[selectedItemIndex];
86
86
  const [pressed, setPressed] = useState(false);
87
- return (_jsx(ResponsiveContainer, { style: styles.container, children: ({ small }) => small ? (_jsxs(Box, { direction: "row", alignItems: "center", justifyContent: "spaceBetween", children: [_jsxs(Pressable, { style: styles.itemMobile, onPress: () => {
87
+ return (_jsx(ResponsiveContainer, { breakpoint: items.length * minItemWidth, style: styles.container, children: ({ small }) => small ? (_jsxs(Box, { direction: "row", alignItems: "center", justifyContent: "spaceBetween", children: [_jsxs(Pressable, { style: styles.itemMobile, onPress: () => {
88
88
  setPressed(true);
89
89
  }, children: [isNotNullish(selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.icon) &&
90
90
  match(selectedItem)
@@ -101,7 +101,7 @@ export const SegmentedControl = ({ selected, items, onValueChange, }) => {
101
101
  match(item)
102
102
  .with({ icon: P.nonNullable, activeIcon: P.nonNullable }, () => (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id) === item.id ? selectedItem.activeIcon : item.icon)
103
103
  .with({ icon: P.nonNullable }, () => item.icon)
104
- .otherwise(() => null), _jsx(Space, { height: 8, width: 12 }), _jsx(LakeText, { color: colors.gray[900], numberOfLines: 1, variant: "regular", style: styles.itemText, children: item.name })] }, item.id), (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id) === item.id && (_jsx(Box, { justifyContent: "center", style: { paddingHorizontal: spacings[24], backgroundColor: colors.gray[50] }, children: _jsx(Icon, { size: 16, name: "lake-check", color: colors.positive[500] }) }))] }))) }), _jsx(LakeButton, { mode: "tertiary", style: styles.button, size: "small", icon: "more-horizontal-filled", onPress: () => setPressed(true), ariaLabel: "Previous" })] })) : (_jsxs(Box, { direction: "row", children: [_jsx(View, { role: "none", style: [
104
+ .otherwise(() => null), _jsx(Space, { height: 8, width: 12 }), _jsx(LakeText, { color: colors.gray[900], numberOfLines: 1, variant: "regular", style: styles.itemText, children: item.name })] }), (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id) === item.id && (_jsx(Box, { justifyContent: "center", style: { paddingHorizontal: spacings[24], backgroundColor: colors.gray[50] }, children: _jsx(Icon, { size: 16, name: "lake-check", color: colors.positive[500] }) }))] }, item.id))) }), _jsx(Space, { width: 4 }), _jsx(LakeButton, { mode: "tertiary", style: styles.button, size: "small", icon: "more-horizontal-filled", onPress: () => setPressed(true), ariaLabel: "Previous" })] })) : (_jsxs(Box, { direction: "row", children: [_jsx(View, { role: "none", style: [
105
105
  styles.selectedItemIndicator,
106
106
  styles.selectedItemDesktop,
107
107
  {
@@ -2,10 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { Array, Option } from "@swan-io/boxed";
3
3
  import { t } from "@swan-io/shared-business/src/utils/i18n";
4
4
  import { memo, useEffect, useRef, useState } from "react";
5
- import { Clipboard, StyleSheet, View } from "react-native";
5
+ import { StyleSheet, View } from "react-native";
6
6
  import { P, match } from "ts-pattern";
7
7
  import { animations, colors, shadows } from "../constants/design";
8
8
  import { getErrorToRequestId, hideToast, useToasts, } from "../state/toasts";
9
+ import { setClipboardText } from "../utils/clipboard";
9
10
  import { isNotNullishOrEmpty, isNullish } from "../utils/nullish";
10
11
  import { Box } from "./Box";
11
12
  import { Icon } from "./Icon";
@@ -115,7 +116,7 @@ const Toast = memo(({ variant, uid, title, description, error, progress, onClose
115
116
  : t("copyButton.copiedTooltip"), children: _jsxs(Pressable, { style: styles.copyButton, onPress: event => {
116
117
  event.stopPropagation();
117
118
  event.preventDefault();
118
- Clipboard.setString(requestId !== null && requestId !== void 0 ? requestId : "");
119
+ setClipboardText(requestId !== null && requestId !== void 0 ? requestId : "");
119
120
  setVisibleState("copied");
120
121
  }, children: [_jsx(Icon, { color: colors.gray[700], size: 14, name: "copy-regular" }), _jsx(Space, { width: 4 }), _jsxs(LakeText, { numberOfLines: 1, variant: "smallRegular", color: colors.gray[700], children: ["ID: ", requestId] })] }) })] })))
121
122
  .exhaustive()] }), _jsx(Pressable, { onPress: () => onClose(uid), style: styles.closeButton, children: _jsx(Icon, { name: "lake-close", size: 24, color: colors.gray[500] }) }), progress != null && (_jsxs(_Fragment, { children: [_jsx(Space, { height: 24 }), _jsx(View, { ref: progressBarRef, role: "progressbar", style: [styles.progressBar, { backgroundColor: colors[colorVariation][500] }] })] }))] }) }));
@@ -1,13 +1,9 @@
1
- // Based on https://github.com/necolas/react-native-web/blob/0.19.10/packages/react-native-web/src/exports/useWindowDimensions/index.js
1
+ // Based on https://github.com/necolas/react-native-web/blob/0.19.12/packages/react-native-web/src/exports/useWindowDimensions/index.js
2
2
  import { useCallback, useSyncExternalStore } from "react";
3
- import { Dimensions } from "react-native";
3
+ import { windowSize } from "../utils/windowSize";
4
4
  export const useBreakpoint = (breakpoint) => useSyncExternalStore(useCallback(onStoreChange => {
5
- const subscription = Dimensions.addEventListener("change", ({ window }) => {
6
- if (window != null) {
7
- onStoreChange();
8
- }
9
- });
10
- return subscription.remove;
5
+ const unsubscribe = windowSize.subscribe(onStoreChange);
6
+ return unsubscribe;
11
7
  }, []), useCallback(() => {
12
- return Dimensions.get("window").width >= breakpoint;
8
+ return windowSize.get().width >= breakpoint;
13
9
  }, [breakpoint]));
@@ -0,0 +1 @@
1
+ export declare const setClipboardText: (text: string) => boolean;
@@ -0,0 +1,30 @@
1
+ // Based on https://github.com/necolas/react-native-web/blob/0.19.12/packages/react-native-web/src/exports/Clipboard/index.js
2
+ export const setClipboardText = (text) => {
3
+ // add the text to a hidden node
4
+ const node = document.createElement("span");
5
+ node.textContent = text;
6
+ node.style.opacity = "0";
7
+ node.style.position = "absolute";
8
+ node.style.userSelect = "auto";
9
+ node.style.whiteSpace = "pre-wrap";
10
+ document.body.appendChild(node);
11
+ // select the text
12
+ const selection = window.getSelection();
13
+ if (selection == null) {
14
+ return false;
15
+ }
16
+ selection.removeAllRanges();
17
+ const range = document.createRange();
18
+ range.selectNodeContents(node);
19
+ selection.addRange(range);
20
+ try {
21
+ document.execCommand("copy");
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ // remove selection and node
27
+ selection.removeAllRanges();
28
+ document.body.removeChild(node);
29
+ return true;
30
+ };
@@ -0,0 +1,8 @@
1
+ export type WindowSize = {
2
+ width: number;
3
+ height: number;
4
+ };
5
+ export declare const windowSize: {
6
+ get: () => WindowSize;
7
+ subscribe: (callback: (size: WindowSize) => void) => () => void;
8
+ };
@@ -0,0 +1,47 @@
1
+ // Based on https://github.com/necolas/react-native-web/blob/0.19.12/packages/react-native-web/src/exports/Dimensions/index.js
2
+ const get = () => {
3
+ const viewport = window.visualViewport;
4
+ /**
5
+ * iOS does not update viewport dimensions on keyboard open/close.
6
+ * window.visualViewport(https://developer.mozilla.org/en-US/docs/Web/API/VisualViewport)
7
+ * is used instead of document.documentElement.clientHeight (which remains as a fallback)
8
+ */
9
+ if (viewport != null) {
10
+ /**
11
+ * We are multiplying by scale because height and width from visual viewport
12
+ * also react to pinch zoom, and become smaller when zoomed. But it is not desired
13
+ * behaviour, since originally documentElement client height and width were used,
14
+ * and they do not react to pinch zoom.
15
+ */
16
+ return {
17
+ width: Math.round(viewport.width * viewport.scale),
18
+ height: Math.round(viewport.height * viewport.scale),
19
+ };
20
+ }
21
+ const documentElement = window.document.documentElement;
22
+ return {
23
+ width: documentElement.clientWidth,
24
+ height: documentElement.clientHeight,
25
+ };
26
+ };
27
+ const callbacks = new Set();
28
+ let currentSize = get();
29
+ const onResize = () => {
30
+ currentSize = get();
31
+ callbacks.forEach(callback => callback(currentSize));
32
+ };
33
+ export const windowSize = {
34
+ get: () => currentSize,
35
+ subscribe: (callback) => {
36
+ callbacks.add(callback);
37
+ return () => {
38
+ callbacks.delete(callback);
39
+ };
40
+ },
41
+ };
42
+ if (window.visualViewport != null) {
43
+ window.visualViewport.addEventListener("resize", onResize, false);
44
+ }
45
+ else {
46
+ window.addEventListener("resize", onResize, false);
47
+ }