1use std::cell::RefCell;
4
5use slab::Slab;
6
7use crate::html::Scope;
8use crate::{Callback, Component, Context, Html, Properties};
9
10#[derive(Debug, Clone, PartialEq, Properties)]
12pub struct ContextProviderProps<T: Clone + PartialEq> {
13 pub context: T,
15 pub children: Html,
17}
18
19#[derive(Debug)]
25pub struct ContextProvider<T: Clone + PartialEq + 'static> {
26 context: T,
27 consumers: RefCell<Slab<Callback<T>>>,
28}
29
30#[derive(Debug)]
33pub struct ContextHandle<T: Clone + PartialEq + 'static> {
34 provider: Scope<ContextProvider<T>>,
35 key: usize,
36}
37
38impl<T: Clone + PartialEq + 'static> Drop for ContextHandle<T> {
39 fn drop(&mut self) {
40 if let Some(component) = self.provider.get_component() {
41 component.consumers.borrow_mut().remove(self.key);
42 }
43 }
44}
45
46impl<T: Clone + PartialEq> ContextProvider<T> {
47 pub(crate) fn subscribe_consumer(
50 &self,
51 callback: Callback<T>,
52 scope: Scope<Self>,
53 ) -> (T, ContextHandle<T>) {
54 let ctx = self.context.clone();
55 let key = self.consumers.borrow_mut().insert(callback);
56
57 (
58 ctx,
59 ContextHandle {
60 provider: scope,
61 key,
62 },
63 )
64 }
65
66 fn notify_consumers(&mut self) {
68 let consumers: Vec<Callback<T>> = self
69 .consumers
70 .borrow()
71 .iter()
72 .map(|(_, v)| v.clone())
73 .collect();
74 for consumer in consumers {
75 consumer.emit(self.context.clone());
76 }
77 }
78}
79
80impl<T: Clone + PartialEq + 'static> Component for ContextProvider<T> {
81 type Message = ();
82 type Properties = ContextProviderProps<T>;
83
84 fn create(ctx: &Context<Self>) -> Self {
85 let props = ctx.props();
86 Self {
87 context: props.context.clone(),
88 consumers: RefCell::new(Slab::new()),
89 }
90 }
91
92 fn changed(&mut self, ctx: &Context<Self>, old_props: &Self::Properties) -> bool {
93 let props = ctx.props();
94
95 let should_render = old_props.children != props.children;
96
97 if self.context != props.context {
98 self.context = props.context.clone();
99 self.notify_consumers();
100 }
101
102 should_render
103 }
104
105 fn view(&self, ctx: &Context<Self>) -> Html {
106 ctx.props().children.clone()
107 }
108}