@kietpt2003/react-native-core-ui
Version:
React Native Core UI components by KietPT
1,509 lines (1,488 loc) • 67.8 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
GalleryBottomSheet: () => GalleryBottomSheet_default,
ScrollPercentage: () => ScrollPercentage_default,
SvgIcon: () => SvgIcon_default,
Text: () => Text_default,
useGalleryAssets: () => useGalleryAssets
});
module.exports = __toCommonJS(index_exports);
// src/Text/Text.tsx
var import_react = __toESM(require("react"));
var import_react_native3 = require("react-native");
// src/utils/resolutions.ts
var import_react_native = require("react-native");
var import_react_native_iphone_x_helper = require("react-native-iphone-x-helper");
var import_react_native_device_info = __toESM(require("react-native-device-info"));
var isTablet = import_react_native_device_info.default.isTablet();
var statusBarHeight = import_react_native.Platform.OS == "android" && import_react_native.StatusBar.currentHeight ? import_react_native.StatusBar.currentHeight : 0;
// src/utils/filters.ts
var ALLOWED_STYLE_KEYS = ["color", "fontFamily", "fontWeight"];
var filterAllowedTextStyle = (style) => {
const filtered = {};
for (const key of ALLOWED_STYLE_KEYS) {
if (key in style) {
filtered[key] = style[key];
}
}
return filtered;
};
// src/utils/scale.ts
var import_react_native2 = require("react-native");
var { width, height } = import_react_native2.Dimensions.get("window");
var IPHONE_12_WIDTH = 375;
var IPHONE_12_HEIGTH = 812;
var scaleWidth = width / IPHONE_12_WIDTH;
var scaleHeight = height / IPHONE_12_HEIGTH;
var scaleAvg = (scaleWidth + scaleHeight) / 2;
var scaleFont = (size) => Math.round(import_react_native2.PixelRatio.roundToNearestPixel(size * scaleAvg));
var scale = (size) => Math.round(import_react_native2.PixelRatio.roundToNearestPixel(size * scaleWidth));
// src/utils/limitedString.ts
var limitedString_default = (str = "", maxLength = 5) => {
let arr = str.split(" ");
if (arr.length > maxLength) {
return arr.slice(0, maxLength).join(" ") + "...";
}
return str;
};
// src/themes/colors.ts
var colors = {
// white
default: "#F1F1F1",
white: "#FFFFFF",
white_80: "rgba(255, 255, 255, 0.8)",
// black
black: "#000000",
black_262626: "#262626",
// transparent
transparent00: "rgba(0, 0, 0, 0)",
transparent10: "rgba(0, 0, 0, 0.1)",
transparent25: "rgba(0, 0, 0, 0.25)",
transparent30: "rgba(0, 0, 0, 0.3)",
transparent40: "rgba(0, 0, 0, 0.4)",
transparent50: "rgba(0, 0, 0, 0.5)",
transparent80: "rgba(0, 0, 0, 0.8)",
// red
red_FF6262: "#FF6262",
red_E00102: "#E00102",
red_E90000: "#E90000",
red_FF0303: "#FF0303",
red_EB4D3D: "#EB4D3D",
red_ED1C1C: "#ED1C1C",
red_F26122: "#F26122",
red_FF3F30: "#FF3F30",
red_FF4D4F: "#FF4D4F",
red_FF6E30: "#FF6E30",
red_F24822: "#F24822",
// green
green_32B14A: "#32B14A",
green_00A720: "#00A720",
green_42A046: "#42A046",
green_4CD964: "#4CD964",
green_8AAD0B: "#8AAD0B",
green_7FBA00: "#7FBA00",
green_3CB371: "#3CB371",
green_7BC143: "#7BC143",
green_387C2B: "#387C2B",
green_50A91A: "#50A91A",
green_2D880C: "#2D880C",
green_389E0D: "#389E0D",
green_42A013: "#42A013",
green_52C41A: "#52C41A",
green_279C2E: "#279C2E",
green_266431: "#266431",
green_32A702: "#32A702",
green_3C7700: "#3C7700",
green_3AA015: "#3AA015",
green_30C75E: "#30C75E",
green_81B214: "#81B214",
green_28C76F: "#28C76F",
green_6CC149: "#6CC149",
green_B2C8B8: "#B2C8B8",
green_6AD36E: "#6AD36E",
green_6ECA67: "#6ECA67",
green_419522: "#419522",
green_266432: "#266432",
green_386338: "#386338",
green_28663B: "#28663B",
green_266643: "#266643",
green_08674F: "#08674F",
green_206A5D: "#206A5D",
green_194527: "#194527",
green_1A4D2E: "#1A4D2E",
green_91C788: "#91C788",
green_D0F4C2: "#D0F4C2",
green_C7F9CC: "#C7F9CC",
green_CBFFDA: "#CBFFDA",
green_9AD7AC: "#9AD7AC",
green_A7E8B9: "#A7E8B9",
green_A0E8AD: "#A0E8AD",
green_92E3A973: "#92E3A973",
green_7BC1431A: "#7BC1431A",
green_0B5270: "#0B5270",
green_E0FEE8: "#E0FEE8",
green_EBF6E3: "#EBF6E3",
greenlightF8ECD2: "#F8ECD2",
green_gradient: ["#7BC143", "#387C2B"],
green_gradient_2: ["#42A046", "#42A046"],
// blue
blue: "#00B4F0",
blue_29ABE2: "#29ABE2",
blue_00A7E1: "#00A7E1",
blue_3677BC: "#3677BC",
blue_DDF7FF: "#DDF7FF",
blue_2B65B2: "#2B65B2",
blue_B2E5F6: "#B2E5F6",
blue_86D3EE: "#86D3EE",
blue_80D3F0: "#80D3F0",
blue_1890FF: "#1890FF",
// yellow
yellow_EDDE5D: "#EDDE5D",
yellow_FFCC29: "#FFCC29",
yellow_F0BD26: "#F0BD26",
yellow_F6BF32: "#F6BF32",
// orange
orange_EB996E: "#EB996E",
orange_F98B0A: "#F98B0A",
orange_F58634: "#F58634",
orange_FFA500: "#FFA500",
orangeFAAE4F: "#FAAE4F",
// grey
gray: "#6E6E6E",
gray_BCBCBC: "#BCBCBC",
gray_7E7E7E: "#7E7E7E",
gray_E5E0E4: "#E5E0E4",
gray_F1F1F1: "#F1F1F1",
gray_E7E3E3: "#E7E3E3",
gray_3C3C3C: "#3C3C3C",
gray_CCCCCC: "#CCCCCC",
gray_FAFAFA: "#FAFAFA",
gray_FDFDFD: "#FDFDFD",
gray_D9D9D9: "#D9D9D9",
gray_D9D9D980: "#D9D9D980",
gray_E6E6E6: "#E6E6E6",
gray_EBEBEB: "#EBEBEB",
gray_F5F5F5: "#F5F5F5",
gray_EEEEEE: "#EEEEEE",
gray_727272: "#727272",
gray_9CA3A5: "#9CA3A5",
gray_E5E5E5: "#E5E5E5",
gray_D1D2D4: "#D1D2D4",
gray_A0A0A0: "#A0A0A0",
gray_EAEAEA: "#EAEAEA",
gray_B3B3B3: "#B3B3B3",
gray_8A8A8A: "#8A8A8A",
gray_E4E4E4: "#E4E4E4",
gray_888888: "#888888",
gray_F4F4F4: "#F4F4F4",
gray_4F4F4F: "#4F4F4F",
gray_DDDDDD: "#DDDDDD",
gray_E1E5EA: "#E1E5EA",
//purple
purple_222C67: "#222C67",
purple_9370DB: "#9370DB",
//shadow
shadow: {
elevation: 4,
shadowColor: "#000",
shadowOpacity: 0.2,
shadowRadius: 3.84,
shadowOffset: {
width: 0,
height: -1
}
},
shadow_btn: {
elevation: 4,
shadowColor: "#000",
shadowOpacity: 0.25,
shadowRadius: 2,
shadowOffset: {
width: 0,
height: 4
}
},
shadow_order: {
elevation: 3,
shadowColor: "#000",
shadowOpacity: 0.15,
shadowRadius: 2.84,
shadowOffset: {
width: 0,
height: 1
}
},
shadow_btn_small: {
elevation: 3,
shadowColor: "#000",
shadowOpacity: 0.25,
shadowRadius: 2,
shadowOffset: {
width: 1,
height: 1
}
}
};
var colors_default = colors;
// src/themes/fontSize.ts
var fontSize_default = {
_8: scaleFont(8),
_10: scaleFont(10),
_11: scaleFont(11),
_12: scaleFont(12),
_13: scaleFont(13),
_14: scaleFont(14),
_16: scaleFont(16),
_18: scaleFont(18),
_20: scaleFont(20),
_22: scaleFont(22),
_24: scaleFont(24),
_28: scaleFont(28),
_30: scaleFont(30),
_36: scaleFont(36),
_48: scaleFont(48)
};
// src/Text/Text.tsx
var Text = ({
style,
color,
bold,
children,
load,
styleView,
...rest
}) => {
return /* @__PURE__ */ import_react.default.createElement(import_react_native3.View, { style: styleView }, load ? /* @__PURE__ */ import_react.default.createElement(import_react_native3.View, { style: styles.load }) : /* @__PURE__ */ import_react.default.createElement(
import_react_native3.Text,
{
allowFontScaling: false,
...rest,
style: [
{
color: color || colors_default.black,
...import_react_native3.Platform.select({
android: {
fontWeight: bold ? "700" : "400",
fontFamily: "Arial"
},
ios: {
fontFamily: bold ? "Arial Bold" : "Arial"
}
})
},
styles.text,
style
]
},
children
));
};
var styles = import_react_native3.StyleSheet.create({
text: {
fontSize: fontSize_default._14
},
load: {
backgroundColor: colors_default.gray_E1E5EA,
borderRadius: 8,
paddingVertical: scale(10)
}
});
var Text_default = Text;
// src/GalleryBottomSheet/GalleryBottomSheet.tsx
var import_react_native8 = require("react-native");
var import_react7 = __toESM(require("react"));
var import_react_native_reanimated2 = __toESM(require("react-native-reanimated"));
var import_react_native_gesture_handler2 = require("react-native-gesture-handler");
var import_react_native_safe_area_context = require("react-native-safe-area-context");
var import_AntDesign = __toESM(require("react-native-vector-icons/AntDesign"));
// src/hooks/useGalleryAssets.ts
var import_react2 = __toESM(require("react"));
var import_react_native4 = require("react-native");
var import_camera_roll = require("@react-native-camera-roll/camera-roll");
var import_react_native_permissions = require("react-native-permissions");
var numberAssetsToLoad = 15;
function useGalleryAssets(defaultAssetType = "All") {
const [assets, setAssets] = import_react2.default.useState({});
const [albums, setAlbums] = import_react2.default.useState([]);
const [pagination, setPagination] = import_react2.default.useState({});
const [fullAssets, setFullAssets] = import_react2.default.useState([]);
const [fullAssetsPagination, setFullAssetsPagination] = import_react2.default.useState(void 0);
const [totalAssets, setTotalAssets] = import_react2.default.useState(0);
const [loading, setLoading] = import_react2.default.useState(true);
const [error, setError] = import_react2.default.useState(null);
const [hasPermission, setHasPermission] = import_react2.default.useState(false);
const [assetType, setAssetType] = import_react2.default.useState(defaultAssetType);
const changeAssetType = (type) => {
setAlbums([]);
setAssets({});
setPagination({});
setFullAssets([]);
setFullAssetsPagination(void 0);
setTotalAssets(0);
if (hasPermission && type !== assetType) {
setAssetType(type);
loadFullAssets(numberAssetsToLoad, void 0, type, true);
loadAlbums(numberAssetsToLoad, type, true);
}
};
const checkPermission = async () => {
if (import_react_native4.Platform.OS === "android") {
if (import_react_native4.Platform.Version >= 33) {
const result = await Promise.all([
import_react_native4.PermissionsAndroid.check(import_react_native4.PermissionsAndroid.PERMISSIONS.READ_MEDIA_IMAGES),
import_react_native4.PermissionsAndroid.check(import_react_native4.PermissionsAndroid.PERMISSIONS.READ_MEDIA_VIDEO)
]);
const hasPermission2 = result[0] && result[1];
setHasPermission(hasPermission2);
return hasPermission2;
} else {
const result = await import_react_native4.PermissionsAndroid.check(import_react_native4.PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
setHasPermission(result);
return result;
}
} else {
const checkResult = await (0, import_react_native_permissions.check)(import_react_native_permissions.PERMISSIONS.IOS.PHOTO_LIBRARY);
if (checkResult === import_react_native_permissions.RESULTS.GRANTED) {
setHasPermission(true);
return true;
} else {
setHasPermission(false);
return false;
}
}
};
const requestPermission = async () => {
if (import_react_native4.Platform.OS === "android") {
if (import_react_native4.Platform.Version >= 33) {
const requestResults = await import_react_native4.PermissionsAndroid.requestMultiple([
import_react_native4.PermissionsAndroid.PERMISSIONS.READ_MEDIA_IMAGES,
import_react_native4.PermissionsAndroid.PERMISSIONS.READ_MEDIA_VIDEO
]);
const result = requestResults[import_react_native4.PermissionsAndroid.PERMISSIONS.READ_MEDIA_IMAGES] === import_react_native4.PermissionsAndroid.RESULTS.GRANTED && requestResults[import_react_native4.PermissionsAndroid.PERMISSIONS.READ_MEDIA_VIDEO] === import_react_native4.PermissionsAndroid.RESULTS.GRANTED;
setHasPermission(result);
return result;
} else {
const requestResults = await import_react_native4.PermissionsAndroid.request(import_react_native4.PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
const result = requestResults === import_react_native4.PermissionsAndroid.RESULTS.GRANTED;
setHasPermission(result);
return result;
}
} else {
const requestResult = await (0, import_react_native_permissions.request)(import_react_native_permissions.PERMISSIONS.IOS.PHOTO_LIBRARY);
const result = requestResult === import_react_native_permissions.RESULTS.GRANTED;
setHasPermission(result);
return result;
}
};
const loadFullAssets = async (numberAssetsToLoad2, after, type = "All", isReset = false) => {
setLoading(true);
if (hasPermission) {
const res = await import_camera_roll.CameraRoll.getPhotos({
first: numberAssetsToLoad2,
assetType: type,
after
});
if (isReset) {
setFullAssets(res.edges);
} else {
const existingAssets = new Set(fullAssets.map((p) => p.node.image.uri));
const uniqueNewAssets = res.edges.filter(
(p) => !existingAssets.has(p.node.image.uri)
);
const newAssets = fullAssets.concat(uniqueNewAssets);
setFullAssets(newAssets);
}
setFullAssetsPagination({
hasNextPage: res.page_info.has_next_page,
endCursor: res.page_info.end_cursor
});
setLoading(false);
return res.page_info.has_next_page;
} else {
setAlbums([]);
setAssets({});
setPagination({});
setFullAssets([]);
setFullAssetsPagination(void 0);
setLoading(false);
return false;
}
};
const loadAlbums = async (numberAssetsToLoad2, type = "All", isReset = false) => {
try {
setLoading(true);
if (!hasPermission) {
setAlbums([]);
setAssets({});
setPagination({});
setFullAssets([]);
setFullAssetsPagination(void 0);
setLoading(false);
return;
}
const albumList = await import_camera_roll.CameraRoll.getAlbums({ assetType: type });
if (albumList.length > 0) {
let total = 0;
albumList.forEach(async (album) => {
total += album.count;
await loadAssets(album, numberAssetsToLoad2, isReset ? void 0 : pagination[album?.id ?? album.title]?.endCursor, type);
});
setTotalAssets(total);
}
setAlbums(albumList);
} catch (err) {
setError("Failed to load albums");
console.error("Error loading albums:", err);
} finally {
setLoading(false);
}
};
const loadAssets = async (album, numberAssetsToLoad2, after, type = "All") => {
try {
setLoading(true);
if (!assets[album?.id ?? album.title]) assets[album?.id ?? album.title] = [];
if (hasPermission) {
const albumAssets = await import_camera_roll.CameraRoll.getPhotos({
first: numberAssetsToLoad2,
groupName: album.title,
assetType: type,
after
});
setAssets((prevAssets) => {
const existing = prevAssets[album?.id ?? album.title] || [];
const existingAssets = new Set(existing.map((a) => a.node.image.uri));
const uniqueNewAssets = albumAssets.edges.filter(
(a) => !existingAssets.has(a.node.image.uri)
);
return {
...prevAssets,
[album?.id ?? album.title]: [...existing, ...uniqueNewAssets]
};
});
setPagination((prevPagination) => ({
...prevPagination,
[album?.id ?? album.title]: {
hasNextPage: albumAssets.page_info.has_next_page,
endCursor: albumAssets.page_info.end_cursor
}
}));
setLoading(false);
return albumAssets.page_info.has_next_page;
} else {
setAlbums([]);
setAssets({});
setPagination({});
setFullAssets([]);
setFullAssetsPagination(void 0);
setLoading(false);
return false;
}
} catch (error2) {
setError("Failed to load assets from album");
console.error("Error loading assets from album:", error2);
setLoading(false);
return false;
}
};
import_react2.default.useEffect(() => {
if (hasPermission) {
loadFullAssets(numberAssetsToLoad, fullAssetsPagination?.endCursor, assetType);
loadAlbums(numberAssetsToLoad, assetType);
}
}, [hasPermission]);
import_react2.default.useEffect(() => {
checkPermission();
}, []);
return {
albums,
assets,
loading,
error,
fullAssets,
totalAssets,
fullAssetsPagination,
loadFullAssets,
loadAssets,
pagination,
requestPermission,
checkPermission,
hasPermission,
changeAssetType
};
}
// src/GalleryBottomSheet/components/AlbumFilter.tsx
var import_react_native6 = require("react-native");
var import_react4 = __toESM(require("react"));
var import_react_native_gesture_handler = require("react-native-gesture-handler");
var import_react_native_reanimated = __toESM(require("react-native-reanimated"));
// src/GalleryBottomSheet/components/AlbumHeaderFilter.tsx
var import_react_native5 = require("react-native");
var import_react3 = __toESM(require("react"));
var AlbumHeaderFilter = ({
setSelectedAlbum,
handleCloseAlbumFilter,
fullAssets,
totalAssets,
headerTitle,
albumItemStyle
}) => {
return /* @__PURE__ */ import_react3.default.createElement(
import_react_native5.TouchableOpacity,
{
style: styles2.itemContentContainer,
onPress: () => {
handleCloseAlbumFilter();
setSelectedAlbum(null);
},
activeOpacity: 0.7
},
/* @__PURE__ */ import_react3.default.createElement(import_react_native5.Image, { source: { uri: fullAssets.at(0)?.node.image.uri }, style: styles2.albumFirstImage }),
/* @__PURE__ */ import_react3.default.createElement(import_react_native5.View, null, /* @__PURE__ */ import_react3.default.createElement(import_react_native5.Text, { style: [
styles2.albumTitlteTxt,
{ color: albumItemStyle?.titleColor ? albumItemStyle.titleColor : colors_default.black }
] }, headerTitle), /* @__PURE__ */ import_react3.default.createElement(import_react_native5.Text, { style: [
styles2.assetCountTxt,
{ color: albumItemStyle?.countColor ? albumItemStyle.countColor : colors_default.gray_A0A0A0 }
] }, totalAssets))
);
};
var styles2 = import_react_native5.StyleSheet.create({
itemContentContainer: {
flexDirection: "row",
gap: 10,
marginBottom: 10
},
albumTitlteTxt: {
fontSize: fontSize_default._16
},
assetCountTxt: {
fontSize: fontSize_default._14
},
albumFirstImage: {
width: (width - 20) / 7,
height: (width - 20) / 7
}
});
var AlbumHeaderFilter_default = AlbumHeaderFilter;
// src/GalleryBottomSheet/components/AlbumFilter.tsx
var AlbumFilter = import_react4.default.forwardRef(
({
setSelectedAlbum,
topAnimation,
isPanEnabled,
setIsPanEnabled,
enableScroll,
setEnableScroll,
isTop,
setIsTop,
openHeight,
setShowAlbumsList,
albums,
assets,
fullAssets,
totalAssets,
headerTitle,
headerBarColor,
backgroundColor,
albumItemStyle,
emptyGalleryMsg,
safeEmptyGalleryMsgStyle
}, ref) => {
const scrollY = (0, import_react_native_reanimated.useSharedValue)(0);
const context = (0, import_react_native_reanimated.useSharedValue)(0);
const timing = (0, import_react_native_reanimated.useSharedValue)(0);
const onScroll = (0, import_react_native_reanimated.useAnimatedScrollHandler)({
onScroll: (event) => {
scrollY.value = event.contentOffset.y;
}
});
const panScroll = import_react_native_gesture_handler.Gesture.Pan().onBegin(() => {
"worklet";
context.value = topAnimation.value;
timing.value = 0;
(0, import_react_native_reanimated.runOnJS)(setIsPanEnabled)(false);
}).onUpdate((event) => {
"worklet";
timing.value++;
if (event.velocityY > 0 && scrollY.value == 0 && isTop) {
(0, import_react_native_reanimated.runOnJS)(setIsPanEnabled)(true);
(0, import_react_native_reanimated.runOnJS)(setEnableScroll)(false);
topAnimation.value = (0, import_react_native_reanimated.withSpring)(context.value + event.translationY, {
damping: 100,
stiffness: 400
});
}
if (!enableScroll && isTop) {
topAnimation.value = (0, import_react_native_reanimated.withSpring)(context.value + event.translationY, {
damping: 100,
stiffness: 400
});
}
}).onEnd((event) => {
"worklet";
(0, import_react_native_reanimated.runOnJS)(setIsPanEnabled)(true);
(0, import_react_native_reanimated.runOnJS)(setEnableScroll)(true);
const speedRate = 1 / timing.value;
if (isPanEnabled && isTop) {
if (event.velocityY > 0 && speedRate > 0.07 && scrollY.value == 0) {
topAnimation.value = (0, import_react_native_reanimated.withSpring)(openHeight, {
damping: 100,
stiffness: 400
});
(0, import_react_native_reanimated.runOnJS)(setIsTop)(false);
} else if (event.velocityY > 0 && (topAnimation.value >= height / 2 - statusBarHeight && speedRate <= 0.11)) {
topAnimation.value = (0, import_react_native_reanimated.withSpring)(openHeight, {
damping: 100,
stiffness: 400
});
(0, import_react_native_reanimated.runOnJS)(setIsTop)(false);
} else {
topAnimation.value = (0, import_react_native_reanimated.withSpring)(statusBarHeight, {
damping: 100,
stiffness: 400
});
}
} else if (!isPanEnabled && enableScroll && isTop) {
topAnimation.value = (0, import_react_native_reanimated.withSpring)(statusBarHeight, {
damping: 100,
stiffness: 400
});
}
});
const scrollViewGesture = import_react_native_gesture_handler.Gesture.Native();
const translateY = import_react4.default.useRef(new import_react_native6.Animated.Value(height)).current;
const handleOpenAlbumFilter = () => {
import_react_native6.Animated.timing(translateY, {
toValue: 0,
// Dịch chuyển đến vị trí ban đầu
duration: 800,
// Thời gian chạy animation (ms)
useNativeDriver: true
// Sử dụng native driver để tăng hiệu suất
}).start();
setShowAlbumsList(true);
};
const handleCloseAlbumFilter = () => {
setIsPanEnabled(true);
import_react_native6.Animated.timing(translateY, {
toValue: height,
// Dịch chuyển đến vị trí ban đầu
duration: 800,
// Thời gian chạy animation (ms)
useNativeDriver: true
// Sử dụng native driver để tăng hiệu suất
}).start();
setShowAlbumsList(false);
};
import_react4.default.useImperativeHandle(
ref,
() => ({
handleOpenAlbumFilter,
handleCloseAlbumFilter
})
);
return /* @__PURE__ */ import_react4.default.createElement(
import_react_native6.Animated.View,
{
style: [
styles3.container,
{
transform: [{ translateY }],
backgroundColor
}
// Thêm animation dịch chuyển
]
},
/* @__PURE__ */ import_react4.default.createElement(import_react_native6.View, { style: [
styles3.handleContainer,
{
backgroundColor: headerBarColor
}
] }),
albums.length === 0 || fullAssets.length === 0 || totalAssets === 0 ? /* @__PURE__ */ import_react4.default.createElement(import_react_native6.Text, { style: [
styles3.noImage,
safeEmptyGalleryMsgStyle
] }, emptyGalleryMsg) : /* @__PURE__ */ import_react4.default.createElement(
import_react_native_gesture_handler.GestureDetector,
{
gesture: import_react_native_gesture_handler.Gesture.Simultaneous(panScroll, scrollViewGesture)
},
/* @__PURE__ */ import_react4.default.createElement(
import_react_native_reanimated.default.FlatList,
{
data: albums,
scrollEnabled: enableScroll,
keyExtractor: (_item, index) => index.toString(),
showsVerticalScrollIndicator: false,
renderItem: ({ item }) => {
return /* @__PURE__ */ import_react4.default.createElement(
import_react_native6.TouchableOpacity,
{
style: styles3.itemContentContainer,
onPress: () => {
handleCloseAlbumFilter();
setSelectedAlbum(item);
},
activeOpacity: 0.7
},
/* @__PURE__ */ import_react4.default.createElement(
import_react_native6.Image,
{
source: {
uri: assets[item?.id ?? item.title]?.[0]?.node.image.uri
},
style: styles3.albumFirstImage
}
),
/* @__PURE__ */ import_react4.default.createElement(import_react_native6.View, null, /* @__PURE__ */ import_react4.default.createElement(import_react_native6.Text, { style: [
styles3.albumTitlteTxt,
{ color: albumItemStyle?.titleColor ? albumItemStyle.titleColor : colors_default.black }
] }, item.title), /* @__PURE__ */ import_react4.default.createElement(import_react_native6.Text, { style: [
styles3.assetCountTxt,
{ color: albumItemStyle?.countColor ? albumItemStyle.countColor : colors_default.gray_A0A0A0 }
] }, item.count))
);
},
ListHeaderComponent: /* @__PURE__ */ import_react4.default.createElement(
AlbumHeaderFilter_default,
{
setSelectedAlbum,
handleCloseAlbumFilter,
fullAssets,
totalAssets,
headerTitle,
albumItemStyle
}
),
onScroll
}
)
)
);
}
);
var styles3 = import_react_native6.StyleSheet.create({
container: {
...import_react_native6.StyleSheet.absoluteFillObject,
paddingHorizontal: 10,
marginTop: 60
},
handleContainer: {
alignItems: "center",
borderTopLeftRadius: 15,
borderTopRightRadius: 15
},
contentContainer: {
flex: 1,
paddingHorizontal: 15,
paddingTop: 5,
paddingBottom: 15
},
selectedAlbumContainer: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
gap: 10
},
noImage: {
textAlign: "center",
fontSize: fontSize_default._18
},
itemContentContainer: {
flexDirection: "row",
gap: 10,
marginBottom: 10
},
albumTitlteTxt: {
fontSize: fontSize_default._16
},
assetCountTxt: {
fontSize: fontSize_default._14
},
albumFirstImage: {
width: (width - 20) / 7,
height: (width - 20) / 7
}
});
// src/GalleryBottomSheet/components/PlayCircle.tsx
var import_react_native_svg = __toESM(require("react-native-svg"));
var import_react5 = __toESM(require("react"));
var defaultStrokeWidth = scale(5);
var maxStrokeWidth = scale(8);
var PlayCircle = ({
size,
viewStyle,
circleStyle,
polygonStyle
}) => {
const circleStrokeWidth = typeof circleStyle?.strokeWidth === "number" && circleStyle.strokeWidth >= 0 && circleStyle.strokeWidth <= maxStrokeWidth ? circleStyle.strokeWidth : defaultStrokeWidth;
const circleRadius = 50 - circleStrokeWidth / 2;
const polygonStrokeWidth = typeof polygonStyle?.strokeWidth === "number" && polygonStyle.strokeWidth >= 0 && polygonStyle.strokeWidth <= maxStrokeWidth ? polygonStyle.strokeWidth : defaultStrokeWidth;
return /* @__PURE__ */ import_react5.default.createElement(
import_react_native_svg.default,
{
width: size,
height: size,
viewBox: "0 0 100 100",
style: viewStyle
},
/* @__PURE__ */ import_react5.default.createElement(
import_react_native_svg.Circle,
{
cx: "50",
cy: "50",
r: circleRadius,
fill: circleStyle?.fill ? circleStyle.fill : colors_default.transparent50,
stroke: circleStyle?.stroke ? circleStyle.stroke : colors_default.black,
strokeWidth: circleStrokeWidth
}
),
/* @__PURE__ */ import_react5.default.createElement(
import_react_native_svg.Polygon,
{
points: "40,30 40,70 70,50",
fill: polygonStyle?.fill ? polygonStyle.fill : colors_default.gray_DDDDDD,
stroke: polygonStyle?.stroke ? polygonStyle.stroke : colors_default.black,
strokeWidth: polygonStrokeWidth
}
)
);
};
var PlayCircle_default = PlayCircle;
// src/GalleryBottomSheet/components/SelectItem.tsx
var import_react_native7 = require("react-native");
var import_react6 = __toESM(require("react"));
var SelectItem = ({
selectItemStyle = {
backgroundColor: colors_default.white_80,
iconColor: colors_default.blue_1890FF,
textColor: colors_default.white
},
value = 0
}) => {
return /* @__PURE__ */ import_react6.default.createElement(
import_react_native7.View,
{
style: [styles4.container, { backgroundColor: selectItemStyle.backgroundColor }]
},
/* @__PURE__ */ import_react6.default.createElement(import_react_native7.View, { style: [styles4.icon, { backgroundColor: selectItemStyle.iconColor }] }, /* @__PURE__ */ import_react6.default.createElement(import_react_native7.Text, { style: [styles4.value, { color: selectItemStyle.textColor }] }, value))
);
};
var styles4 = import_react_native7.StyleSheet.create({
container: {
width: "100%",
height: "100%",
borderRadius: 8,
position: "absolute",
top: 0,
alignItems: "center",
justifyContent: "center"
},
icon: {
width: "30%",
height: "30%",
borderRadius: 50,
alignItems: "center",
justifyContent: "center"
},
value: {
fontSize: fontSize_default._14,
fontWeight: "bold"
}
});
var SelectItem_default = SelectItem;
// src/GalleryBottomSheet/GalleryBottomSheet.tsx
var videoIconSize = 48;
var GalleryBottomSheet = ({
isOpen = false,
openHeight = height / 2,
closeHeight = height,
maxHeight = 0,
headerBarColor = colors_default.white,
barIconColor = colors_default.black,
headerTitleStyle = {
color: colors_default.black
},
headerTitle = "T\u1EA5t c\u1EA3 \u1EA3nh",
headerTitleIconColor = colors_default.black,
backgroundColor = colors_default.white,
emptyGalleryMsg = "Kh\xF4ng c\xF3 h\xECnh \u1EA3nh \u0111\u1EC3 hi\u1EC3n th\u1ECB",
emptyGalleryMsgStyle = {
color: colors_default.black
},
videoIconStyle,
albumItemStyle,
assetType = "All",
maxSelectable = 5,
onSelectedAssetsChange
}) => {
const safeHeaderTitleStyle = filterAllowedTextStyle(headerTitleStyle);
const truncatedHeaderTitle = limitedString_default(headerTitle, 3);
const safeEmptyGalleryMsgStyle = filterAllowedTextStyle(emptyGalleryMsgStyle);
const {
albums,
fullAssets,
fullAssetsPagination,
loadFullAssets,
totalAssets,
assets,
loadAssets,
pagination,
requestPermission,
hasPermission,
changeAssetType
} = useGalleryAssets(assetType);
const albumFilterRef = import_react7.default.useRef(null);
const [selectedAlbum, setSelectedAlbum] = import_react7.default.useState(null);
const [showAlbumsList, setShowAlbumsList] = import_react7.default.useState(false);
const inset = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
const topAnimation = (0, import_react_native_reanimated2.useSharedValue)(closeHeight);
const context = (0, import_react_native_reanimated2.useSharedValue)(0);
const timing = (0, import_react_native_reanimated2.useSharedValue)(0);
const scrollY = (0, import_react_native_reanimated2.useSharedValue)(0);
const [enableScroll, setEnableScroll] = import_react7.default.useState(true);
const [isPanEnabled, setIsPanEnabled] = import_react7.default.useState(true);
const [isTop, setIsTop] = import_react7.default.useState(false);
const [assetTypeState, setAssetTypeState] = import_react7.default.useState(assetType);
const [selectedAssets, setSelectedAssets] = import_react7.default.useState([]);
const toggleAsset = (item) => {
setSelectedAssets((prev) => {
const index = prev.findIndex((asset) => asset.node.image.uri === item.node.image.uri);
if (index !== -1) {
return [...prev.slice(0, index), ...prev.slice(index + 1)];
} else if (prev.length < maxSelectable) {
return [...prev, item];
} else {
return prev;
}
});
};
const getSelectedInfo = (item) => {
const index = selectedAssets.findIndex(
(asset) => asset.node.image.uri === item.node.image.uri
);
return {
isSelected: index !== -1,
index
};
};
const expand = import_react7.default.useCallback(() => {
"worklet";
topAnimation.value = (0, import_react_native_reanimated2.withTiming)(openHeight);
(0, import_react_native_reanimated2.runOnJS)(setIsTop)(false);
}, [openHeight, topAnimation]);
const close = import_react7.default.useCallback(() => {
"worklet";
topAnimation.value = (0, import_react_native_reanimated2.withTiming)(closeHeight);
(0, import_react_native_reanimated2.runOnJS)(setIsTop)(false);
}, [closeHeight, topAnimation]);
const handleOpenBottomSheet = () => {
setShowAlbumsList(false);
};
const handleCloseBottomSheet = () => {
if (albumFilterRef.current != null) {
albumFilterRef.current.handleCloseAlbumFilter();
}
};
const handleBottomSheetGallery = () => {
if (!hasPermission) {
requestPermission();
return;
}
if (isOpen) {
handleOpenBottomSheet();
expand();
} else {
handleCloseBottomSheet();
close();
}
};
const animationStyle = (0, import_react_native_reanimated2.useAnimatedStyle)(() => {
const top = topAnimation.value;
return {
top
};
});
const pan = import_react_native_gesture_handler2.Gesture.Pan().enabled(isPanEnabled).onBegin(() => {
"worklet";
context.value = topAnimation.value;
timing.value = 0;
}).onUpdate((event) => {
"worklet";
timing.value++;
topAnimation.value = (0, import_react_native_reanimated2.withSpring)(context.value + event.translationY, {
damping: 100,
stiffness: 400
});
}).onEnd((event) => {
"worklet";
const speedRate = 1 / timing.value;
if (!isTop) {
if (event.velocityY < 0 && (topAnimation.value < height / 2 || speedRate > 0.08 && topAnimation.value < openHeight)) {
topAnimation.value = (0, import_react_native_reanimated2.withSpring)(maxHeight, {
damping: 100,
stiffness: 400
});
(0, import_react_native_reanimated2.runOnJS)(setIsTop)(true);
} else {
topAnimation.value = (0, import_react_native_reanimated2.withSpring)(openHeight, {
damping: 100,
stiffness: 400
});
}
} else {
if (event.velocityY > 0 && speedRate > 0.11) {
topAnimation.value = (0, import_react_native_reanimated2.withSpring)(openHeight, {
damping: 100,
stiffness: 400
});
(0, import_react_native_reanimated2.runOnJS)(setIsTop)(false);
} else if (event.velocityY > 0 && (topAnimation.value >= height / 2 - statusBarHeight && speedRate <= 0.11)) {
topAnimation.value = (0, import_react_native_reanimated2.withSpring)(openHeight, {
damping: 100,
stiffness: 400
});
(0, import_react_native_reanimated2.runOnJS)(setIsTop)(false);
} else {
topAnimation.value = (0, import_react_native_reanimated2.withSpring)(maxHeight, {
damping: 100,
stiffness: 400
});
}
}
});
const onScroll = (0, import_react_native_reanimated2.useAnimatedScrollHandler)({
onScroll: (event) => {
scrollY.value = event.contentOffset.y;
}
});
const panScroll = import_react_native_gesture_handler2.Gesture.Pan().onBegin(() => {
"worklet";
context.value = topAnimation.value;
timing.value = 0;
(0, import_react_native_reanimated2.runOnJS)(setIsPanEnabled)(false);
}).onUpdate((event) => {
"worklet";
timing.value++;
if (event.velocityY > 0 && scrollY.value == 0 && isTop) {
(0, import_react_native_reanimated2.runOnJS)(setIsPanEnabled)(true);
(0, import_react_native_reanimated2.runOnJS)(setEnableScroll)(false);
topAnimation.value = (0, import_react_native_reanimated2.withSpring)(context.value + event.translationY, {
damping: 100,
stiffness: 400
});
}
if (!enableScroll && isTop) {
topAnimation.value = (0, import_react_native_reanimated2.withSpring)(context.value + event.translationY, {
damping: 100,
stiffness: 400
});
}
}).onEnd((event) => {
"worklet";
(0, import_react_native_reanimated2.runOnJS)(setIsPanEnabled)(true);
(0, import_react_native_reanimated2.runOnJS)(setEnableScroll)(true);
const speedRate = 1 / timing.value;
if (isPanEnabled && isTop) {
if (event.velocityY > 0 && speedRate > 0.11 && scrollY.value == 0) {
topAnimation.value = (0, import_react_native_reanimated2.withSpring)(openHeight, {
damping: 100,
stiffness: 400
});
(0, import_react_native_reanimated2.runOnJS)(setIsTop)(false);
} else if (event.velocityY > 0 && (topAnimation.value >= height / 2 - statusBarHeight && speedRate <= 0.11)) {
topAnimation.value = (0, import_react_native_reanimated2.withSpring)(openHeight, {
damping: 100,
stiffness: 400
});
(0, import_react_native_reanimated2.runOnJS)(setIsTop)(false);
} else {
topAnimation.value = (0, import_react_native_reanimated2.withSpring)(maxHeight, {
damping: 100,
stiffness: 400
});
}
} else if (!isPanEnabled && enableScroll && isTop) {
topAnimation.value = (0, import_react_native_reanimated2.withSpring)(maxHeight, {
damping: 100,
stiffness: 400
});
}
});
const scrollViewGesture = import_react_native_gesture_handler2.Gesture.Native();
import_react7.default.useEffect(() => {
if (assetTypeState !== assetType) {
setAssetTypeState(assetType);
changeAssetType(assetType);
setSelectedAlbum(null);
}
}, [assetType]);
import_react7.default.useEffect(() => {
if (onSelectedAssetsChange) {
onSelectedAssetsChange(selectedAssets);
}
}, [selectedAssets]);
import_react7.default.useEffect(() => {
handleBottomSheetGallery();
}, [isOpen]);
return /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, /* @__PURE__ */ import_react7.default.createElement(import_react_native_gesture_handler2.GestureDetector, { gesture: pan }, /* @__PURE__ */ import_react7.default.createElement(
import_react_native_reanimated2.default.View,
{
style: [
styles5.container,
animationStyle,
{
backgroundColor,
paddingBottom: inset.bottom
}
]
},
/* @__PURE__ */ import_react7.default.createElement(
import_react_native8.View,
{
style: [
styles5.handleContainer,
{
backgroundColor: headerBarColor
}
]
},
/* @__PURE__ */ import_react7.default.createElement(import_react_native8.View, { style: [styles5.handleBar, {
backgroundColor: barIconColor
}] }),
/* @__PURE__ */ import_react7.default.createElement(
import_react_native8.TouchableOpacity,
{
style: styles5.selectedAlbumContainer,
onPress: () => {
if (albumFilterRef.current != null) {
if (!showAlbumsList) {
albumFilterRef.current.handleOpenAlbumFilter();
} else {
albumFilterRef.current.handleCloseAlbumFilter();
}
}
},
disabled: albums.length === 0 || fullAssets.length === 0 || totalAssets == 0,
touchSoundDisabled: true,
activeOpacity: 0.7
},
/* @__PURE__ */ import_react7.default.createElement(import_react_native8.Text, { style: [
styles5.handleText,
safeHeaderTitleStyle
] }, selectedAlbum == null ? truncatedHeaderTitle : selectedAlbum.title),
/* @__PURE__ */ import_react7.default.createElement(import_AntDesign.default, { name: !showAlbumsList ? "down" : "up", size: 15, color: headerTitleIconColor })
)
),
/* @__PURE__ */ import_react7.default.createElement(
import_react_native_gesture_handler2.GestureDetector,
{
gesture: import_react_native_gesture_handler2.Gesture.Simultaneous(panScroll, scrollViewGesture)
},
albums.length === 0 || fullAssets.length === 0 || totalAssets == 0 ? /* @__PURE__ */ import_react7.default.createElement(import_react_native8.View, null, /* @__PURE__ */ import_react7.default.createElement(import_react_native8.Text, { style: [
styles5.noImage,
safeEmptyGalleryMsgStyle
] }, emptyGalleryMsg)) : /* @__PURE__ */ import_react7.default.createElement(
import_react_native_reanimated2.default.FlatList,
{
data: selectedAlbum == null ? fullAssets : assets[selectedAlbum?.id ?? selectedAlbum.title],
scrollEnabled: enableScroll,
keyExtractor: (item) => item.node.image.uri,
renderItem: ({ item }) => {
const info = getSelectedInfo(item);
return /* @__PURE__ */ import_react7.default.createElement(
import_react_native8.TouchableOpacity,
{
style: styles5.image,
onPress: () => toggleAsset(item),
activeOpacity: 0.7
},
/* @__PURE__ */ import_react7.default.createElement(
import_react_native8.Image,
{
source: { uri: item.node.image.uri },
style: styles5.image
}
),
item.node.type === "video/mp4" && /* @__PURE__ */ import_react7.default.createElement(
PlayCircle_default,
{
size: videoIconSize,
viewStyle: styles5.videoIcon,
circleStyle: videoIconStyle?.circleStyle,
polygonStyle: videoIconStyle?.polygonStyle
}
),
info.isSelected && /* @__PURE__ */ import_react7.default.createElement(
SelectItem_default,
{
selectItemStyle: {
backgroundColor: colors_default.white_80,
iconColor: colors_default.blue_1890FF,
textColor: colors_default.white
},
value: info.index + 1
}
)
);
},
onScroll,
bounces: false,
scrollEventThrottle: 16,
numColumns: 3,
columnWrapperStyle: styles5.row,
onEndReached: () => {
if (selectedAlbum == null) {
loadFullAssets(15, fullAssetsPagination?.endCursor, assetTypeState);
} else {
loadAssets(selectedAlbum, 15, pagination[selectedAlbum?.id ?? selectedAlbum.title].endCursor, assetTypeState);
}
},
onEndReachedThreshold: 0.5,
initialNumToRender: 9,
showsVerticalScrollIndicator: false
}
)
),
/* @__PURE__ */ import_react7.default.createElement(
AlbumFilter,
{
ref: albumFilterRef,
setSelectedAlbum,
topAnimation,
isPanEnabled,
setIsPanEnabled,
enableScroll,
setEnableScroll,
isTop,
setIsTop,
openHeight,
setShowAlbumsList,
albums,
assets,
fullAssets,
totalAssets,
headerTitle,
headerBarColor,
backgroundColor,
albumItemStyle,
emptyGalleryMsg,
safeEmptyGalleryMsgStyle
}
)
)));
};
var GalleryBottomSheet_default = GalleryBottomSheet;
var styles5 = import_react_native8.StyleSheet.create({
container: {
...import_react_native8.StyleSheet.absoluteFillObject,
borderTopLeftRadius: 20,
borderTopRightRadius: 20
},
lineContainer: {
marginVertical: 10,
alignItems: "center"
},
line: {
width: 50,
height: 4,
backgroundColor: "black",
borderRadius: 20
},
handleContainer: {
alignItems: "center",
paddingVertical: 10,
// Tăng chiều cao của panning area
borderTopLeftRadius: 15,
borderTopRightRadius: 15,
height: 60
},
handleBar: {
width: 40,
height: 6,
borderRadius: 3,
marginBottom: 8
// Thêm khoảng cách giữa thanh kéo và chữ
},
handleText: {
fontSize: 16
},
contentContainer: {
flex: 1,
padding: 5
},
selectedAlbumContainer: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
gap: 10
},
noImage: {
textAlign: "center",
fontSize: fontSize_default._18
},
row: {
justifyContent: "space-between"
},
imageContainer: {
width: (width - 20) / 3,
height: (width - 20) / 3,
borderRadius: 8,
marginBottom: 5
},
image: {
width: (width - 20) / 3,
height: (width - 20) / 3,
borderRadius: 8,
marginBottom: 5
},
videoIcon: {
position: "absolute",
top: "50%",
left: "50%",
transform: [{ translateX: -videoIconSize / 2 }, { translateY: -videoIconSize / 2 }]
}
});
// src/SvgIcon/SvgIcon.tsx
var import_react16 = __toESM(require("react"));
// src/SvgIcon/components/IconPlus.tsx
var import_react8 = __toESM(require("react"));
var import_react_native_svg2 = __toESM(require("react-native-svg"));
var IconPlus = ({
size,
color,
strokeWidth,
style
}) => {
return /* @__PURE__ */ import_react8.default.createElement(
import_react_native_svg2.default,
{
width: size,
height: size,
viewBox: "0 0 24 24",
fill: "none",
style
},
/* @__PURE__ */ import_react8.default.createElement(
import_react_native_svg2.Line,
{
x1: "12",
y1: "5",
x2: "12",
y2: "19",
stroke: color,
strokeWidth,
strokeLinecap: "round"
}
),
/* @__PURE__ */ import_react8.default.createElement(
import_react_native_svg2.Line,
{
x1: "5",
y1: "12",
x2: "19",
y2: "12",
stroke: color,
strokeWidth,
strokeLinecap: "round"
}
)
);
};
var IconPlus_default = IconPlus;
// src/SvgIcon/components/IconCheckBox.tsx
var import_react9 = __toESM(require("react"));
var import_react_native_svg3 = __toESM(require("react-native-svg"));
var IconCheckBox = ({
size,
color,
strokeWidth,
fill,
style
}) => {
return /* @__PURE__ */ import_react9.default.createElement(
import_react_native_svg3.default,
{
width: size,
height: size,
viewBox: "0 0 24 24",
fill: "none",
style
},
/* @__PURE__ */ import_react9.default.createElement(
import_react_native_svg3.Rect,
{
x: "3",
y: "3",
width: "18",
height: "18",
rx: "2",
ry: "2",
stroke: color,
strokeWidth,
fill
}
),
/* @__PURE__ */ import_react9.default.createElement(
import_react_native_svg3.Path,
{
d: "M7 12.5L10.5 16L17 8",
stroke: color,
strokeWidth,
strokeLinecap: "round",
strokeLinejoin: "round"
}
)
);
};
var IconCheckBox_default = IconCheckBox;
// src/SvgIcon/components/IconCheckBoxWithBg.tsx
var import_react10 = __toESM(require("react"));
var import_react_native_svg4 = __toESM(require("react-native-svg"));
var IconCheckBoxWithBg = ({
size,
color,
strokeWidth,
fill,
style
}) => {
return /* @__PURE__ */ import_react10.default.createElement(
import_react_native_svg4.default,
{
width: size,
height: size,
viewBox: "0 0 24 24",
fill: "none",
style
},
/* @__PURE__ */ import_react10.default.createElement(
import_react_native