UNPKG

@kietpt2003/react-native-core-ui

Version:
1,509 lines (1,488 loc) 67.8 kB
"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