@0610studio/zs-ui 0.18.1 → 0.19.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.
Files changed (106) hide show
  1. package/build/assets/SvgTooltipTail.d.ts +7 -0
  2. package/build/assets/SvgTooltipTail.d.ts.map +1 -0
  3. package/build/assets/SvgTooltipTail.js +7 -0
  4. package/build/assets/SvgTooltipTail.js.map +1 -0
  5. package/build/context/ThemeContext.d.ts.map +1 -1
  6. package/build/context/ThemeContext.js +17 -5
  7. package/build/context/ThemeContext.js.map +1 -1
  8. package/build/index.d.ts +15 -2
  9. package/build/index.d.ts.map +1 -1
  10. package/build/index.js +7 -1
  11. package/build/index.js.map +1 -1
  12. package/build/model/types.d.ts +1 -0
  13. package/build/model/types.d.ts.map +1 -1
  14. package/build/model/types.js.map +1 -1
  15. package/build/model/useFoldingState.d.ts.map +1 -1
  16. package/build/model/useFoldingState.js +51 -32
  17. package/build/model/useFoldingState.js.map +1 -1
  18. package/build/overlay/AlertOverlay/index.d.ts +1 -2
  19. package/build/overlay/AlertOverlay/index.d.ts.map +1 -1
  20. package/build/overlay/AlertOverlay/index.js +11 -9
  21. package/build/overlay/AlertOverlay/index.js.map +1 -1
  22. package/build/overlay/BottomSheetOverlay/index.d.ts.map +1 -1
  23. package/build/overlay/BottomSheetOverlay/index.js +4 -3
  24. package/build/overlay/BottomSheetOverlay/index.js.map +1 -1
  25. package/build/overlay/LoadingNotify/index.d.ts +2 -2
  26. package/build/overlay/LoadingNotify/index.d.ts.map +1 -1
  27. package/build/overlay/LoadingNotify/index.js +2 -2
  28. package/build/overlay/LoadingNotify/index.js.map +1 -1
  29. package/build/overlay/Modality/index.d.ts.map +1 -1
  30. package/build/overlay/Modality/index.js +3 -3
  31. package/build/overlay/Modality/index.js.map +1 -1
  32. package/build/overlay/PopOver/PopOverMenu.d.ts.map +1 -1
  33. package/build/overlay/PopOver/PopOverMenu.js +5 -3
  34. package/build/overlay/PopOver/PopOverMenu.js.map +1 -1
  35. package/build/overlay/SnackbarNotify/ui/SnackbarItem.d.ts +3 -3
  36. package/build/overlay/SnackbarNotify/ui/SnackbarItem.d.ts.map +1 -1
  37. package/build/overlay/SnackbarNotify/ui/SnackbarItem.js +5 -5
  38. package/build/overlay/SnackbarNotify/ui/SnackbarItem.js.map +1 -1
  39. package/build/theme/intentColors.d.ts +9 -0
  40. package/build/theme/intentColors.d.ts.map +1 -0
  41. package/build/theme/intentColors.js +30 -0
  42. package/build/theme/intentColors.js.map +1 -0
  43. package/build/theme/tokens.d.ts +35 -0
  44. package/build/theme/tokens.d.ts.map +1 -0
  45. package/build/theme/tokens.js +35 -0
  46. package/build/theme/tokens.js.map +1 -0
  47. package/build/theme/typography.d.ts.map +1 -1
  48. package/build/theme/typography.js +36 -72
  49. package/build/theme/typography.js.map +1 -1
  50. package/build/ui/ZSBlockButton/index.d.ts.map +1 -1
  51. package/build/ui/ZSBlockButton/index.js +4 -19
  52. package/build/ui/ZSBlockButton/index.js.map +1 -1
  53. package/build/ui/ZSBorderBeam/index.d.ts +48 -0
  54. package/build/ui/ZSBorderBeam/index.d.ts.map +1 -0
  55. package/build/ui/ZSBorderBeam/index.js +135 -0
  56. package/build/ui/ZSBorderBeam/index.js.map +1 -0
  57. package/build/ui/ZSCheckBox/index.d.ts +18 -0
  58. package/build/ui/ZSCheckBox/index.d.ts.map +1 -0
  59. package/build/ui/ZSCheckBox/index.js +60 -0
  60. package/build/ui/ZSCheckBox/index.js.map +1 -0
  61. package/build/ui/ZSChip/index.d.ts +28 -0
  62. package/build/ui/ZSChip/index.d.ts.map +1 -0
  63. package/build/ui/ZSChip/index.js +76 -0
  64. package/build/ui/ZSChip/index.js.map +1 -0
  65. package/build/ui/ZSContainer/index.d.ts +0 -1
  66. package/build/ui/ZSContainer/index.d.ts.map +1 -1
  67. package/build/ui/ZSContainer/index.js +3 -2
  68. package/build/ui/ZSContainer/index.js.map +1 -1
  69. package/build/ui/ZSPressable/index.d.ts.map +1 -1
  70. package/build/ui/ZSPressable/index.js +9 -4
  71. package/build/ui/ZSPressable/index.js.map +1 -1
  72. package/build/ui/ZSRadioGroup/index.d.ts +2 -3
  73. package/build/ui/ZSRadioGroup/index.d.ts.map +1 -1
  74. package/build/ui/ZSRadioGroup/index.js +12 -4
  75. package/build/ui/ZSRadioGroup/index.js.map +1 -1
  76. package/build/ui/ZSSegmented/index.d.ts +26 -0
  77. package/build/ui/ZSSegmented/index.d.ts.map +1 -0
  78. package/build/ui/ZSSegmented/index.js +124 -0
  79. package/build/ui/ZSSegmented/index.js.map +1 -0
  80. package/build/ui/ZSSkeleton/SkiaShimmer.d.ts +18 -0
  81. package/build/ui/ZSSkeleton/SkiaShimmer.d.ts.map +1 -0
  82. package/build/ui/ZSSkeleton/SkiaShimmer.js +73 -0
  83. package/build/ui/ZSSkeleton/SkiaShimmer.js.map +1 -0
  84. package/build/ui/ZSSkeleton/index.d.ts +7 -2
  85. package/build/ui/ZSSkeleton/index.d.ts.map +1 -1
  86. package/build/ui/ZSSkeleton/index.js +12 -43
  87. package/build/ui/ZSSkeleton/index.js.map +1 -1
  88. package/build/ui/ZSSkeletonBox/index.d.ts +8 -2
  89. package/build/ui/ZSSkeletonBox/index.d.ts.map +1 -1
  90. package/build/ui/ZSSkeletonBox/index.js +6 -46
  91. package/build/ui/ZSSkeletonBox/index.js.map +1 -1
  92. package/build/ui/ZSSwitch/index.d.ts +2 -1
  93. package/build/ui/ZSSwitch/index.d.ts.map +1 -1
  94. package/build/ui/ZSSwitch/index.js +6 -4
  95. package/build/ui/ZSSwitch/index.js.map +1 -1
  96. package/build/ui/ZSTextField/index.d.ts.map +1 -1
  97. package/build/ui/ZSTextField/index.js +17 -13
  98. package/build/ui/ZSTextField/index.js.map +1 -1
  99. package/build/ui/ZSTextField/ui/ButtonClose.d.ts.map +1 -1
  100. package/build/ui/ZSTextField/ui/ButtonClose.js +4 -1
  101. package/build/ui/ZSTextField/ui/ButtonClose.js.map +1 -1
  102. package/build/ui/ZSTooltip/index.d.ts +38 -0
  103. package/build/ui/ZSTooltip/index.d.ts.map +1 -0
  104. package/build/ui/ZSTooltip/index.js +99 -0
  105. package/build/ui/ZSTooltip/index.js.map +1 -0
  106. package/package.json +6 -1
