leptos_use/
use_cycle_list.rs1use crate::core::MaybeRwSignal;
2use default_struct_builder::DefaultBuilder;
3use leptos::prelude::*;
4
5pub fn use_cycle_list<T, L>(
34 list: L,
35) -> UseCycleListReturn<
36 T,
37 impl Fn(usize) -> T + Clone + Send + Sync,
38 impl Fn() + Clone + Send + Sync,
39 impl Fn() + Clone + Send + Sync,
40 impl Fn(i64) -> T + Clone + Send + Sync,
41>
42where
43 T: Clone + PartialEq + Send + Sync + 'static,
44 L: Into<Signal<Vec<T>>>,
45{
46 use_cycle_list_with_options(list, UseCycleListOptions::default())
47}
48
49pub fn use_cycle_list_with_options<T, L>(
50 list: L,
51 options: UseCycleListOptions<T>,
52) -> UseCycleListReturn<
53 T,
54 impl Fn(usize) -> T + Clone,
55 impl Fn() + Clone,
56 impl Fn() + Clone,
57 impl Fn(i64) -> T + Clone,
58>
59where
60 T: Clone + PartialEq + Send + Sync + 'static,
61 L: Into<Signal<Vec<T>>>,
62{
63 let UseCycleListOptions {
64 initial_value,
65 fallback_index,
66 get_position,
67 } = options;
68
69 let list = list.into();
70
71 let get_initial_value = {
72 let list = list.get_untracked();
73 let first = list.first().cloned();
74
75 move || {
76 if let Some(initial_value) = initial_value {
77 initial_value
78 } else {
79 MaybeRwSignal::from(first.expect("The provided list shouldn't be empty"))
80 }
81 }
82 };
83
84 let (state, set_state) = get_initial_value().into_signal();
85
86 let index = Signal::derive(move || {
87 let index = get_position(&state.get(), &list.read());
88
89 if let Some(index) = index {
90 index
91 } else {
92 fallback_index
93 }
94 });
95
96 let set = move |i: usize| {
97 let length = list.read().len();
98
99 let index = i % length;
100 let value = list.read()[index].clone();
101
102 set_state.update({
103 let value = value.clone();
104
105 move |v| *v = value
106 });
107
108 value
109 };
110
111 let shift = move |delta: i64| {
112 let length = list.read().len() as i64;
113
114 let i = index.get_untracked() as i64 + delta;
115 let index = (i % length) + length;
116
117 set(index as usize)
118 };
119
120 let next = move || {
121 shift(1);
122 };
123
124 let prev = move || {
125 shift(-1);
126 };
127
128 let _ = Effect::watch(move || list.get(), move |_, _, _| set(index.get()), false);
129
130 UseCycleListReturn {
131 state,
132 set_state,
133 index,
134 set_index: set,
135 next,
136 prev,
137 shift,
138 }
139}
140
141#[derive(DefaultBuilder)]
143pub struct UseCycleListOptions<T>
144where
145 T: Clone + PartialEq + Send + Sync + 'static,
146{
147 #[builder(keep_type)]
150 initial_value: Option<MaybeRwSignal<T>>,
151
152 fallback_index: usize,
155
156 #[builder(keep_type)]
158 get_position: fn(&T, &Vec<T>) -> Option<usize>,
159}
160
161impl<T> Default for UseCycleListOptions<T>
162where
163 T: Clone + PartialEq + Send + Sync + 'static,
164{
165 fn default() -> Self {
166 Self {
167 initial_value: None,
168 fallback_index: 0,
169 get_position: |value: &T, list: &Vec<T>| list.iter().position(|v| v == value),
170 }
171 }
172}
173
174pub struct UseCycleListReturn<T, SetFn, NextFn, PrevFn, ShiftFn>
176where
177 T: Clone + PartialEq + Send + Sync + 'static,
178 SetFn: Fn(usize) -> T + Clone,
179 NextFn: Fn() + Clone,
180 PrevFn: Fn() + Clone,
181 ShiftFn: Fn(i64) -> T + Clone,
182{
183 pub state: Signal<T>,
185 pub set_state: WriteSignal<T>,
187 pub index: Signal<usize>,
189 pub set_index: SetFn,
191 pub next: NextFn,
193 pub prev: PrevFn,
195 pub shift: ShiftFn,
197}