blurview
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
import { BlurView } from "expo-blur";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
FlatList,
|
||||
Image,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
@@ -21,6 +19,7 @@ import {
|
||||
OPTIONAL_STRUCTURE_SEGMENTS,
|
||||
sanitizeStructureList,
|
||||
} from "../../utils/songStructure";
|
||||
import ItemContainer from "../../components/ItemContainer/ItemContainer";
|
||||
import CreateLyricsHeader from "./components/CreateLyricsHeader";
|
||||
|
||||
const randomSuffix = () => Math.random().toString(36).slice(2, 8);
|
||||
@@ -138,66 +137,56 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
||||
);
|
||||
|
||||
const Row = ({ item: rowData, onPress }) => {
|
||||
const isButton = typeof onPress === "function";
|
||||
const Wrapper = isButton ? TouchableOpacity : View;
|
||||
const selected = rowData?.selected;
|
||||
const icon = rowData?.icon;
|
||||
const baseType = rowData?.type || rowData?.value;
|
||||
const meta = getSegmentMeta(baseType);
|
||||
const canRemove = rowData?.sortable && meta?.optional === true;
|
||||
const selected = rowData?.selected;
|
||||
const count = rowData?.count ?? 0;
|
||||
const containerStyle = StyleSheet.flatten([
|
||||
styles.rowContainer,
|
||||
]);
|
||||
const blurStyle = StyleSheet.flatten([
|
||||
styles.rowBlur,
|
||||
selected ? styles.selectedBlur : null,
|
||||
]);
|
||||
const badgeStyle = StyleSheet.flatten([
|
||||
styles.countBadge,
|
||||
count > 0 ? styles.countBadgeActive : null,
|
||||
]);
|
||||
|
||||
return (
|
||||
<Wrapper
|
||||
<CreateLyricsHeader
|
||||
tint="dark"
|
||||
intensity={40}
|
||||
showBorder={false}
|
||||
onPress={onPress}
|
||||
activeOpacity={0.8}
|
||||
style={isButton ? styles.touchable : undefined}
|
||||
containerStyle={containerStyle}
|
||||
blurViewStyle={blurStyle}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
styles.itemContainer,
|
||||
selected && styles.selectedItem,
|
||||
rowData?.sortable && styles.sortableItem,
|
||||
]}
|
||||
>
|
||||
<BlurView
|
||||
intensity={Platform.OS !== "ios" ? 10 : 30}
|
||||
style={[
|
||||
styles.rowBlur,
|
||||
selected && styles.selectedBlur,
|
||||
isButton && styles.buttonBlur,
|
||||
]}
|
||||
>
|
||||
<View style={styles.rowContent}>
|
||||
<Text style={styles.rowText}>{rowData?.label || ""}</Text>
|
||||
<View style={styles.rowContent}>
|
||||
<Text style={styles.rowText}>{rowData?.label || ""}</Text>
|
||||
{rowData?.sortable ? (
|
||||
<View style={styles.rowActions}>
|
||||
<Sortable.Handle>
|
||||
<Image source={icons.dragDots} style={styles.handleIcon} />
|
||||
</Sortable.Handle>
|
||||
{canRemove && (
|
||||
<TouchableOpacity
|
||||
onPress={() => handleRemoveItem(rowData.id)}
|
||||
style={styles.removeButton}
|
||||
hitSlop={{ top: 8, right: 8, bottom: 8, left: 8 }}
|
||||
>
|
||||
<Image source={icons.close} style={styles.removeIcon} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
{rowData?.sortable ? (
|
||||
<View style={styles.rowActions}>
|
||||
<Sortable.Handle>
|
||||
<Image source={icons.dragDots} style={styles.handleIcon} />
|
||||
</Sortable.Handle>
|
||||
{canRemove && (
|
||||
<TouchableOpacity
|
||||
onPress={() => handleRemoveItem(rowData.id)}
|
||||
style={styles.removeButton}
|
||||
hitSlop={{ top: 8, right: 8, bottom: 8, left: 8 }}
|
||||
>
|
||||
<Image source={icons.close} style={styles.removeIcon} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
) : (
|
||||
<View
|
||||
style={[
|
||||
styles.countBadge,
|
||||
count > 0 ? styles.countBadgeActive : null,
|
||||
]}
|
||||
>
|
||||
<Text style={styles.countBadgeText}>{count}</Text>
|
||||
</View>
|
||||
)}
|
||||
</BlurView>
|
||||
) : (
|
||||
<View style={badgeStyle}>
|
||||
<Text style={styles.countBadgeText}>{count}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</Wrapper>
|
||||
</CreateLyricsHeader>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -216,47 +205,60 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
||||
<View style={styles.sectionTag}>
|
||||
<Text style={styles.sectionTagText}>Structure actuelle</Text>
|
||||
</View>
|
||||
<Sortable.Grid
|
||||
columns={1}
|
||||
rowGap={12}
|
||||
data={labeledItems}
|
||||
renderItem={({ item }) => <Row item={item} />}
|
||||
customHandle
|
||||
scrollableRef={scrollRef}
|
||||
onDragEnd={({ data: newData }) => {
|
||||
const values = extractValues(newData);
|
||||
const sanitized = sanitizeStructureList(values);
|
||||
setSortableItems(buildSortableItems(sanitized));
|
||||
}}
|
||||
/>
|
||||
<ItemContainer
|
||||
height="auto"
|
||||
disableKeyboardHeight
|
||||
style={styles.blurSection}
|
||||
>
|
||||
<Sortable.Grid
|
||||
columns={1}
|
||||
rowGap={12}
|
||||
data={labeledItems}
|
||||
renderItem={({ item }) => <Row item={item} />}
|
||||
customHandle
|
||||
scrollableRef={scrollRef}
|
||||
onDragEnd={({ data: newData }) => {
|
||||
const values = extractValues(newData);
|
||||
const sanitized = sanitizeStructureList(values);
|
||||
setSortableItems(buildSortableItems(sanitized));
|
||||
}}
|
||||
/>
|
||||
</ItemContainer>
|
||||
<View style={[styles.sectionTag, { marginTop: 24 }]}>
|
||||
<Text style={styles.sectionTagText}>Éléments à ajouter</Text>
|
||||
</View>
|
||||
<FlatList
|
||||
data={optionalSegments}
|
||||
gap={12}
|
||||
renderItem={({ item }) => {
|
||||
const count = segmentCounts[item.type] || 0;
|
||||
const selected = count > 0;
|
||||
return (
|
||||
<View style={{ marginTop: 12 }}>
|
||||
<Row
|
||||
item={{
|
||||
...item,
|
||||
sortable: false,
|
||||
selected,
|
||||
count,
|
||||
}}
|
||||
onPress={() => handleOptionalSegmentPress(item.type)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}}
|
||||
keyExtractor={(item) => item.type}
|
||||
contentContainerStyle={{ paddingBottom: 24 }}
|
||||
scrollEnabled={false}
|
||||
nestedScrollEnabled={false}
|
||||
/>
|
||||
<ItemContainer
|
||||
height="auto"
|
||||
disableKeyboardHeight
|
||||
style={styles.blurSection}
|
||||
>
|
||||
<FlatList
|
||||
data={optionalSegments}
|
||||
gap={12}
|
||||
renderItem={({ item, index }) => {
|
||||
const count = segmentCounts[item.type] || 0;
|
||||
const selected = count > 0;
|
||||
const wrapperStyle = index === 0 ? null : styles.optionWrapper;
|
||||
return (
|
||||
<View style={wrapperStyle}>
|
||||
<Row
|
||||
item={{
|
||||
...item,
|
||||
sortable: false,
|
||||
selected,
|
||||
count,
|
||||
}}
|
||||
onPress={() => handleOptionalSegmentPress(item.type)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}}
|
||||
keyExtractor={(item) => item.type}
|
||||
contentContainerStyle={{ paddingBottom: 24 }}
|
||||
scrollEnabled={false}
|
||||
nestedScrollEnabled={false}
|
||||
/>
|
||||
</ItemContainer>
|
||||
</Animated.ScrollView>
|
||||
</View>
|
||||
</View>
|
||||
@@ -266,40 +268,19 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
||||
export default CustomizeSongStructure;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
touchable: {
|
||||
rowContainer: {
|
||||
borderRadius: 14,
|
||||
width: "100%",
|
||||
},
|
||||
itemContainer: {
|
||||
height: 54,
|
||||
backgroundColor: Palette.glass,
|
||||
borderRadius: 14,
|
||||
overflow: "hidden",
|
||||
shadowColor: "#00000040",
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 3.84,
|
||||
elevation: 5,
|
||||
},
|
||||
sortableItem: {
|
||||
marginBottom: 0,
|
||||
},
|
||||
selectedItem: {
|
||||
borderWidth: 1,
|
||||
borderColor: Palette.white,
|
||||
blurSection: {
|
||||
marginTop: 12,
|
||||
width: "100%",
|
||||
},
|
||||
rowBlur: {
|
||||
flex: 1,
|
||||
backgroundColor: Palette.glass,
|
||||
justifyContent: "center",
|
||||
paddingHorizontal: 12,
|
||||
minHeight: 54,
|
||||
paddingHorizontal: 16,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
height: "100%",
|
||||
},
|
||||
buttonBlur: {
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
selectedBlur: {
|
||||
@@ -307,7 +288,9 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
rowContent: {
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
rowText: {
|
||||
fontSize: 16,
|
||||
@@ -320,11 +303,6 @@ const styles = StyleSheet.create({
|
||||
tintColor: Palette.white,
|
||||
marginRight: 12,
|
||||
},
|
||||
optionIcon: {
|
||||
width: 20,
|
||||
height: 20,
|
||||
tintColor: Palette.white,
|
||||
},
|
||||
countBadge: {
|
||||
minWidth: 28,
|
||||
paddingHorizontal: 8,
|
||||
@@ -356,6 +334,9 @@ const styles = StyleSheet.create({
|
||||
height: 16,
|
||||
tintColor: Palette.white,
|
||||
},
|
||||
optionWrapper: {
|
||||
marginTop: 12,
|
||||
},
|
||||
sectionTag: {
|
||||
alignSelf: "flex-start",
|
||||
paddingHorizontal: 12,
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
OPTIONAL_STRUCTURE_SEGMENTS,
|
||||
sanitizeStructureList,
|
||||
} from "../../utils/songStructure";
|
||||
import ItemContainer from "../../components/ItemContainer/ItemContainer";
|
||||
import CreateLyricsHeader from "./components/CreateLyricsHeader";
|
||||
|
||||
const ROW_HEIGHT = 54;
|
||||
@@ -222,12 +223,22 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
||||
const baseType = item?.type || item?.value;
|
||||
const meta = getSegmentMeta(baseType);
|
||||
const canRemove = meta?.optional === true;
|
||||
const containerStyle = StyleSheet.flatten([
|
||||
styles.rowContainer,
|
||||
activeItemId === item.id ? styles.activeItem : null,
|
||||
]);
|
||||
const blurStyle = StyleSheet.flatten([
|
||||
styles.itemBlur,
|
||||
activeItemId === item.id ? styles.activeBlur : null,
|
||||
]);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.itemContainer,
|
||||
activeItemId === item.id && styles.activeItem,
|
||||
]}
|
||||
<CreateLyricsHeader
|
||||
tint="dark"
|
||||
intensity={40}
|
||||
showBorder={false}
|
||||
containerStyle={containerStyle}
|
||||
blurViewStyle={blurStyle}
|
||||
>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.rowText}>{item.label}</Text>
|
||||
@@ -253,7 +264,7 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</CreateLyricsHeader>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -271,38 +282,59 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
||||
<View style={styles.sectionTag}>
|
||||
<Text style={styles.sectionTagText}>Structure actuelle</Text>
|
||||
</View>
|
||||
<View style={styles.listContainer} ref={containerRef}>
|
||||
{labeledItems.map((item) => (
|
||||
<StructureRow key={item.id} item={item} />
|
||||
))}
|
||||
</View>
|
||||
<ItemContainer
|
||||
height="auto"
|
||||
disableKeyboardHeight
|
||||
style={styles.blurSection}
|
||||
>
|
||||
<View style={styles.listContainer} ref={containerRef}>
|
||||
{labeledItems.map((item) => (
|
||||
<StructureRow key={item.id} item={item} />
|
||||
))}
|
||||
</View>
|
||||
</ItemContainer>
|
||||
<View style={[styles.sectionTag, styles.addSectionTag]}>
|
||||
<Text style={styles.sectionTagText}>Éléments à ajouter</Text>
|
||||
</View>
|
||||
<View style={styles.optionsContainer}>
|
||||
{optionalSegments.map((segment) => {
|
||||
const count = segmentCounts[segment.type] || 0;
|
||||
const selected = count > 0;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={segment.type}
|
||||
activeOpacity={0.85}
|
||||
onPress={() => handleOptionalSegmentPress(segment.type)}
|
||||
style={[styles.optionItem, selected && styles.selectedItem]}
|
||||
>
|
||||
<Text style={styles.rowText}>{segment.label}</Text>
|
||||
<View
|
||||
style={[
|
||||
styles.countBadge,
|
||||
selected && styles.countBadgeActive,
|
||||
]}
|
||||
<ItemContainer
|
||||
height="auto"
|
||||
disableKeyboardHeight
|
||||
style={styles.blurSection}
|
||||
>
|
||||
<View style={styles.optionsContainer}>
|
||||
{optionalSegments.map((segment) => {
|
||||
const count = segmentCounts[segment.type] || 0;
|
||||
const selected = count > 0;
|
||||
const blurStyle = StyleSheet.flatten([
|
||||
styles.itemBlur,
|
||||
selected ? styles.selectedBlur : null,
|
||||
]);
|
||||
const badgeStyle = StyleSheet.flatten([
|
||||
styles.countBadge,
|
||||
selected ? styles.countBadgeActive : null,
|
||||
]);
|
||||
|
||||
return (
|
||||
<CreateLyricsHeader
|
||||
key={segment.type}
|
||||
tint="dark"
|
||||
intensity={40}
|
||||
showBorder={false}
|
||||
onPress={() => handleOptionalSegmentPress(segment.type)}
|
||||
containerStyle={styles.rowContainer}
|
||||
blurViewStyle={blurStyle}
|
||||
>
|
||||
<Text style={styles.countBadgeText}>{count}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.rowText}>{segment.label}</Text>
|
||||
<View style={badgeStyle}>
|
||||
<Text style={styles.countBadgeText}>{count}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</CreateLyricsHeader>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</ItemContainer>
|
||||
</ScrollView>
|
||||
</View>
|
||||
</View>
|
||||
@@ -337,24 +369,32 @@ const styles = StyleSheet.create({
|
||||
listContainer: {
|
||||
gap: 12,
|
||||
},
|
||||
itemContainer: {
|
||||
height: ROW_HEIGHT,
|
||||
blurSection: {
|
||||
marginTop: 12,
|
||||
width: "100%",
|
||||
},
|
||||
rowContainer: {
|
||||
borderRadius: 14,
|
||||
backgroundColor: Palette.glass,
|
||||
shadowColor: "#00000040",
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 3.84,
|
||||
elevation: 5,
|
||||
overflow: "hidden",
|
||||
},
|
||||
activeItem: {
|
||||
borderWidth: 1,
|
||||
borderColor: Palette.white,
|
||||
},
|
||||
itemBlur: {
|
||||
minHeight: ROW_HEIGHT,
|
||||
paddingHorizontal: 16,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
activeBlur: {
|
||||
backgroundColor: Palette.transparentWhite,
|
||||
},
|
||||
selectedBlur: {
|
||||
backgroundColor: Palette.transparentWhite,
|
||||
},
|
||||
row: {
|
||||
flex: 1,
|
||||
paddingHorizontal: 16,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
@@ -391,26 +431,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
optionsContainer: {
|
||||
gap: 12,
|
||||
marginTop: 12,
|
||||
},
|
||||
optionItem: {
|
||||
height: ROW_HEIGHT,
|
||||
borderRadius: 14,
|
||||
backgroundColor: Palette.glass,
|
||||
shadowColor: "#00000040",
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 3.84,
|
||||
elevation: 5,
|
||||
overflow: "hidden",
|
||||
paddingHorizontal: 16,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
selectedItem: {
|
||||
borderWidth: 1,
|
||||
borderColor: Palette.white,
|
||||
},
|
||||
countBadge: {
|
||||
minWidth: 28,
|
||||
|
||||
Reference in New Issue
Block a user