@@ -0,0 +1,124 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useEffect, useState } from "react";
3
+ import { Pressable, StyleSheet, View } from "react-native";
4
+ import Animated, { Easing, useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
5
+ import ZSText from "../ZSText";
6
+ import { useTheme } from "../../context/ThemeContext";
7
+ import { DISABLED_OPACITY, DURATION } from "../../theme/tokens";
8
+ const ANIMATION_DURATION = DURATION.slow;
9
+ const CONTAINER_INSET = 3;
10
+ const SEGMENT_HORIZONTAL_PADDING = 14;
11
+ function ZSSegmented({ options, selectedIndex, initialIndex = 0, onChange, fullWidth = true, containerHeight = 40, textSize = '2', trackColor, thumbColor, disabled = false, style, onLayout, ...props }) {
12
+ const { palette } = useTheme();
13
+ const [internalIndex, setInternalIndex] = useState(initialIndex);
14
+ const [trackWidth, setTrackWidth] = useState(0);
15
+ const [labelWidths, setLabelWidths] = useState([]);
16
+ const translateX = useSharedValue(0);
17
+ const count = Math.max(options.length, 1);
18
+ const index = Math.min(selectedIndex ?? internalIndex, count - 1);
19
+ const measuredWidths = labelWidths.slice(0, count);
20
+ const maxLabelWidth = measuredWidths.length === count && measuredWidths.every(w => w > 0)
21
+ ? Math.max(...measuredWidths)
22
+ : 0;
23
+ const segmentWidth = fullWidth
24
+ ? (trackWidth > 0 ? (trackWidth - CONTAINER_INSET * 2) / count : 0)
25
+ : (maxLabelWidth > 0 ? Math.ceil(maxLabelWidth) + SEGMENT_HORIZONTAL_PADDING * 2 : 0);
26
+ useEffect(() => {
27
+ if (segmentWidth <= 0)
28
+ return;
29
+ translateX.value = withTiming(index * segmentWidth, {
30
+ duration: ANIMATION_DURATION,
31
+ easing: Easing.out(Easing.quad),
32
+ });
33
+ }, [index, segmentWidth, translateX]);
34
+ const thumbAnimatedStyle = useAnimatedStyle(() => ({
35
+ transform: [{ translateX: translateX.value }],
36
+ }));
37
+ const handleLayout = (event) => {
38
+ const { width } = event.nativeEvent.layout;
39
+ setTrackWidth(prev => (prev === width ? prev : width));
40
+ onLayout?.(event);
41
+ };
42
+ const handlePress = (nextIndex) => {
43
+ if (disabled || nextIndex === index)
44
+ return;
45
+ if (selectedIndex === undefined)
46
+ setInternalIndex(nextIndex);
47
+ onChange?.(nextIndex);
48
+ };
49
+ return (_jsxs(View, { style: [
50
+ styles.container,
51
+ fullWidth ? styles.fullWidth : styles.hugContent,
52
+ {
53
+ height: containerHeight,
54
+ borderRadius: containerHeight / 2,
55
+ backgroundColor: trackColor ?? palette.background.layer2,
56
+ opacity: disabled ? DISABLED_OPACITY : 1,
57
+ },
58
+ style,
59
+ ], onLayout: handleLayout, accessibilityRole: 'tablist', ...props, children: [!fullWidth && (_jsx(View, { pointerEvents: 'none', accessible: false, importantForAccessibility: 'no-hide-descendants', style: styles.measureBox, children: options.map((label, optionIndex) => (_jsx(ZSText, { typo: `subTitle.${textSize}`, allowFontScaling: false, numberOfLines: 1, onLayout: event => {
60
+ const measuredWidth = event.nativeEvent.layout.width;
61
+ setLabelWidths(prev => {
62
+ if (prev[optionIndex] === measuredWidth)
63
+ return prev;
64
+ const next = [...prev];
65
+ next[optionIndex] = measuredWidth;
66
+ return next;
67
+ });
68
+ }, children: label }, `measure_${optionIndex}_${label}`))) })), segmentWidth > 0 && (_jsx(Animated.View, { testID: 'zs-segmented-thumb', pointerEvents: 'none', style: [
69
+ styles.thumb,
70
+ {
71
+ width: segmentWidth,
72
+ height: containerHeight - CONTAINER_INSET * 2,
73
+ borderRadius: (containerHeight - CONTAINER_INSET * 2) / 2,
74
+ backgroundColor: thumbColor ?? palette.background.base,
75
+ // 다크 모드에서는 밝은 그림자로 thumb 를 구분
76
+ shadowColor: palette.grey[100],
77
+ },
78
+ thumbAnimatedStyle,
79
+ ] })), options.map((label, optionIndex) => {
80
+ const isActive = optionIndex === index;
81
+ return (_jsx(Pressable, { testID: `zs-segmented-segment-${optionIndex}`, style: [styles.segment, fullWidth ? styles.segmentFlex : { width: segmentWidth > 0 ? segmentWidth : undefined }], onPress: () => handlePress(optionIndex), disabled: disabled, accessibilityRole: 'tab', accessibilityState: { selected: isActive }, children: _jsx(ZSText, { typo: isActive ? `subTitle.${textSize}` : `body.${textSize}`, color: isActive ? 'base' : 'secondary', allowFontScaling: false, numberOfLines: 1, children: label }) }, `${optionIndex}_${label}`));
82
+ })] }));
83
+ }
84
+ export default React.memo(ZSSegmented);
85
+ const styles = StyleSheet.create({
86
+ container: {
87
+ flexDirection: 'row',
88
+ padding: CONTAINER_INSET,
89
+ position: 'relative',
90
+ },
91
+ // flexWrap 부모에서도 폭이 부모 기준으로 고정되도록 stretch 대신 100% 사용
92
+ fullWidth: {
93
+ width: '100%',
94
+ },
95
+ hugContent: {
96
+ alignSelf: 'flex-start',
97
+ },
98
+ measureBox: {
99
+ position: 'absolute',
100
+ width: 2000,
101
+ opacity: 0,
102
+ flexDirection: 'row',
103
+ gap: 20,
104
+ },
105
+ thumb: {
106
+ position: 'absolute',
107
+ top: CONTAINER_INSET,
108
+ left: CONTAINER_INSET,
109
+ shadowOffset: { width: 0, height: 2 },
110
+ shadowOpacity: 0.1,
111
+ shadowRadius: 4,
112
+ elevation: 3,
113
+ },
114
+ segment: {
115
+ zIndex: 1,
116
+ alignItems: 'center',
117
+ justifyContent: 'center',
118
+ paddingHorizontal: 8,
119
+ },
120
+ segmentFlex: {
121
+ flex: 1,
122
+ },
123
+ });
124
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui/ZSSegmented/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useEffect, useState } from \"react\";\nimport { Pressable, StyleSheet, View, type LayoutChangeEvent, type StyleProp, type ViewProps, type ViewStyle } from \"react-native\";\nimport Animated, { Easing, useAnimatedStyle, useSharedValue, withTiming } from \"react-native-reanimated\";\nimport ZSText from \"../ZSText\";\nimport { useTheme } from \"../../context/ThemeContext\";\nimport { DISABLED_OPACITY, DURATION } from \"../../theme/tokens\";\n\nconst ANIMATION_DURATION = DURATION.slow;\nconst CONTAINER_INSET = 3;\nconst SEGMENT_HORIZONTAL_PADDING = 14;\n\nexport type ZSSegmentedTextSize = '1' | '2' | '3' | '4' | '5' | '6';\n\nexport interface ZSSegmentedProps extends ViewProps {\n /** 세그먼트 라벨 목록 (2개 이상) */\n options: string[];\n /** 선택 인덱스를 외부에서 제어할 때 사용 (미지정 시 내부 상태로 동작) */\n selectedIndex?: number;\n /** 비제어 모드의 초기 선택 인덱스 */\n initialIndex?: number;\n onChange?: (index: number) => void;\n /** true(기본): 부모 폭을 가득 채움 · false: 가장 긴 라벨 폭에 맞춰 콘텐츠 크기로 렌더링 */\n fullWidth?: boolean;\n containerHeight?: number;\n textSize?: ZSSegmentedTextSize;\n /** 트랙 배경색 (기본: palette.background.layer2) */\n trackColor?: string;\n /** 선택 블럭 색상 (기본: palette.background.base) */\n thumbColor?: string;\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\nfunction ZSSegmented({\n options,\n selectedIndex,\n initialIndex = 0,\n onChange,\n fullWidth = true,\n containerHeight = 40,\n textSize = '2',\n trackColor,\n thumbColor,\n disabled = false,\n style,\n onLayout,\n ...props\n}: ZSSegmentedProps) {\n const { palette } = useTheme();\n const [internalIndex, setInternalIndex] = useState(initialIndex);\n const [trackWidth, setTrackWidth] = useState(0);\n const [labelWidths, setLabelWidths] = useState<number[]>([]);\n const translateX = useSharedValue(0);\n\n const count = Math.max(options.length, 1);\n const index = Math.min(selectedIndex ?? internalIndex, count - 1);\n const measuredWidths = labelWidths.slice(0, count);\n const maxLabelWidth = measuredWidths.length === count && measuredWidths.every(w => w > 0)\n ? Math.max(...measuredWidths)\n : 0;\n const segmentWidth = fullWidth\n ? (trackWidth > 0 ? (trackWidth - CONTAINER_INSET * 2) / count : 0)\n : (maxLabelWidth > 0 ? Math.ceil(maxLabelWidth) + SEGMENT_HORIZONTAL_PADDING * 2 : 0);\n\n useEffect(() => {\n if (segmentWidth <= 0) return;\n translateX.value = withTiming(index * segmentWidth, {\n duration: ANIMATION_DURATION,\n easing: Easing.out(Easing.quad),\n });\n }, [index, segmentWidth, translateX]);\n\n const thumbAnimatedStyle = useAnimatedStyle(() => ({\n transform: [{ translateX: translateX.value }],\n }));\n\n const handleLayout = (event: LayoutChangeEvent) => {\n const { width } = event.nativeEvent.layout;\n setTrackWidth(prev => (prev === width ? prev : width));\n onLayout?.(event);\n };\n\n const handlePress = (nextIndex: number) => {\n if (disabled || nextIndex === index) return;\n if (selectedIndex === undefined) setInternalIndex(nextIndex);\n onChange?.(nextIndex);\n };\n\n return (\n <View\n style={[\n styles.container,\n fullWidth ? styles.fullWidth : styles.hugContent,\n {\n height: containerHeight,\n borderRadius: containerHeight / 2,\n backgroundColor: trackColor ?? palette.background.layer2,\n opacity: disabled ? DISABLED_OPACITY : 1,\n },\n style,\n ]}\n onLayout={handleLayout}\n accessibilityRole='tablist'\n {...props}\n >\n {/* 라벨 폭 측정용 히든 박스 — 활성(굵은) typo 기준으로 측정해 상태 전환 시 넘침 방지 */}\n {!fullWidth && (\n <View\n pointerEvents='none'\n accessible={false}\n importantForAccessibility='no-hide-descendants'\n style={styles.measureBox}\n >\n {options.map((label, optionIndex) => (\n <ZSText\n key={`measure_${optionIndex}_${label}`}\n typo={`subTitle.${textSize}`}\n allowFontScaling={false}\n numberOfLines={1}\n onLayout={event => {\n const measuredWidth = event.nativeEvent.layout.width;\n setLabelWidths(prev => {\n if (prev[optionIndex] === measuredWidth) return prev;\n const next = [...prev];\n next[optionIndex] = measuredWidth;\n return next;\n });\n }}\n >\n {label}\n </ZSText>\n ))}\n </View>\n )}\n {segmentWidth > 0 && (\n <Animated.View\n testID='zs-segmented-thumb'\n pointerEvents='none'\n style={[\n styles.thumb,\n {\n width: segmentWidth,\n height: containerHeight - CONTAINER_INSET * 2,\n borderRadius: (containerHeight - CONTAINER_INSET * 2) / 2,\n backgroundColor: thumbColor ?? palette.background.base,\n // 다크 모드에서는 밝은 그림자로 thumb 를 구분\n shadowColor: palette.grey[100],\n },\n thumbAnimatedStyle,\n ]}\n />\n )}\n\n {options.map((label, optionIndex) => {\n const isActive = optionIndex === index;\n return (\n <Pressable\n key={`${optionIndex}_${label}`}\n testID={`zs-segmented-segment-${optionIndex}`}\n style={[styles.segment, fullWidth ? styles.segmentFlex : { width: segmentWidth > 0 ? segmentWidth : undefined }]}\n onPress={() => handlePress(optionIndex)}\n disabled={disabled}\n accessibilityRole='tab'\n accessibilityState={{ selected: isActive }}\n >\n <ZSText\n typo={isActive ? `subTitle.${textSize}` : `body.${textSize}`}\n color={isActive ? 'base' : 'secondary'}\n allowFontScaling={false}\n numberOfLines={1}\n >\n {label}\n </ZSText>\n </Pressable>\n );\n })}\n </View>\n );\n}\n\nexport default React.memo(ZSSegmented);\n\nconst styles = StyleSheet.create({\n container: {\n flexDirection: 'row',\n padding: CONTAINER_INSET,\n position: 'relative',\n },\n // flexWrap 부모에서도 폭이 부모 기준으로 고정되도록 stretch 대신 100% 사용\n fullWidth: {\n width: '100%',\n },\n hugContent: {\n alignSelf: 'flex-start',\n },\n measureBox: {\n position: 'absolute',\n width: 2000,\n opacity: 0,\n flexDirection: 'row',\n gap: 20,\n },\n thumb: {\n position: 'absolute',\n top: CONTAINER_INSET,\n left: CONTAINER_INSET,\n shadowOffset: { width: 0, height: 2 },\n shadowOpacity: 0.1,\n shadowRadius: 4,\n elevation: 3,\n },\n segment: {\n zIndex: 1,\n alignItems: 'center',\n justifyContent: 'center',\n paddingHorizontal: 8,\n },\n segmentFlex: {\n flex: 1,\n },\n});\n"]}
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ export interface SkiaShimmerProps {
3
+ /** 하이라이트 색상 (#RGB / #RRGGBB) */
4
+ color: string;
5
+ /** 밴드 중심의 최대 불투명도 (0~1) */
6
+ opacity?: number;
7
+ /** 밴드가 한 번 지나가는 시간(ms) */
8
+ duration?: number;
9
+ }
10
+ /**
11
+ * 부모를 가득 채우는 shimmer 오버레이.
12
+ * 같은 hue의 알파 그라데이션(투명 → 하이라이트 → 투명)을 대각선으로 흘려보내
13
+ * 아래 콘텐츠가 실제로 비쳐 보인다.
14
+ */
15
+ declare function SkiaShimmer({ color, opacity, duration }: SkiaShimmerProps): React.JSX.Element;
16
+ declare const _default: React.MemoExoticComponent<typeof SkiaShimmer>;
17
+ export default _default;
18
+ //# sourceMappingURL=SkiaShimmer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SkiaShimmer.d.ts","sourceRoot":"","sources":["../../../src/ui/ZSSkeleton/SkiaShimmer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAY5D,MAAM,WAAW,gBAAgB;IAC/B,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAmBD;;;;GAIG;AACH,iBAAS,WAAW,CAAC,EAAE,KAAK,EAAE,OAAa,EAAE,QAAe,EAAE,EAAE,gBAAgB,qBAwE/E;;AAED,wBAAuC"}
@@ -0,0 +1,73 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React, { useEffect, useMemo, useState } from "react";
3
+ import { StyleSheet, View } from "react-native";
4
+ import { Canvas, LinearGradient, Rect, vec } from "@shopify/react-native-skia";
5
+ import { Easing, cancelAnimation, useDerivedValue, useSharedValue, withRepeat, withTiming, } from "react-native-reanimated";
6
+ const clamp01 = (value) => Math.min(Math.max(value, 0), 1);
7
+ /** #RGB / #RRGGBB hex만 정규화, 그 외 형식은 null */
8
+ const toRgbHex = (color) => {
9
+ if (/^#[0-9a-fA-F]{6}$/.test(color))
10
+ return color;
11
+ if (/^#[0-9a-fA-F]{3}$/.test(color)) {
12
+ const [r, g, b] = color.slice(1);
13
+ return `#${r}${r}${g}${g}${b}${b}`;
14
+ }
15
+ return null;
16
+ };
17
+ const withAlpha = (rgbHex, alpha) => `${rgbHex}${Math.round(clamp01(alpha) * 255)
18
+ .toString(16)
19
+ .padStart(2, "0")}`;
20
+ /**
21
+ * 부모를 가득 채우는 shimmer 오버레이.
22
+ * 같은 hue의 알파 그라데이션(투명 → 하이라이트 → 투명)을 대각선으로 흘려보내
23
+ * 아래 콘텐츠가 실제로 비쳐 보인다.
24
+ */
25
+ function SkiaShimmer({ color, opacity = 0.6, duration = 1100 }) {
26
+ const [size, setSize] = useState({ width: 0, height: 0 });
27
+ const progress = useSharedValue(0);
28
+ // 밴드 폭·이동 거리는 실제 측정된 크기 기준 (기존 구현의 DEVICE_WIDTH 하드코딩 제거)
29
+ const bandWidth = size.width * 0.65;
30
+ const startOffset = bandWidth + size.height;
31
+ const travel = size.width + bandWidth * 2 + size.height;
32
+ const gradient = useMemo(() => {
33
+ const rgb = toRgbHex(color);
34
+ const peak = clamp01(opacity);
35
+ if (!rgb) {
36
+ // hex가 아니면 알파 조절이 불가하므로 색상 그대로 중앙에 사용
37
+ return { colors: ["transparent", color, "transparent"], positions: [0, 0.5, 1] };
38
+ }
39
+ // 같은 hue로만 알파를 올렸다 내리는 종 모양 프로파일 → 경계선 없는 부드러운 광택
40
+ return {
41
+ colors: [
42
+ withAlpha(rgb, 0),
43
+ withAlpha(rgb, peak * 0.4),
44
+ withAlpha(rgb, peak),
45
+ withAlpha(rgb, peak * 0.4),
46
+ withAlpha(rgb, 0),
47
+ ],
48
+ positions: [0, 0.3, 0.5, 0.7, 1],
49
+ };
50
+ }, [color, opacity]);
51
+ useEffect(() => {
52
+ if (size.width === 0)
53
+ return;
54
+ progress.value = 0;
55
+ progress.value = withRepeat(withTiming(1, { duration, easing: Easing.inOut(Easing.ease) }), -1, false);
56
+ return () => cancelAnimation(progress);
57
+ }, [duration, size.width]);
58
+ const gradientStart = useDerivedValue(() => {
59
+ const x = -startOffset + travel * progress.value;
60
+ return vec(x, 0);
61
+ });
62
+ const gradientEnd = useDerivedValue(() => {
63
+ const x = -startOffset + travel * progress.value;
64
+ return vec(x + bandWidth, size.height);
65
+ });
66
+ const handleLayout = (event) => {
67
+ const { width, height } = event.nativeEvent.layout;
68
+ setSize((prev) => (prev.width === width && prev.height === height ? prev : { width, height }));
69
+ };
70
+ return (_jsx(View, { pointerEvents: "none", style: StyleSheet.absoluteFill, onLayout: handleLayout, children: size.width > 0 && size.height > 0 && (_jsx(Canvas, { style: StyleSheet.absoluteFill, children: _jsx(Rect, { x: 0, y: 0, width: size.width, height: size.height, children: _jsx(LinearGradient, { start: gradientStart, end: gradientEnd, colors: gradient.colors, positions: gradient.positions }) }) })) }));
71
+ }
72
+ export default React.memo(SkiaShimmer);
73
+ //# sourceMappingURL=SkiaShimmer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SkiaShimmer.js","sourceRoot":"","sources":["../../../src/ui/ZSSkeleton/SkiaShimmer.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useEffect, useMemo, useState } from \"react\";\nimport { StyleSheet, View, type LayoutChangeEvent } from \"react-native\";\nimport { Canvas, LinearGradient, Rect, vec } from \"@shopify/react-native-skia\";\nimport {\n Easing,\n cancelAnimation,\n useDerivedValue,\n useSharedValue,\n withRepeat,\n withTiming,\n} from \"react-native-reanimated\";\n\nexport interface SkiaShimmerProps {\n /** 하이라이트 색상 (#RGB / #RRGGBB) */\n color: string;\n /** 밴드 중심의 최대 불투명도 (0~1) */\n opacity?: number;\n /** 밴드가 한 번 지나가는 시간(ms) */\n duration?: number;\n}\n\nconst clamp01 = (value: number) => Math.min(Math.max(value, 0), 1);\n\n/** #RGB / #RRGGBB hex만 정규화, 그 외 형식은 null */\nconst toRgbHex = (color: string): string | null => {\n if (/^#[0-9a-fA-F]{6}$/.test(color)) return color;\n if (/^#[0-9a-fA-F]{3}$/.test(color)) {\n const [r, g, b] = color.slice(1);\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return null;\n};\n\nconst withAlpha = (rgbHex: string, alpha: number): string =>\n `${rgbHex}${Math.round(clamp01(alpha) * 255)\n .toString(16)\n .padStart(2, \"0\")}`;\n\n/**\n * 부모를 가득 채우는 shimmer 오버레이.\n * 같은 hue의 알파 그라데이션(투명 → 하이라이트 → 투명)을 대각선으로 흘려보내\n * 아래 콘텐츠가 실제로 비쳐 보인다.\n */\nfunction SkiaShimmer({ color, opacity = 0.6, duration = 1100 }: SkiaShimmerProps) {\n const [size, setSize] = useState({ width: 0, height: 0 });\n const progress = useSharedValue(0);\n\n // 밴드 폭·이동 거리는 실제 측정된 크기 기준 (기존 구현의 DEVICE_WIDTH 하드코딩 제거)\n const bandWidth = size.width * 0.65;\n const startOffset = bandWidth + size.height;\n const travel = size.width + bandWidth * 2 + size.height;\n\n const gradient = useMemo(() => {\n const rgb = toRgbHex(color);\n const peak = clamp01(opacity);\n if (!rgb) {\n // hex가 아니면 알파 조절이 불가하므로 색상 그대로 중앙에 사용\n return { colors: [\"transparent\", color, \"transparent\"], positions: [0, 0.5, 1] };\n }\n // 같은 hue로만 알파를 올렸다 내리는 종 모양 프로파일 → 경계선 없는 부드러운 광택\n return {\n colors: [\n withAlpha(rgb, 0),\n withAlpha(rgb, peak * 0.4),\n withAlpha(rgb, peak),\n withAlpha(rgb, peak * 0.4),\n withAlpha(rgb, 0),\n ],\n positions: [0, 0.3, 0.5, 0.7, 1],\n };\n }, [color, opacity]);\n\n useEffect(() => {\n if (size.width === 0) return;\n\n progress.value = 0;\n progress.value = withRepeat(\n withTiming(1, { duration, easing: Easing.inOut(Easing.ease) }),\n -1,\n false\n );\n\n return () => cancelAnimation(progress);\n }, [duration, size.width]);\n\n const gradientStart = useDerivedValue(() => {\n const x = -startOffset + travel * progress.value;\n return vec(x, 0);\n });\n const gradientEnd = useDerivedValue(() => {\n const x = -startOffset + travel * progress.value;\n return vec(x + bandWidth, size.height);\n });\n\n const handleLayout = (event: LayoutChangeEvent) => {\n const { width, height } = event.nativeEvent.layout;\n setSize((prev) => (prev.width === width && prev.height === height ? prev : { width, height }));\n };\n\n return (\n <View pointerEvents=\"none\" style={StyleSheet.absoluteFill} onLayout={handleLayout}>\n {size.width > 0 && size.height > 0 && (\n <Canvas style={StyleSheet.absoluteFill}>\n <Rect x={0} y={0} width={size.width} height={size.height}>\n <LinearGradient\n start={gradientStart}\n end={gradientEnd}\n colors={gradient.colors}\n positions={gradient.positions}\n />\n </Rect>\n </Canvas>\n )}\n </View>\n );\n}\n\nexport default React.memo(SkiaShimmer);\n"]}
@@ -1,12 +1,17 @@
1
1
  import React from "react";
2
2
  import { type StyleProp, type ViewStyle, type ViewProps } from "react-native";
3
- interface ZSSkeletonProps extends ViewProps {
3
+ export interface ZSSkeletonProps extends ViewProps {
4
4
  isFetching?: boolean;
5
5
  style?: StyleProp<ViewStyle>;
6
6
  children?: React.ReactNode;
7
+ /** shimmer 하이라이트 색상 (기본: palette.background.base) */
7
8
  overlayColor?: string;
9
+ /** shimmer 밴드 중심의 최대 불투명도 0~1 (기본 0.6) */
10
+ overlayOpacity?: number;
11
+ /** shimmer 한 사이클(ms) */
12
+ duration?: number;
8
13
  }
9
- declare function ZSSkeleton({ isFetching, style, children, overlayColor, ...props }: ZSSkeletonProps): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.JSX.Element | null | undefined;
14
+ declare function ZSSkeleton({ isFetching, style, children, overlayColor, overlayOpacity, duration, ...props }: ZSSkeletonProps): React.JSX.Element;
10
15
  declare const _default: React.MemoExoticComponent<typeof ZSSkeleton>;
11
16
  export default _default;
12
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/ZSSkeleton/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AACzC,OAAO,EAAc,KAAK,SAAS,EAAoB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAM5G,UAAU,eAAgB,SAAQ,SAAS;IACzC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,iBAAS,UAAU,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,mSAuC3F;;AAED,wBAAsC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/ZSSkeleton/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,KAAK,SAAS,EAAoB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAIhG,MAAM,WAAW,eAAgB,SAAQ,SAAS;IAChD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wBAAwB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,iBAAS,UAAU,CAAC,EAClB,UAAU,EACV,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,cAAoB,EACpB,QAAe,EACf,GAAG,KAAK,EACT,EAAE,eAAe,qBAYjB;;AAED,wBAAsC"}
@@ -1,54 +1,23 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React, { useEffect } from "react";
3
- import { Dimensions, StyleSheet, View } from "react-native";
4
- import Animated, { useSharedValue, useAnimatedStyle, withRepeat, withTiming, cancelAnimation } from "react-native-reanimated";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { StyleSheet, View } from "react-native";
5
4
  import { useTheme } from "../../context/ThemeContext";
6
- const DEVICE_WIDTH = Dimensions.get("window").width;
7
- function ZSSkeleton({ isFetching, style, children, overlayColor, ...props }) {
8
- const translateX = useSharedValue(-DEVICE_WIDTH * 1.2);
5
+ import SkiaShimmer from "./SkiaShimmer";
6
+ function ZSSkeleton({ isFetching, style, children, overlayColor, overlayOpacity = 0.6, duration = 1100, ...props }) {
9
7
  const { palette } = useTheme();
10
- const effectColor = overlayColor || palette.background.base;
11
- useEffect(() => {
12
- if (isFetching) {
13
- translateX.value = withRepeat(withTiming(DEVICE_WIDTH * 1.2, { duration: 800 }), -1, false);
14
- }
15
- else {
16
- cancelAnimation(translateX);
17
- translateX.value = -DEVICE_WIDTH * 1.2;
18
- }
19
- return () => {
20
- cancelAnimation(translateX);
21
- };
22
- }, [isFetching]);
23
- const animatedStyle = useAnimatedStyle(() => {
24
- return {
25
- transform: [{ translateX: translateX.value }],
26
- };
27
- });
28
- return isFetching ? (_jsx(View, { style: [style, { overflow: "hidden" }], ...props, children: _jsxs(View, { style: styles.container, children: [children, _jsxs(Animated.View, { style: [styles.shimmer, animatedStyle], children: [_jsx(View, { style: [styles.shimmerSub, { backgroundColor: effectColor }] }), _jsx(View, { style: [styles.shimmerCenter, { backgroundColor: effectColor }] }), _jsx(View, { style: [styles.shimmerSub, { backgroundColor: effectColor }] })] })] }) })) : (children);
8
+ const highlightColor = overlayColor || palette.background.base;
9
+ if (!isFetching)
10
+ return _jsx(_Fragment, { children: children });
11
+ return (_jsxs(View, { style: [style, styles.host], ...props, children: [_jsx(View, { style: styles.content, children: children }), _jsx(SkiaShimmer, { color: highlightColor, opacity: overlayOpacity, duration: duration })] }));
29
12
  }
30
13
  export default React.memo(ZSSkeleton);
31
14
  const styles = StyleSheet.create({
32
- container: {
15
+ host: {
33
16
  overflow: "hidden",
34
- opacity: 0.6,
35
- width: "100%",
36
17
  },
37
- shimmer: {
18
+ content: {
38
19
  width: "100%",
39
- height: "100%",
40
- position: "absolute",
41
- flexDirection: "row",
42
- },
43
- shimmerSub: {
44
- height: "100%",
45
- opacity: 0.3,
46
- width: DEVICE_WIDTH * 0.3,
47
- },
48
- shimmerCenter: {
49
- height: "100%",
50
- opacity: 0.6,
51
- width: DEVICE_WIDTH * 0.4,
20
+ opacity: 0.5,
52
21
  },
53
22
  });
54
23
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui/ZSSkeleton/index.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACzC,OAAO,EAAE,UAAU,EAAkB,UAAU,EAAE,IAAI,EAAkC,MAAM,cAAc,CAAC;AAC5G,OAAO,QAAQ,EAAE,EAAE,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC9H,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAEtD,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;AASpD,SAAS,UAAU,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,KAAK,EAAmB;IAC1F,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC;IACvD,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC/B,MAAM,WAAW,GAAG,YAAY,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;IAE5D,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU,EAAE,CAAC;YACf,UAAU,CAAC,KAAK,GAAG,UAAU,CAAC,UAAU,CAAC,YAAY,GAAG,GAAG,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC9F,CAAC;aAAM,CAAC;YACN,eAAe,CAAC,UAAU,CAAC,CAAC;YAC5B,UAAU,CAAC,KAAK,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC;QACzC,CAAC;QAED,OAAO,GAAG,EAAE;YACV,eAAe,CAAC,UAAU,CAAC,CAAC;QAC9B,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,MAAM,aAAa,GAAG,gBAAgB,CAAC,GAAG,EAAE;QAC1C,OAAO;YACL,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC;SAC9C,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC,CAAC,CAAC,CAClB,KAAC,IAAI,IAAC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,KAAM,KAAK,YACrD,MAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,SAAS,aAC1B,QAAQ,EAET,MAAC,QAAQ,CAAC,IAAI,IAAC,KAAK,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC,aACnD,KAAC,IAAI,IAAC,KAAK,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,eAAe,EAAE,WAAW,EAAE,CAAC,GAAI,EACtE,KAAC,IAAI,IAAC,KAAK,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,eAAe,EAAE,WAAW,EAAE,CAAC,GAAI,EACzE,KAAC,IAAI,IAAC,KAAK,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,eAAe,EAAE,WAAW,EAAE,CAAC,GAAI,IACxD,IACX,GACF,CACR,CAAC,CAAC,CAAC,CACF,QAAQ,CACT,CAAC;AACJ,CAAC;AAED,eAAe,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/B,SAAS,EAAE;QACT,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,GAAG;QACZ,KAAK,EAAE,MAAM;KACd;IACD,OAAO,EAAE;QACP,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,UAAU;QACpB,aAAa,EAAE,KAAK;KACrB;IACD,UAAU,EAAE;QACV,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,GAAG;QACZ,KAAK,EAAE,YAAY,GAAG,GAAG;KAC1B;IACD,aAAa,EAAE;QACb,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,GAAG;QACZ,KAAK,EAAE,YAAY,GAAG,GAAG;KAC1B;CACF,CAAC,CAAC","sourcesContent":["import React, { useEffect } from \"react\";\nimport { Dimensions, type StyleProp, StyleSheet, View, type ViewStyle, type ViewProps } from \"react-native\";\nimport Animated, { useSharedValue, useAnimatedStyle, withRepeat, withTiming, cancelAnimation } from \"react-native-reanimated\";\nimport { useTheme } from \"../../context/ThemeContext\";\n\nconst DEVICE_WIDTH = Dimensions.get(\"window\").width;\n\ninterface ZSSkeletonProps extends ViewProps {\n isFetching?: boolean;\n style?: StyleProp<ViewStyle>;\n children?: React.ReactNode;\n overlayColor?: string;\n}\n\nfunction ZSSkeleton({ isFetching, style, children, overlayColor, ...props }: ZSSkeletonProps) {\n const translateX = useSharedValue(-DEVICE_WIDTH * 1.2);\n const { palette } = useTheme();\n const effectColor = overlayColor || palette.background.base;\n\n useEffect(() => {\n if (isFetching) {\n translateX.value = withRepeat(withTiming(DEVICE_WIDTH * 1.2, { duration: 800 }), -1, false);\n } else {\n cancelAnimation(translateX);\n translateX.value = -DEVICE_WIDTH * 1.2;\n }\n\n return () => {\n cancelAnimation(translateX);\n };\n }, [isFetching]);\n\n const animatedStyle = useAnimatedStyle(() => {\n return {\n transform: [{ translateX: translateX.value }],\n };\n });\n\n return isFetching ? (\n <View style={[style, { overflow: \"hidden\" }]} {...props}>\n <View style={styles.container}>\n {children}\n\n <Animated.View style={[styles.shimmer, animatedStyle]}>\n <View style={[styles.shimmerSub, { backgroundColor: effectColor }]} />\n <View style={[styles.shimmerCenter, { backgroundColor: effectColor }]} />\n <View style={[styles.shimmerSub, { backgroundColor: effectColor }]} />\n </Animated.View>\n </View>\n </View>\n ) : (\n children\n );\n}\n\nexport default React.memo(ZSSkeleton);\n\nconst styles = StyleSheet.create({\n container: {\n overflow: \"hidden\",\n opacity: 0.6,\n width: \"100%\",\n },\n shimmer: {\n width: \"100%\",\n height: \"100%\",\n position: \"absolute\",\n flexDirection: \"row\",\n },\n shimmerSub: {\n height: \"100%\",\n opacity: 0.3,\n width: DEVICE_WIDTH * 0.3,\n },\n shimmerCenter: {\n height: \"100%\",\n opacity: 0.6,\n width: DEVICE_WIDTH * 0.4,\n },\n});\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui/ZSSkeleton/index.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAkB,UAAU,EAAE,IAAI,EAAkC,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,WAAW,MAAM,eAAe,CAAC;AAcxC,SAAS,UAAU,CAAC,EAClB,UAAU,EACV,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,cAAc,GAAG,GAAG,EACpB,QAAQ,GAAG,IAAI,EACf,GAAG,KAAK,EACQ;IAChB,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC/B,MAAM,cAAc,GAAG,YAAY,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;IAE/D,IAAI,CAAC,UAAU;QAAE,OAAO,4BAAG,QAAQ,GAAI,CAAC;IAExC,OAAO,CACL,MAAC,IAAI,IAAC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAM,KAAK,aAC1C,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,OAAO,YAAG,QAAQ,GAAQ,EAC9C,KAAC,WAAW,IAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,GAAI,IAC9E,CACR,CAAC;AACJ,CAAC;AAED,eAAe,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/B,IAAI,EAAE;QACJ,QAAQ,EAAE,QAAQ;KACnB;IACD,OAAO,EAAE;QACP,KAAK,EAAE,MAAM;QACb,OAAO,EAAE,GAAG;KACb;CACF,CAAC,CAAC","sourcesContent":["import React from \"react\";\nimport { type StyleProp, StyleSheet, View, type ViewStyle, type ViewProps } from \"react-native\";\nimport { useTheme } from \"../../context/ThemeContext\";\nimport SkiaShimmer from \"./SkiaShimmer\";\n\nexport interface ZSSkeletonProps extends ViewProps {\n isFetching?: boolean;\n style?: StyleProp<ViewStyle>;\n children?: React.ReactNode;\n /** shimmer 하이라이트 색상 (기본: palette.background.base) */\n overlayColor?: string;\n /** shimmer 밴드 중심의 최대 불투명도 0~1 (기본 0.6) */\n overlayOpacity?: number;\n /** shimmer 사이클(ms) */\n duration?: number;\n}\n\nfunction ZSSkeleton({\n isFetching,\n style,\n children,\n overlayColor,\n overlayOpacity = 0.6,\n duration = 1100,\n ...props\n}: ZSSkeletonProps) {\n const { palette } = useTheme();\n const highlightColor = overlayColor || palette.background.base;\n\n if (!isFetching) return <>{children}</>;\n\n return (\n <View style={[style, styles.host]} {...props}>\n <View style={styles.content}>{children}</View>\n <SkiaShimmer color={highlightColor} opacity={overlayOpacity} duration={duration} />\n </View>\n );\n}\n\nexport default React.memo(ZSSkeleton);\n\nconst styles = StyleSheet.create({\n host: {\n overflow: \"hidden\",\n },\n content: {\n width: \"100%\",\n opacity: 0.5,\n },\n});\n"]}
@@ -1,13 +1,19 @@
1
1
  import React from "react";
2
2
  import { type StyleProp, type ViewStyle, type ViewProps } from "react-native";
3
3
  import { ViewColorOptions } from "../../theme/types";
4
- interface ZSSkeletonBoxProps extends ViewProps {
4
+ export interface ZSSkeletonBoxProps extends ViewProps {
5
5
  height: number;
6
6
  style?: StyleProp<ViewStyle>;
7
+ /** shimmer 하이라이트 색상 (기본: palette.background.layer1) */
7
8
  overlayColor?: string;
9
+ /** shimmer 밴드 중심의 최대 불투명도 0~1 (기본 0.7) */
10
+ overlayOpacity?: number;
11
+ /** shimmer 한 사이클(ms) */
12
+ duration?: number;
13
+ /** 박스 배경 색상 토큰 */
8
14
  color?: ViewColorOptions;
9
15
  }
10
- declare function ZSSkeletonBox({ height, style, overlayColor, color, ...props }: ZSSkeletonBoxProps): React.JSX.Element;
16
+ declare function ZSSkeletonBox({ height, style, overlayColor, overlayOpacity, duration, color, ...props }: ZSSkeletonBoxProps): React.JSX.Element;
11
17
  declare const _default: React.MemoExoticComponent<typeof ZSSkeletonBox>;
12
18
  export default _default;
13
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/ZSSkeletonBox/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AACzC,OAAO,EAAc,KAAK,SAAS,EAAoB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAG5G,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAKrD,UAAU,kBAAmB,SAAQ,SAAS;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,gBAAgB,CAAC;CAC1B;AAED,iBAAS,aAAa,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,KAAiB,EAAE,GAAG,KAAK,EAAE,EAAE,kBAAkB,qBAuCtG;;AAED,wBAAyC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/ZSSkeletonBox/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,KAAK,SAAS,EAAoB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAEhG,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAIrD,MAAM,WAAW,kBAAmB,SAAQ,SAAS;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wBAAwB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,KAAK,CAAC,EAAE,gBAAgB,CAAC;CAC1B;AAED,iBAAS,aAAa,CAAC,EACrB,MAAM,EACN,KAAK,EACL,YAAY,EACZ,cAAoB,EACpB,QAAe,EACf,KAAiB,EACjB,GAAG,KAAK,EACT,EAAE,kBAAkB,qBAUpB;;AAED,wBAAyC"}
@@ -1,58 +1,18 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React, { useEffect } from "react";
3
- import { Dimensions, StyleSheet, View } from "react-native";
4
- import Animated, { useSharedValue, useAnimatedStyle, withRepeat, withTiming, cancelAnimation } from "react-native-reanimated";
2
+ import React from "react";
3
+ import { StyleSheet, View } from "react-native";
5
4
  import ZSView from "../ZSView";
6
5
  import { useTheme } from "../../context/ThemeContext";
7
- const DEVICE_WIDTH = Dimensions.get("window").width;
8
- function ZSSkeletonBox({ height, style, overlayColor, color = 'neutral', ...props }) {
9
- const translateX = useSharedValue(-DEVICE_WIDTH * 1.2);
6
+ import SkiaShimmer from "../ZSSkeleton/SkiaShimmer";
7
+ function ZSSkeletonBox({ height, style, overlayColor, overlayOpacity = 0.7, duration = 1100, color = "neutral", ...props }) {
10
8
  const { palette } = useTheme();
11
- const effectColor = overlayColor || palette.background.layer1;
12
- useEffect(() => {
13
- translateX.value = withRepeat(withTiming(DEVICE_WIDTH * 1.2, { duration: 800 }), -1, false);
14
- return () => {
15
- cancelAnimation(translateX);
16
- };
17
- }, []);
18
- const animatedStyle = useAnimatedStyle(() => {
19
- return {
20
- transform: [{ translateX: translateX.value }],
21
- };
22
- });
23
- return (_jsx(View, { style: [styles.fullWidth, style, { height, overflow: "hidden" }], ...props, children: _jsx(ZSView, { style: [
24
- styles.container,
25
- {
26
- width: DEVICE_WIDTH,
27
- height,
28
- },
29
- ], color: color, children: _jsxs(Animated.View, { style: [styles.shimmer, animatedStyle], children: [_jsx(View, { style: [styles.shimmerSub, { backgroundColor: effectColor }] }), _jsx(View, { style: [styles.shimmerCenter, { backgroundColor: effectColor }] }), _jsx(View, { style: [styles.shimmerSub, { backgroundColor: effectColor }] })] }) }) }));
9
+ const highlightColor = overlayColor || palette.background.layer1;
10
+ return (_jsxs(View, { style: [styles.fullWidth, style, { height, overflow: "hidden" }], ...props, children: [_jsx(ZSView, { style: StyleSheet.absoluteFill, color: color }), _jsx(SkiaShimmer, { color: highlightColor, opacity: overlayOpacity, duration: duration })] }));
30
11
  }
31
12
  export default React.memo(ZSSkeletonBox);
32
13
  const styles = StyleSheet.create({
33
14
  fullWidth: {
34
- width: '100%',
35
- },
36
- container: {
37
- overflow: "hidden",
38
- },
39
- shimmer: {
40
15
  width: "100%",
41
- height: "100%",
42
- position: "absolute",
43
- flexDirection: "row",
44
- },
45
- shimmerSub: {
46
- height: "100%",
47
- backgroundColor: "#ebebeb",
48
- opacity: 0.3,
49
- width: DEVICE_WIDTH * 0.3,
50
- },
51
- shimmerCenter: {
52
- height: "100%",
53
- backgroundColor: "#f5f5f5",
54
- opacity: 0.7,
55
- width: DEVICE_WIDTH * 0.4,
56
16
  },
57
17
  });
58
18
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui/ZSSkeletonBox/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useEffect } from \"react\";\nimport { Dimensions, type StyleProp, StyleSheet, View, type ViewStyle, type ViewProps } from \"react-native\";\nimport Animated, { useSharedValue, useAnimatedStyle, withRepeat, withTiming, cancelAnimation } from \"react-native-reanimated\";\nimport ZSView from \"../ZSView\";\nimport { ViewColorOptions } from \"../../theme/types\";\nimport { useTheme } from \"../../context/ThemeContext\";\n\nconst DEVICE_WIDTH = Dimensions.get(\"window\").width;\n\ninterface ZSSkeletonBoxProps extends ViewProps {\n height: number;\n style?: StyleProp<ViewStyle>;\n overlayColor?: string;\n color?: ViewColorOptions;\n}\n\nfunction ZSSkeletonBox({ height, style, overlayColor, color = 'neutral', ...props }: ZSSkeletonBoxProps) {\n const translateX = useSharedValue(-DEVICE_WIDTH * 1.2);\n const { palette } = useTheme();\n const effectColor = overlayColor || palette.background.layer1;\n\n useEffect(() => {\n translateX.value = withRepeat(withTiming(DEVICE_WIDTH * 1.2, { duration: 800 }), -1, false);\n\n return () => {\n cancelAnimation(translateX);\n };\n }, []);\n\n const animatedStyle = useAnimatedStyle(() => {\n return {\n transform: [{ translateX: translateX.value }],\n };\n });\n\n return (\n <View style={[styles.fullWidth, style, { height, overflow: \"hidden\" }]} {...props}>\n <ZSView\n style={[\n styles.container,\n {\n width: DEVICE_WIDTH,\n height,\n },\n ]}\n color={color}\n >\n <Animated.View style={[styles.shimmer, animatedStyle]}>\n <View style={[styles.shimmerSub, { backgroundColor: effectColor }]} />\n <View style={[styles.shimmerCenter, { backgroundColor: effectColor }]} />\n <View style={[styles.shimmerSub, { backgroundColor: effectColor }]} />\n </Animated.View>\n </ZSView>\n </View>\n );\n}\n\nexport default React.memo(ZSSkeletonBox);\n\nconst styles = StyleSheet.create({\n fullWidth: {\n width: '100%',\n },\n container: {\n overflow: \"hidden\",\n },\n shimmer: {\n width: \"100%\",\n height: \"100%\",\n position: \"absolute\",\n flexDirection: \"row\",\n },\n shimmerSub: {\n height: \"100%\",\n backgroundColor: \"#ebebeb\",\n opacity: 0.3,\n width: DEVICE_WIDTH * 0.3,\n },\n shimmerCenter: {\n height: \"100%\",\n backgroundColor: \"#f5f5f5\",\n opacity: 0.7,\n width: DEVICE_WIDTH * 0.4,\n },\n});\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui/ZSSkeletonBox/index.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAkB,UAAU,EAAE,IAAI,EAAkC,MAAM,cAAc,CAAC;AAChG,OAAO,MAAM,MAAM,WAAW,CAAC;AAE/B,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,WAAW,MAAM,2BAA2B,CAAC;AAepD,SAAS,aAAa,CAAC,EACrB,MAAM,EACN,KAAK,EACL,YAAY,EACZ,cAAc,GAAG,GAAG,EACpB,QAAQ,GAAG,IAAI,EACf,KAAK,GAAG,SAAS,EACjB,GAAG,KAAK,EACW;IACnB,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC/B,MAAM,cAAc,GAAG,YAAY,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC;IAEjE,OAAO,CACL,MAAC,IAAI,IAAC,KAAK,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,KAAM,KAAK,aAC/E,KAAC,MAAM,IAAC,KAAK,EAAE,UAAU,CAAC,YAAY,EAAE,KAAK,EAAE,KAAK,GAAI,EACxD,KAAC,WAAW,IAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,GAAI,IAC9E,CACR,CAAC;AACJ,CAAC;AAED,eAAe,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAEzC,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/B,SAAS,EAAE;QACT,KAAK,EAAE,MAAM;KACd;CACF,CAAC,CAAC","sourcesContent":["import React from \"react\";\nimport { type StyleProp, StyleSheet, View, type ViewStyle, type ViewProps } from \"react-native\";\nimport ZSView from \"../ZSView\";\nimport { ViewColorOptions } from \"../../theme/types\";\nimport { useTheme } from \"../../context/ThemeContext\";\nimport SkiaShimmer from \"../ZSSkeleton/SkiaShimmer\";\n\nexport interface ZSSkeletonBoxProps extends ViewProps {\n height: number;\n style?: StyleProp<ViewStyle>;\n /** shimmer 하이라이트 색상 (기본: palette.background.layer1) */\n overlayColor?: string;\n /** shimmer 밴드 중심의 최대 불투명도 0~1 (기본 0.7) */\n overlayOpacity?: number;\n /** shimmer 한 사이클(ms) */\n duration?: number;\n /** 박스 배경 색상 토큰 */\n color?: ViewColorOptions;\n}\n\nfunction ZSSkeletonBox({\n height,\n style,\n overlayColor,\n overlayOpacity = 0.7,\n duration = 1100,\n color = \"neutral\",\n ...props\n}: ZSSkeletonBoxProps) {\n const { palette } = useTheme();\n const highlightColor = overlayColor || palette.background.layer1;\n\n return (\n <View style={[styles.fullWidth, style, { height, overflow: \"hidden\" }]} {...props}>\n <ZSView style={StyleSheet.absoluteFill} color={color} />\n <SkiaShimmer color={highlightColor} opacity={overlayOpacity} duration={duration} />\n </View>\n );\n}\n\nexport default React.memo(ZSSkeletonBox);\n\nconst styles = StyleSheet.create({\n fullWidth: {\n width: \"100%\",\n },\n});\n"]}
@@ -8,8 +8,9 @@ interface ZSSwitchProps extends ViewProps {
8
8
  trackColorInactive?: string;
9
9
  trackColorActive?: string;
10
10
  thumbColor?: string;
11
+ disabled?: boolean;
11
12
  }
12
- declare function ZSSwitch({ isActive, onToggle, style, width, trackColorInactive, trackColorActive, thumbColor, ...props }: ZSSwitchProps): React.JSX.Element;
13
+ declare function ZSSwitch({ isActive, onToggle, style, width, trackColorInactive, trackColorActive, thumbColor, disabled, ...props }: ZSSwitchProps): React.JSX.Element;
13
14
  declare const _default: React.MemoExoticComponent<typeof ZSSwitch>;
14
15
  export default _default;
15
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/ZSSwitch/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAa,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAUzF,UAAU,aAAc,SAAQ,SAAS;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,iBAAS,QAAQ,CAAC,EAChB,QAAgB,EAChB,QAAQ,EACR,KAAK,EACL,KAAU,EACV,kBAAkB,EAClB,gBAAgB,EAChB,UAAsB,EACtB,GAAG,KAAK,EACT,EAAE,aAAa,qBAyCf;;AAED,wBAAoC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/ZSSwitch/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAa,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAYzF,UAAU,aAAc,SAAQ,SAAS;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,iBAAS,QAAQ,CAAC,EAChB,QAAgB,EAChB,QAAQ,EACR,KAAK,EACL,KAAU,EACV,kBAAkB,EAClB,gBAAgB,EAChB,UAAkB,EAClB,QAAgB,EAChB,GAAG,KAAK,EACT,EAAE,aAAa,qBAgDf;;AAED,wBAAoC"}
@@ -3,17 +3,19 @@ import React from "react";
3
3
  import { Pressable } from "react-native";
4
4
  import Animated from "react-native-reanimated";
5
5
  import { useTheme } from "../../context/ThemeContext";
6
- const SWITCH_TRANSITION_DURATION = 200;
6
+ import { WHITE } from "../../theme/primitive";
7
+ import { DISABLED_OPACITY, DURATION } from "../../theme/tokens";
8
+ const SWITCH_TRANSITION_DURATION = DURATION.base;
7
9
  const SWITCH_TRANSITION = {
8
10
  transitionDuration: SWITCH_TRANSITION_DURATION,
9
11
  transitionTimingFunction: 'ease-in-out',
10
12
  };
11
- function ZSSwitch({ isActive = false, onToggle, style, width = 50, trackColorInactive, trackColorActive, thumbColor = '#ffffff', ...props }) {
13
+ function ZSSwitch({ isActive = false, onToggle, style, width = 50, trackColorInactive, trackColorActive, thumbColor = WHITE, disabled = false, ...props }) {
12
14
  const { palette } = useTheme();
13
15
  const height = width * 0.6;
14
16
  const padding = width * 0.04;
15
17
  const thumbSize = height - padding * 2;
16
- const toggleBorderRadius = height * 1.2;
18
+ const toggleBorderRadius = height / 2;
17
19
  const thumbBorderRadius = thumbSize / 2;
18
20
  const inactiveColor = trackColorInactive ?? palette.grey[30];
19
21
  const activeColor = trackColorActive ?? palette.primary.main;
@@ -38,7 +40,7 @@ function ZSSwitch({ isActive = false, onToggle, style, width = 50, trackColorIna
38
40
  transitionProperty: 'transform',
39
41
  ...SWITCH_TRANSITION,
40
42
  };
41
- return (_jsx(Pressable, { onPress: onToggle, style: style, ...props, children: _jsx(Animated.View, { style: toggleStyle, children: _jsx(Animated.View, { style: thumbStyle }) }) }));
43
+ return (_jsx(Pressable, { onPress: onToggle, style: [style, disabled && { opacity: DISABLED_OPACITY }], disabled: disabled, accessibilityRole: 'switch', accessibilityState: { checked: isActive, disabled }, ...props, children: _jsx(Animated.View, { style: toggleStyle, children: _jsx(Animated.View, { style: thumbStyle }) }) }));
42
44
  }
43
45
  export default React.memo(ZSSwitch);
44
46
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui/ZSSwitch/index.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAkC,SAAS,EAAkB,MAAM,cAAc,CAAC;AACzF,OAAO,QAAQ,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAEtD,MAAM,0BAA0B,GAAG,GAAG,CAAC;AACvC,MAAM,iBAAiB,GAAG;IACxB,kBAAkB,EAAE,0BAA0B;IAC9C,wBAAwB,EAAE,aAAa;CAC/B,CAAC;AAYX,SAAS,QAAQ,CAAC,EAChB,QAAQ,GAAG,KAAK,EAChB,QAAQ,EACR,KAAK,EACL,KAAK,GAAG,EAAE,EACV,kBAAkB,EAClB,gBAAgB,EAChB,UAAU,GAAG,SAAS,EACtB,GAAG,KAAK,EACM;IACd,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,KAAK,GAAG,GAAG,CAAC;IAC3B,MAAM,OAAO,GAAG,KAAK,GAAG,IAAI,CAAC;IAC7B,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC;IACvC,MAAM,kBAAkB,GAAG,MAAM,GAAG,GAAG,CAAC;IACxC,MAAM,iBAAiB,GAAG,SAAS,GAAG,CAAC,CAAC;IAExC,MAAM,aAAa,GAAG,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;IAC7D,MAAM,eAAe,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,SAAS,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvE,MAAM,WAAW,GAAG;QAClB,KAAK;QACL,MAAM;QACN,YAAY,EAAE,kBAAkB;QAChC,OAAO;QACP,cAAc,EAAE,QAAiB;QACjC,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa;QACvD,kBAAkB,EAAE,iBAA0B;QAC9C,GAAG,iBAAiB;KACrB,CAAC;IAEF,MAAM,UAAU,GAAG;QACjB,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,iBAAiB;QAC/B,cAAc,EAAE,QAAiB;QACjC,eAAe,EAAE,UAAU;QAC3B,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC;QAC5C,kBAAkB,EAAE,WAAoB;QACxC,GAAG,iBAAiB;KACrB,CAAC;IAEF,OAAO,CACL,KAAC,SAAS,IAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,KAAM,KAAK,YACnD,KAAC,QAAQ,CAAC,IAAI,IAAC,KAAK,EAAE,WAAW,YAC/B,KAAC,QAAQ,CAAC,IAAI,IAAC,KAAK,EAAE,UAAU,GAAI,GACtB,GACN,CACb,CAAC;AACJ,CAAC;AAED,eAAe,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC","sourcesContent":["import React from \"react\";\nimport { type StyleProp, type ViewStyle, Pressable, type ViewProps } from \"react-native\";\nimport Animated from \"react-native-reanimated\";\nimport { useTheme } from \"../../context/ThemeContext\";\n\nconst SWITCH_TRANSITION_DURATION = 200;\nconst SWITCH_TRANSITION = {\n transitionDuration: SWITCH_TRANSITION_DURATION,\n transitionTimingFunction: 'ease-in-out',\n} as const;\n\ninterface ZSSwitchProps extends ViewProps {\n isActive: boolean;\n onToggle: () => void;\n style?: StyleProp<ViewStyle>;\n width?: number;\n trackColorInactive?: string;\n trackColorActive?: string;\n thumbColor?: string;\n}\n\nfunction ZSSwitch({\n isActive = false,\n onToggle,\n style,\n width = 50,\n trackColorInactive,\n trackColorActive,\n thumbColor = '#ffffff',\n ...props\n}: ZSSwitchProps) {\n const { palette } = useTheme();\n const height = width * 0.6;\n const padding = width * 0.04;\n const thumbSize = height - padding * 2;\n const toggleBorderRadius = height * 1.2;\n const thumbBorderRadius = thumbSize / 2;\n\n const inactiveColor = trackColorInactive ?? palette.grey[30];\n const activeColor = trackColorActive ?? palette.primary.main;\n const thumbTranslateX = isActive ? width - thumbSize - padding * 2 : 0;\n\n const toggleStyle = {\n width,\n height,\n borderRadius: toggleBorderRadius,\n padding,\n justifyContent: \"center\" as const,\n backgroundColor: isActive ? activeColor : inactiveColor,\n transitionProperty: 'backgroundColor' as const,\n ...SWITCH_TRANSITION,\n };\n\n const thumbStyle = {\n width: thumbSize,\n height: thumbSize,\n borderRadius: thumbBorderRadius,\n justifyContent: \"center\" as const,\n backgroundColor: thumbColor,\n transform: [{ translateX: thumbTranslateX }],\n transitionProperty: 'transform' as const,\n ...SWITCH_TRANSITION,\n };\n\n return (\n <Pressable onPress={onToggle} style={style} {...props}>\n <Animated.View style={toggleStyle}>\n <Animated.View style={thumbStyle} />\n </Animated.View>\n </Pressable>\n );\n}\n\nexport default React.memo(ZSSwitch);\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui/ZSSwitch/index.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAkC,SAAS,EAAkB,MAAM,cAAc,CAAC;AACzF,OAAO,QAAQ,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEhE,MAAM,0BAA0B,GAAG,QAAQ,CAAC,IAAI,CAAC;AACjD,MAAM,iBAAiB,GAAG;IACxB,kBAAkB,EAAE,0BAA0B;IAC9C,wBAAwB,EAAE,aAAa;CAC/B,CAAC;AAaX,SAAS,QAAQ,CAAC,EAChB,QAAQ,GAAG,KAAK,EAChB,QAAQ,EACR,KAAK,EACL,KAAK,GAAG,EAAE,EACV,kBAAkB,EAClB,gBAAgB,EAChB,UAAU,GAAG,KAAK,EAClB,QAAQ,GAAG,KAAK,EAChB,GAAG,KAAK,EACM;IACd,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,KAAK,GAAG,GAAG,CAAC;IAC3B,MAAM,OAAO,GAAG,KAAK,GAAG,IAAI,CAAC;IAC7B,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC;IACvC,MAAM,kBAAkB,GAAG,MAAM,GAAG,CAAC,CAAC;IACtC,MAAM,iBAAiB,GAAG,SAAS,GAAG,CAAC,CAAC;IAExC,MAAM,aAAa,GAAG,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;IAC7D,MAAM,eAAe,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,SAAS,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvE,MAAM,WAAW,GAAG;QAClB,KAAK;QACL,MAAM;QACN,YAAY,EAAE,kBAAkB;QAChC,OAAO;QACP,cAAc,EAAE,QAAiB;QACjC,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa;QACvD,kBAAkB,EAAE,iBAA0B;QAC9C,GAAG,iBAAiB;KACrB,CAAC;IAEF,MAAM,UAAU,GAAG;QACjB,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,iBAAiB;QAC/B,cAAc,EAAE,QAAiB;QACjC,eAAe,EAAE,UAAU;QAC3B,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC;QAC5C,kBAAkB,EAAE,WAAoB;QACxC,GAAG,iBAAiB;KACrB,CAAC;IAEF,OAAO,CACL,KAAC,SAAS,IACR,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,CAAC,KAAK,EAAE,QAAQ,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,EACzD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAC,QAAQ,EAC1B,kBAAkB,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAC/C,KAAK,YAET,KAAC,QAAQ,CAAC,IAAI,IAAC,KAAK,EAAE,WAAW,YAC/B,KAAC,QAAQ,CAAC,IAAI,IAAC,KAAK,EAAE,UAAU,GAAI,GACtB,GACN,CACb,CAAC;AACJ,CAAC;AAED,eAAe,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC","sourcesContent":["import React from \"react\";\nimport { type StyleProp, type ViewStyle, Pressable, type ViewProps } from \"react-native\";\nimport Animated from \"react-native-reanimated\";\nimport { useTheme } from \"../../context/ThemeContext\";\nimport { WHITE } from \"../../theme/primitive\";\nimport { DISABLED_OPACITY, DURATION } from \"../../theme/tokens\";\n\nconst SWITCH_TRANSITION_DURATION = DURATION.base;\nconst SWITCH_TRANSITION = {\n transitionDuration: SWITCH_TRANSITION_DURATION,\n transitionTimingFunction: 'ease-in-out',\n} as const;\n\ninterface ZSSwitchProps extends ViewProps {\n isActive: boolean;\n onToggle: () => void;\n style?: StyleProp<ViewStyle>;\n width?: number;\n trackColorInactive?: string;\n trackColorActive?: string;\n thumbColor?: string;\n disabled?: boolean;\n}\n\nfunction ZSSwitch({\n isActive = false,\n onToggle,\n style,\n width = 50,\n trackColorInactive,\n trackColorActive,\n thumbColor = WHITE,\n disabled = false,\n ...props\n}: ZSSwitchProps) {\n const { palette } = useTheme();\n const height = width * 0.6;\n const padding = width * 0.04;\n const thumbSize = height - padding * 2;\n const toggleBorderRadius = height / 2;\n const thumbBorderRadius = thumbSize / 2;\n\n const inactiveColor = trackColorInactive ?? palette.grey[30];\n const activeColor = trackColorActive ?? palette.primary.main;\n const thumbTranslateX = isActive ? width - thumbSize - padding * 2 : 0;\n\n const toggleStyle = {\n width,\n height,\n borderRadius: toggleBorderRadius,\n padding,\n justifyContent: \"center\" as const,\n backgroundColor: isActive ? activeColor : inactiveColor,\n transitionProperty: 'backgroundColor' as const,\n ...SWITCH_TRANSITION,\n };\n\n const thumbStyle = {\n width: thumbSize,\n height: thumbSize,\n borderRadius: thumbBorderRadius,\n justifyContent: \"center\" as const,\n backgroundColor: thumbColor,\n transform: [{ translateX: thumbTranslateX }],\n transitionProperty: 'transform' as const,\n ...SWITCH_TRANSITION,\n };\n\n return (\n <Pressable\n onPress={onToggle}\n style={[style, disabled && { opacity: DISABLED_OPACITY }]}\n disabled={disabled}\n accessibilityRole='switch'\n accessibilityState={{ checked: isActive, disabled }}\n {...props}\n >\n <Animated.View style={toggleStyle}>\n <Animated.View style={thumbStyle} />\n </Animated.View>\n </Pressable>\n );\n}\n\nexport default React.memo(ZSSwitch);\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/ZSTextField/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAA0C,SAAS,EAAE,cAAc,EAAa,MAAM,cAAc,CAAC;AAI5G,OAAO,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC;AAKzE,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;AAMzD,UAAU,cAAc;IACtB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,aAAa,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC;AAEvC,QAAA,MAAM,WAAW,sGA8Qf,CAAC;AAIH,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/ZSTextField/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAA0C,SAAS,EAAE,cAAc,EAAa,MAAM,cAAc,CAAC;AAI5G,OAAO,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC;AAMzE,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;AAMzD,UAAU,cAAc;IACtB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,aAAa,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC;AAEvC,QAAA,MAAM,WAAW,sGAkRf,CAAC;AAIH,eAAe,WAAW,CAAC"}