Skip to content

Commit

Permalink
feat(carousel-native): association
Browse files Browse the repository at this point in the history
  • Loading branch information
Andries-Smit committed Nov 27, 2023
1 parent 9d2a729 commit d60c596
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { RowLayoutProps, StructurePreviewProps, topBar } from "@mendix/piw-utils-internal";
import { hidePropertiesIn, Properties } from "@mendix/pluggable-widgets-tools";

import paginationSVG from "./assets/pagination.svg";

Expand Down Expand Up @@ -37,3 +38,11 @@ export function getPreview(values: CarouselPreviewProps, isDarkMode: boolean): S

return topBar("Carousel", content, isDarkMode);
}

export function getProperties(values: CarouselPreviewProps, defaultProperties: Properties): Properties {
if (!values.activeSelection) {
hidePropertiesIn(defaultProperties, values, ["onChangeAction", "animateExpression"]);
}

return defaultProperties;
}
64 changes: 57 additions & 7 deletions packages/pluggableWidgets/carousel-native/src/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,67 @@ export const Carousel = (props: CarouselProps<CarouselStyle>): ReactElement => {

const [activeSlide, setActiveSlide] = useState(0);

const [firstItem, setFirstItem] = useState(0);

const [loading, setLoading] = useState(true);

useEffect(() => {
if (props.contentSource?.status === ValueStatus.Available) {
if (props.contentSource?.status === ValueStatus.Available && loading) {
// Set initial index of the first item to show the associated active selection.
const index =
(props.activeSelection?.value
? props.contentSource?.items?.findIndex(i => i.id === props.activeSelection?.value?.id)
: 0) ?? 0;
setFirstItem(index);
setActiveSlide(index);
setLoading(false);
}
}, [props.contentSource]);
}, [loading, props.activeSelection, props.contentSource]);

const onSnap = useCallback((index: number) => {
setActiveSlide(index);
}, []);
useEffect(() => {
if (carouselRef && props.activeSelection) {
let index = props.contentSource.items?.findIndex(i => i.id === props.activeSelection?.value?.id) ?? 0;
// Removed item that is active selection can not be found
index = index >= 0 ? index : 0;
// Should check carouselRef.currentIndex though this is not fast enough for update.
if (index !== activeSlide) {
// Update carousel when associated item is changed
setActiveSlide(index);
const animate = props.animateExpression?.value ?? true;
// Async snap to index, use case add item is added before current selected
setTimeout(() => {
(carouselRef as NativeCarousel<ObjectItem>).snapToItem(index, animate);
}, 1);
}
}
}, [activeSlide, carouselRef, props.activeSelection, props.animateExpression, props.contentSource]);

useEffect(() => {
if (props.activeSelection) {
// Check if selected item is still available, reset to index 0 or null
let item = props.contentSource.items?.find(i => i.id === props.activeSelection?.value?.id);
if (item == null) {
item = props.contentSource.items?.[0];
}
if (props.activeSelection.value?.id !== item?.id) {
// Set association when empty to first slide
props.activeSelection.setValue(item);
}
}
}, [props.activeSelection, props.contentSource]);

const onSnap = useCallback(
(index: number) => {
setActiveSlide(index);
if (props.activeSelection) {
const item = props.contentSource?.items?.[index];
if (item?.id !== props.activeSelection.value?.id) {
props.activeSelection.setValue(item);
}
}
},
[props.activeSelection, props.contentSource]
);

const renderItem = useCallback(({ item, index }: { item: ObjectItem; index: number }) => {
const viewStyle = layoutSpecificStyle.slideItem;
Expand Down Expand Up @@ -97,7 +147,7 @@ export const Carousel = (props: CarouselProps<CarouselStyle>): ReactElement => {
);
}, [activeSlide, carouselRef, props.contentSource, props.showPagination]);

const onLayout = (event: LayoutChangeEvent) => {
const onLayout = (event: LayoutChangeEvent): void => {
let viewHeight = event.nativeEvent.layout.height;
const viewWidth = event.nativeEvent.layout.width;

Expand Down Expand Up @@ -149,7 +199,7 @@ export const Carousel = (props: CarouselProps<CarouselStyle>): ReactElement => {
testID={`${props.name}$carousel`}
activeSlideAlignment={props.activeSlideAlignment}
layout="default"
firstItem={0}
firstItem={firstItem}
useScrollView
enableSnap
data={props.contentSource.items}
Expand Down
20 changes: 20 additions & 0 deletions packages/pluggableWidgets/carousel-native/src/Carousel.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@
<caption>Content</caption>
<description/>
</property>
<property key="activeSelection" type="association" selectableObjects="contentSource" onChange="onChangeAction" required="false">
<caption>Active selection</caption>
<description/>
<associationTypes>
<associationType name="Reference"/>
</associationTypes>
</property>
</propertyGroup>
<propertyGroup caption="Effects">
<property key="animateExpression" type="expression" required="false">
<caption>Animate changed</caption>
<description>Animate when 'Active selection' association is changed, animation on user swiping will always be on.</description>
<returnType type="Boolean" />
</property>
</propertyGroup>
<propertyGroup caption="Display">
<property key="layout" type="enumeration" defaultValue="card">
Expand All @@ -41,6 +55,12 @@
</enumerationValues>
</property>
</propertyGroup>
<propertyGroup caption="Events">
<property key="onChangeAction" type="action" required="false">
<caption>On change</caption>
<description>When active selection association is changed.</description>
</property>
</propertyGroup>
<!-- Library has a bug with loops-->
<!-- https://github.com/archriss/react-native-snap-carousel/issues/653 - -->
<!-- https://github.com/archriss/react-native-snap-carousel/issues/608-->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* @author Mendix Widgets Framework Team
*/
import { ComponentType, CSSProperties, ReactNode } from "react";
import { ListValue, ListWidgetValue } from "mendix";
import { DynamicValue, ListValue, ListWidgetValue, ReferenceValue } from "mendix";

export type LayoutEnum = "card" | "fullWidth";

Expand All @@ -15,6 +15,8 @@ export interface CarouselProps<Style> {
style: Style[];
contentSource: ListValue;
content: ListWidgetValue;
activeSelection?: ReferenceValue;
animateExpression?: DynamicValue<boolean>;
layout: LayoutEnum;
showPagination: boolean;
activeSlideAlignment: ActiveSlideAlignmentEnum;
Expand All @@ -31,7 +33,10 @@ export interface CarouselPreviewProps {
readOnly: boolean;
contentSource: {} | { caption: string } | { type: string } | null;
content: { widgetCount: number; renderer: ComponentType<{ children: ReactNode; caption?: string }> };
activeSelection: string;
animateExpression: string;
layout: LayoutEnum;
showPagination: boolean;
activeSlideAlignment: ActiveSlideAlignmentEnum;
onChangeAction: {} | null;
}

0 comments on commit d60c596

Please sign in to comment.