1use std::{
29 any::Any,
30 cell::{Cell, RefCell},
31 fmt,
32 rc::Rc,
33};
34
35use dioxus::prelude::{Props, dioxus_elements, rsx};
36use dioxus_core::{
37 Attribute, AttributeValue, Callback, Element, current_scope_id, has_context, provide_context,
38 try_consume_context, use_hook,
39};
40use dioxus_hooks::{use_effect, use_reactive, use_signal};
41use dioxus_signals::{ReadSignal, ReadableExt, Signal, WritableExt};
42
43pub mod testing;
44
45#[allow(
50 clippy::struct_excessive_bools,
51 reason = "these independent presentation flags have no invalid combinations"
52)]
53#[derive(Clone, Debug, Default, PartialEq, Eq)]
54pub struct FieldMetaValues {
55 pub id: Option<Rc<str>>,
57 pub name: Option<Rc<str>>,
59 pub required: bool,
61 pub disabled: bool,
63 pub invalid: Option<bool>,
65 pub errors: Vec<Rc<str>>,
67 pub touched: bool,
69 pub dirty: bool,
71}
72
73#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
78pub struct FieldStateOverrides {
79 pub invalid: Option<bool>,
81 pub disabled: Option<bool>,
83 pub required: Option<bool>,
85}
86
87#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
93#[non_exhaustive]
94pub enum AttributeSurface {
95 #[default]
97 Native,
98 Aria,
100 Omit,
105}
106
107#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
113#[non_exhaustive]
114pub enum ValiditySurface {
115 #[default]
117 Aria,
118 Omit,
120}
121
122#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
126#[non_exhaustive]
127pub enum NameSurface {
128 #[default]
130 Native,
131 Omit,
136}
137
138#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
149pub struct FieldSurface {
150 pub required: AttributeSurface,
152 pub disabled: AttributeSurface,
154 pub validity: ValiditySurface,
156 pub name: NameSurface,
158}
159
160impl FieldSurface {
161 pub const NATIVE: Self = Self {
164 required: AttributeSurface::Native,
165 disabled: AttributeSurface::Native,
166 validity: ValiditySurface::Aria,
167 name: NameSurface::Native,
168 };
169
170 pub const BUTTON_WIDGET: Self = Self {
173 required: AttributeSurface::Aria,
174 disabled: AttributeSurface::Native,
175 validity: ValiditySurface::Aria,
176 name: NameSurface::Native,
177 };
178
179 pub const ARIA_WIDGET: Self = Self {
184 required: AttributeSurface::Aria,
185 disabled: AttributeSurface::Aria,
186 validity: ValiditySurface::Aria,
187 name: NameSurface::Omit,
188 };
189}
190
191#[derive(Clone, Debug, Default, PartialEq)]
205pub struct FieldControlOptions {
206 state: FieldStateOverrides,
207 id: Option<Rc<str>>,
208 name: Option<Rc<str>>,
209 surface: FieldSurface,
210}
211
212impl FieldControlOptions {
213 #[must_use]
215 pub fn new() -> Self {
216 Self::default()
217 }
218
219 #[must_use]
221 pub fn state(mut self, state: FieldStateOverrides) -> Self {
222 self.state = state;
223 self
224 }
225
226 #[must_use]
228 pub fn invalid(mut self, invalid: Option<bool>) -> Self {
229 self.state.invalid = invalid;
230 self
231 }
232
233 #[must_use]
235 pub fn disabled(mut self, disabled: Option<bool>) -> Self {
236 self.state.disabled = disabled;
237 self
238 }
239
240 #[must_use]
242 pub fn required(mut self, required: Option<bool>) -> Self {
243 self.state.required = required;
244 self
245 }
246
247 #[must_use]
252 pub fn id(mut self, id: Option<Rc<str>>) -> Self {
253 self.id = id;
254 self
255 }
256
257 #[must_use]
262 pub fn name(mut self, name: Option<Rc<str>>) -> Self {
263 self.name = name;
264 self
265 }
266
267 #[must_use]
269 pub fn surface(mut self, surface: FieldSurface) -> Self {
270 self.surface = surface;
271 self
272 }
273}
274
275#[derive(Clone, Copy, PartialEq)]
280pub struct FieldMeta {
281 id: Signal<Option<Rc<str>>>,
282 fallback_id: Signal<Rc<str>>,
283 name: Signal<Option<Rc<str>>>,
284 required: Signal<bool>,
285 disabled: Signal<bool>,
286 invalid: Signal<Option<bool>>,
287 errors: Signal<Vec<Rc<str>>>,
288 touched: Signal<bool>,
289 dirty: Signal<bool>,
290 registered_ids: Signal<RegisteredIds>,
291}
292
293impl fmt::Debug for FieldMeta {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 let id = self
296 .id
297 .peek()
298 .clone()
299 .unwrap_or_else(|| self.fallback_id.peek().clone());
300
301 f.debug_struct("FieldMeta")
302 .field("id", &id)
303 .field("name", &*self.name.peek())
304 .field("required", &*self.required.peek())
305 .field("disabled", &*self.disabled.peek())
306 .field("invalid", &*self.invalid.peek())
307 .field("errors", &*self.errors.peek())
308 .field("touched", &*self.touched.peek())
309 .field("dirty", &*self.dirty.peek())
310 .finish_non_exhaustive()
311 }
312}
313
314impl FieldMeta {
315 pub fn id(&self) -> Rc<str> {
322 (self.id)().unwrap_or_else(|| (self.fallback_id)())
323 }
324
325 pub fn set_id(&mut self, id: Option<Rc<str>>) {
327 self.id.set(id);
328 }
329
330 pub fn name(&self) -> Option<Rc<str>> {
332 (self.name)()
333 }
334
335 pub fn set_name(&mut self, name: Option<Rc<str>>) {
337 self.name.set(name);
338 }
339
340 pub fn required(&self) -> bool {
342 (self.required)()
343 }
344
345 pub fn set_required(&mut self, required: bool) {
347 self.required.set(required);
348 }
349
350 pub fn disabled(&self) -> bool {
352 (self.disabled)()
353 }
354
355 pub fn set_disabled(&mut self, disabled: bool) {
357 self.disabled.set(disabled);
358 }
359
360 pub fn invalid(&self) -> bool {
364 (self.invalid)().unwrap_or_else(|| !(self.errors)().is_empty())
365 }
366
367 pub fn set_invalid(&mut self, invalid: Option<bool>) {
369 self.invalid.set(invalid);
370 }
371
372 pub fn errors(&self) -> Vec<Rc<str>> {
374 (self.errors)()
375 }
376
377 pub fn set_errors(&mut self, errors: Vec<Rc<str>>) {
379 self.errors.set(errors);
380 }
381
382 pub fn touched(&self) -> bool {
384 (self.touched)()
385 }
386
387 pub fn set_touched(&mut self, touched: bool) {
389 self.touched.set(touched);
390 }
391
392 pub fn dirty(&self) -> bool {
394 (self.dirty)()
395 }
396
397 pub fn set_dirty(&mut self, dirty: bool) {
399 self.dirty.set(dirty);
400 }
401
402 #[must_use]
407 pub fn register_label_id(&mut self, id: Rc<str>) -> FieldMetaIdRegistration {
408 self.register_id(RegisteredIdKind::Label, id)
409 }
410
411 #[must_use]
413 pub fn register_description_id(&mut self, id: Rc<str>) -> FieldMetaIdRegistration {
414 self.register_id(RegisteredIdKind::Description, id)
415 }
416
417 #[must_use]
419 pub fn register_error_id(&mut self, id: Rc<str>) -> FieldMetaIdRegistration {
420 self.register_id(RegisteredIdKind::Error, id)
421 }
422
423 pub fn attributes(&self) -> Vec<Attribute> {
426 self.attributes_for(&FieldControlOptions::default())
427 }
428
429 pub fn attributes_for(&self, options: &FieldControlOptions) -> Vec<Attribute> {
464 let required = options.state.required.unwrap_or_else(|| self.required());
465 let disabled = options.state.disabled.unwrap_or_else(|| self.disabled());
466 let invalid = options.state.invalid.unwrap_or_else(|| self.invalid());
467 let id = options.id.clone().unwrap_or_else(|| self.id());
468 let name = options.name.clone().or_else(|| self.name());
469 let registered_ids = (self.registered_ids)();
470 let error_ids = registered_ids.ids(RegisteredIdKind::Error);
471 let mut described_by = registered_ids.ids(RegisteredIdKind::Description);
472 if invalid {
473 described_by.extend(error_ids.iter().cloned());
474 }
475 let mut attributes = Vec::new();
476
477 attributes.push(Attribute::new("id", id.to_string(), None, false));
478
479 if options.surface.name == NameSurface::Native {
480 push_optional_text(&mut attributes, "name", name);
481 }
482
483 push_surface_state(
484 &mut attributes,
485 options.surface.required,
486 ("required", "aria-required"),
487 required,
488 );
489 push_surface_state(
490 &mut attributes,
491 options.surface.disabled,
492 ("disabled", "aria-disabled"),
493 disabled,
494 );
495
496 if options.surface.validity == ValiditySurface::Aria {
497 attributes.push(Attribute::new(
498 "aria-invalid",
499 invalid.to_string(),
500 None,
501 false,
502 ));
503 if invalid {
504 push_optional_text(
505 &mut attributes,
506 "aria-errormessage",
507 error_ids.first().cloned(),
508 );
509 }
510 }
511
512 push_optional_text(
513 &mut attributes,
514 "aria-labelledby",
515 join_ids(®istered_ids.ids(RegisteredIdKind::Label)),
516 );
517 push_optional_text(&mut attributes, "aria-describedby", join_ids(&described_by));
518
519 push_state(&mut attributes, "data-required", required);
520 push_state(&mut attributes, "data-disabled", disabled);
521 push_state(&mut attributes, "data-invalid", invalid);
522 push_state(&mut attributes, "data-touched", self.touched());
523 push_state(&mut attributes, "data-dirty", self.dirty());
524
525 normalize_attributes(&mut attributes);
526
527 attributes
528 }
529
530 fn register_id(&mut self, kind: RegisteredIdKind, id: Rc<str>) -> FieldMetaIdRegistration {
531 let token = self.registered_ids.with_mut(|ids| ids.insert(kind, id));
532
533 FieldMetaIdRegistration {
534 registered_ids: self.registered_ids,
535 token,
536 }
537 }
538
539 pub fn set_values(&mut self, values: FieldMetaValues) {
541 if *self.id.peek() != values.id {
542 self.id.set(values.id);
543 }
544 if *self.name.peek() != values.name {
545 self.name.set(values.name);
546 }
547 if *self.required.peek() != values.required {
548 self.required.set(values.required);
549 }
550 if *self.disabled.peek() != values.disabled {
551 self.disabled.set(values.disabled);
552 }
553 if *self.invalid.peek() != values.invalid {
554 self.invalid.set(values.invalid);
555 }
556 if *self.errors.peek() != values.errors {
557 self.errors.set(values.errors);
558 }
559 if *self.touched.peek() != values.touched {
560 self.touched.set(values.touched);
561 }
562 if *self.dirty.peek() != values.dirty {
563 self.dirty.set(values.dirty);
564 }
565 }
566}
567
568pub fn use_field_meta_state(initial: FieldMetaValues) -> FieldMeta {
574 let FieldMetaValues {
575 id,
576 name,
577 required,
578 disabled,
579 invalid,
580 errors,
581 touched,
582 dirty,
583 } = initial;
584
585 FieldMeta {
586 id: use_signal(|| id),
587 fallback_id: use_signal(|| generated_id("field")),
588 name: use_signal(|| name),
589 required: use_signal(|| required),
590 disabled: use_signal(|| disabled),
591 invalid: use_signal(|| invalid),
592 errors: use_signal(|| errors),
593 touched: use_signal(|| touched),
594 dirty: use_signal(|| dirty),
595 registered_ids: use_signal(RegisteredIds::default),
596 }
597}
598
599fn use_synced_field_meta_state(values: &FieldMetaValues) -> FieldMeta {
600 let meta = use_field_meta_state(values.clone());
601 use_effect(use_reactive(values, move |values| {
602 let mut meta = meta;
603 meta.set_values(values);
604 }));
605
606 meta
607}
608
609pub struct FieldMetaIdRegistration {
611 registered_ids: Signal<RegisteredIds>,
612 token: u64,
613}
614
615impl fmt::Debug for FieldMetaIdRegistration {
616 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
617 f.debug_struct("FieldMetaIdRegistration")
618 .field("token", &self.token)
619 .finish_non_exhaustive()
620 }
621}
622
623impl Drop for FieldMetaIdRegistration {
624 fn drop(&mut self) {
625 self.registered_ids
626 .with_mut(|ids| ids.entries.retain(|entry| entry.token != self.token));
627 }
628}
629
630#[derive(Clone, Copy, PartialEq, Eq)]
631enum RegisteredIdKind {
632 Label,
633 Description,
634 Error,
635}
636
637#[derive(Clone, Default, PartialEq, Eq)]
638struct RegisteredIds {
639 next_token: u64,
640 entries: Vec<RegisteredId>,
641}
642
643impl RegisteredIds {
644 fn insert(&mut self, kind: RegisteredIdKind, id: Rc<str>) -> u64 {
645 let token = self.next_token;
646 self.next_token += 1;
647 self.entries.push(RegisteredId { token, kind, id });
648
649 token
650 }
651
652 fn ids(&self, kind: RegisteredIdKind) -> Vec<Rc<str>> {
655 self.entries
656 .iter()
657 .filter(|entry| entry.kind == kind)
658 .map(|entry| Rc::clone(&entry.id))
659 .collect()
660 }
661}
662
663fn join_ids(ids: &[Rc<str>]) -> Option<Rc<str>> {
664 (!ids.is_empty()).then(|| {
665 Rc::from(
666 ids.iter()
667 .map(AsRef::as_ref)
668 .collect::<Vec<_>>()
669 .join(" ")
670 .as_str(),
671 )
672 })
673}
674
675#[derive(Clone)]
677struct GeneratedIdCounter(Rc<Cell<u64>>);
678
679fn generated_id(prefix: &str) -> Rc<str> {
685 let counter = has_context::<GeneratedIdCounter>()
686 .unwrap_or_else(|| provide_context(GeneratedIdCounter(Rc::new(Cell::new(0)))));
687 let index = counter.0.get();
688 counter.0.set(index + 1);
689
690 Rc::from(format!("dxf-{prefix}-{}-{index}", current_scope_id().0).as_str())
691}
692
693fn use_part_id(explicit: Option<Rc<str>>, prefix: &'static str) -> Rc<str> {
695 let generated = use_hook(|| generated_id(prefix));
696
697 explicit.unwrap_or(generated)
698}
699
700#[derive(Clone, PartialEq, Eq)]
701struct RegisteredId {
702 token: u64,
703 kind: RegisteredIdKind,
704 id: Rc<str>,
705}
706
707fn push_optional_text(attributes: &mut Vec<Attribute>, name: &'static str, value: Option<Rc<str>>) {
708 if let Some(value) = value {
709 attributes.push(Attribute::new(name, value.to_string(), None, false));
710 }
711}
712
713fn push_bool(attributes: &mut Vec<Attribute>, name: &'static str, value: bool) {
714 if value {
715 attributes.push(Attribute::new(name, true, None, false));
716 }
717}
718
719fn push_surface_state(
724 attributes: &mut Vec<Attribute>,
725 surface: AttributeSurface,
726 (native, aria): (&'static str, &'static str),
727 value: bool,
728) {
729 match surface {
730 AttributeSurface::Native => push_bool(attributes, native, value),
731 AttributeSurface::Aria => push_state(attributes, aria, value),
732 AttributeSurface::Omit => {}
733 }
734}
735
736fn push_state(attributes: &mut Vec<Attribute>, name: &'static str, value: bool) {
741 if value {
742 attributes.push(Attribute::new(name, "true", None, false));
743 }
744}
745
746pub fn merge_attributes(groups: Vec<Vec<Attribute>>) -> Vec<Attribute> {
781 let mut attributes = groups.into_iter().flatten().collect::<Vec<_>>();
782 normalize_attributes(&mut attributes);
783
784 attributes
785}
786
787fn normalize_attributes(attributes: &mut Vec<Attribute>) {
795 attributes.sort_by(|left, right| {
796 left.name
797 .cmp(right.name)
798 .then_with(|| left.namespace.cmp(&right.namespace))
799 });
800 attributes.dedup_by(|later, earlier| {
801 let duplicate = later.name == earlier.name && later.namespace == earlier.namespace;
802
803 if duplicate {
804 if let ("class", AttributeValue::Text(kept), AttributeValue::Text(dropped)) =
805 (later.name, &later.value, &earlier.value)
806 {
807 let combined = format!("{dropped} {kept}");
810 later.value = AttributeValue::Text(combined);
811 }
812
813 std::mem::swap(later, earlier);
814 }
815
816 duplicate
817 });
818}
819
820#[derive(Clone, Copy, Debug, PartialEq, Eq)]
822pub enum ChangeOrigin {
823 User,
825 Programmatic,
827}
828
829pub struct Binding<T: 'static> {
835 pub read: ReadSignal<T>,
837 write: Callback<(T, ChangeOrigin)>,
838 commit: Callback<()>,
839 identity: BindingIdentity,
840}
841
842impl<T: 'static> Binding<T> {
843 pub fn new(
845 read: ReadSignal<T>,
846 write: Callback<(T, ChangeOrigin)>,
847 commit: Callback<()>,
848 ) -> Self {
849 Self::new_with_identity(read, write, commit, (read, write, commit))
850 }
851
852 pub fn new_with_identity<I>(
857 read: ReadSignal<T>,
858 write: Callback<(T, ChangeOrigin)>,
859 commit: Callback<()>,
860 identity: I,
861 ) -> Self
862 where
863 I: PartialEq + 'static,
864 {
865 Self {
866 read,
867 write,
868 commit,
869 identity: BindingIdentity::new(identity),
870 }
871 }
872
873 pub fn write(&self, value: T, origin: ChangeOrigin) {
875 self.write.call((value, origin));
876 }
877
878 pub fn commit(&self) {
880 self.commit.call(());
881 }
882
883 pub fn into_trio(self) -> BindingPropTrio<T> {
887 let value = self.read;
888 let on_commit = self.commit;
889 let on_change = Callback::new(move |value| self.write(value, ChangeOrigin::User));
890
891 BindingPropTrio {
892 value,
893 on_change,
894 on_commit,
895 }
896 }
897}
898
899impl<T: fmt::Debug + 'static> fmt::Debug for Binding<T> {
900 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
901 f.debug_struct("Binding")
902 .field("read", &*self.read.peek())
903 .finish_non_exhaustive()
904 }
905}
906
907impl<T: 'static> Clone for Binding<T> {
908 fn clone(&self) -> Self {
909 Self {
910 read: self.read,
911 write: self.write,
912 commit: self.commit,
913 identity: self.identity.clone(),
914 }
915 }
916}
917
918impl<T: 'static> PartialEq for Binding<T> {
919 fn eq(&self, other: &Self) -> bool {
920 self.identity == other.identity
921 }
922}
923
924impl<T: 'static> From<Signal<T>> for Binding<T> {
925 fn from(signal: Signal<T>) -> Self {
926 let read = ReadSignal::from(signal);
927 let mut writer = signal;
928 let write = Callback::new(move |(value, _origin)| writer.set(value));
929 let commit = Callback::new(|()| {});
930
931 Self::new_with_identity(read, write, commit, signal)
932 }
933}
934
935impl<T: 'static> From<(ReadSignal<T>, Callback<T>)> for Binding<T> {
936 fn from((read, on_change): (ReadSignal<T>, Callback<T>)) -> Self {
937 let write = Callback::new(move |(value, _origin)| on_change.call(value));
938 let commit = Callback::new(|()| {});
939
940 Self::new_with_identity(read, write, commit, (read, on_change))
941 }
942}
943
944impl<T: 'static> From<T> for Binding<T> {
945 fn from(value: T) -> Self {
946 Signal::new(value).into()
947 }
948}
949
950pub struct BindingPropTrio<T: 'static> {
955 pub value: ReadSignal<T>,
957 pub on_change: Callback<T>,
959 pub on_commit: Callback<()>,
961}
962
963impl<T: fmt::Debug + 'static> fmt::Debug for BindingPropTrio<T> {
964 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
965 f.debug_struct("BindingPropTrio")
966 .field("value", &*self.value.peek())
967 .field("on_change", &self.on_change)
968 .field("on_commit", &self.on_commit)
969 .finish()
970 }
971}
972
973impl<T: 'static> From<Binding<T>> for BindingPropTrio<T> {
974 fn from(binding: Binding<T>) -> Self {
975 binding.into_trio()
976 }
977}
978
979#[derive(Clone, Default)]
981pub struct FocusRequest(Rc<RefCell<FocusRequestState>>);
982
983impl FocusRequest {
984 #[must_use]
989 pub fn register(&self, callback: Callback<()>) -> FocusRegistration {
990 let mut state = self.0.borrow_mut();
991 let token = state.next_token;
992 state.next_token += 1;
993 state.current = Some((token, callback));
994
995 FocusRegistration {
996 request: self.clone(),
997 token,
998 }
999 }
1000
1001 pub fn request(&self) -> bool {
1005 let callback = self.0.borrow().current.map(|(_, callback)| callback);
1006
1007 if let Some(callback) = callback {
1008 callback.call(());
1009 true
1010 } else {
1011 false
1012 }
1013 }
1014}
1015
1016impl fmt::Debug for FocusRequest {
1017 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1018 f.debug_struct("FocusRequest")
1019 .field("registered", &self.0.borrow().current.is_some())
1020 .finish()
1021 }
1022}
1023
1024impl PartialEq for FocusRequest {
1025 fn eq(&self, other: &Self) -> bool {
1026 Rc::ptr_eq(&self.0, &other.0)
1027 }
1028}
1029
1030#[derive(Default)]
1031struct FocusRequestState {
1032 next_token: u64,
1033 current: Option<(u64, Callback<()>)>,
1034}
1035
1036pub struct FocusRegistration {
1038 request: FocusRequest,
1039 token: u64,
1040}
1041
1042impl fmt::Debug for FocusRegistration {
1043 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1044 f.debug_struct("FocusRegistration")
1045 .field("request", &self.request)
1046 .field("token", &self.token)
1047 .finish()
1048 }
1049}
1050
1051impl Drop for FocusRegistration {
1052 fn drop(&mut self) {
1053 let mut state = self.request.0.borrow_mut();
1054
1055 if state.current.is_some_and(|(token, _)| token == self.token) {
1056 state.current = None;
1057 }
1058 }
1059}
1060
1061#[derive(Clone)]
1075pub struct FieldContext {
1076 binding: Option<ErasedBinding>,
1077 meta: Option<FieldMeta>,
1078 meta_values: Option<FieldMetaValues>,
1079 focus_request: FocusRequest,
1080}
1081
1082impl FieldContext {
1083 pub fn new<T: 'static>(binding: Binding<T>) -> Self {
1085 Self {
1086 binding: Some(ErasedBinding::new(binding)),
1087 meta: None,
1088 meta_values: None,
1089 focus_request: FocusRequest::default(),
1090 }
1091 }
1092
1093 pub fn empty() -> Self {
1095 Self {
1096 binding: None,
1097 meta: None,
1098 meta_values: None,
1099 focus_request: FocusRequest::default(),
1100 }
1101 }
1102
1103 #[must_use]
1105 pub fn with_binding<T: 'static>(mut self, binding: Binding<T>) -> Self {
1106 self.binding = Some(ErasedBinding::new(binding));
1107 self
1108 }
1109
1110 #[must_use]
1112 pub fn with_meta(mut self, meta: FieldMeta) -> Self {
1113 self.meta = Some(meta);
1114 self.meta_values = None;
1115 self
1116 }
1117
1118 #[must_use]
1120 pub fn with_meta_values(mut self, values: FieldMetaValues) -> Self {
1121 self.meta = None;
1122 self.meta_values = Some(values);
1123 self
1124 }
1125
1126 pub fn meta(&self) -> Option<FieldMeta> {
1128 self.meta
1129 }
1130
1131 pub fn focus_request(&self) -> FocusRequest {
1136 self.focus_request.clone()
1137 }
1138
1139 pub fn resolve<T: 'static>(&self) -> Binding<T> {
1145 let erased = self
1146 .binding
1147 .as_ref()
1148 .unwrap_or_else(|| panic!("Field Context contains no value binding"));
1149
1150 erased
1151 .binding
1152 .downcast_ref::<Binding<T>>()
1153 .unwrap_or_else(|| {
1154 panic!(
1155 "Field Context contains a binding for {}, but a binding for {} was requested",
1156 erased.value_type_name,
1157 std::any::type_name::<T>()
1158 )
1159 })
1160 .clone()
1161 }
1162
1163 fn try_resolve<T: 'static>(&self) -> Option<Binding<T>> {
1164 self.binding.as_ref().map(|_| self.resolve())
1165 }
1166
1167 fn with_focus_request(mut self, focus_request: FocusRequest) -> Self {
1168 self.focus_request = focus_request;
1169 self
1170 }
1171}
1172
1173impl fmt::Debug for FieldContext {
1174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1175 f.debug_struct("FieldContext")
1176 .field(
1177 "value_type_name",
1178 &self.binding.as_ref().map(|binding| binding.value_type_name),
1179 )
1180 .field("meta", &self.meta)
1181 .field("meta_values", &self.meta_values)
1182 .field("focus_request", &self.focus_request)
1183 .finish_non_exhaustive()
1184 }
1185}
1186
1187impl PartialEq for FieldContext {
1188 fn eq(&self, other: &Self) -> bool {
1189 self.binding == other.binding
1190 && self.meta == other.meta
1191 && self.meta_values == other.meta_values
1192 }
1193}
1194
1195impl<T: 'static> From<Binding<T>> for FieldContext {
1196 fn from(binding: Binding<T>) -> Self {
1197 Self::new(binding)
1198 }
1199}
1200
1201impl<T: 'static> From<Signal<T>> for FieldContext {
1202 fn from(signal: Signal<T>) -> Self {
1203 Self::new(Binding::<T>::from(signal))
1204 }
1205}
1206
1207#[derive(Clone)]
1212struct ErasedBinding {
1213 binding: Rc<dyn Any>,
1214 value_type_name: &'static str,
1215 eq: fn(&dyn Any, &dyn Any) -> bool,
1216}
1217
1218impl ErasedBinding {
1219 fn new<T: 'static>(binding: Binding<T>) -> Self {
1220 Self {
1221 binding: Rc::new(binding),
1222 value_type_name: std::any::type_name::<T>(),
1223 eq: |left, right| match (
1224 left.downcast_ref::<Binding<T>>(),
1225 right.downcast_ref::<Binding<T>>(),
1226 ) {
1227 (Some(left), Some(right)) => left == right,
1228 _ => false,
1229 },
1230 }
1231 }
1232}
1233
1234impl PartialEq for ErasedBinding {
1235 fn eq(&self, other: &Self) -> bool {
1236 (self.eq)(&*self.binding, &*other.binding)
1237 }
1238}
1239
1240pub fn provide_field_context<T: 'static>(binding: Binding<T>) -> FieldContext {
1246 let mut context = FieldContext::new(binding);
1247
1248 if let Some(existing) = has_context::<FieldContext>() {
1249 context = context.with_focus_request(existing.focus_request());
1250 }
1251
1252 provide_context(context)
1253}
1254
1255pub fn use_binding<T: 'static>(explicit: Option<Binding<T>>, default: T) -> Binding<T> {
1260 let internal = use_signal(|| default);
1261
1262 if let Some(binding) = explicit {
1263 return binding;
1264 }
1265
1266 if let Some(binding) =
1267 try_consume_context::<FieldContext>().and_then(|context| context.try_resolve())
1268 {
1269 return binding;
1270 }
1271
1272 internal.into()
1273}
1274
1275pub fn use_field_meta(explicit: Option<FieldMeta>) -> FieldMeta {
1280 use_resolved_field_meta(explicit.as_ref()).0
1281}
1282
1283#[derive(Clone, Copy, PartialEq, Eq)]
1288enum FieldMetaSource {
1289 Shared,
1292 Standalone,
1294}
1295
1296fn use_resolved_field_meta(explicit: Option<&FieldMeta>) -> (FieldMeta, FieldMetaSource) {
1298 let internal = use_field_meta_state(FieldMetaValues::default());
1299
1300 explicit
1301 .copied()
1302 .or_else(|| try_consume_context::<FieldContext>().and_then(|context| context.meta()))
1303 .map_or((internal, FieldMetaSource::Standalone), |meta| {
1304 (meta, FieldMetaSource::Shared)
1305 })
1306}
1307
1308pub fn use_focus_request() -> FocusRequest {
1310 let internal = use_hook(FocusRequest::default);
1311
1312 try_consume_context::<FieldContext>().map_or(internal, |context| context.focus_request())
1313}
1314
1315pub fn use_focus_registration(callback: Callback<()>) -> FocusRequest {
1335 let request = use_focus_request();
1336 let active = use_hook(|| Rc::new(RefCell::new(None::<ActiveFocusRegistration>)));
1337 let should_replace = active
1338 .borrow()
1339 .as_ref()
1340 .is_none_or(|active| active.request != request || active.callback != callback);
1341
1342 if should_replace {
1343 let registration = request.register(callback);
1344 active.borrow_mut().replace(ActiveFocusRegistration {
1345 request: request.clone(),
1346 callback,
1347 _registration: registration,
1348 });
1349 }
1350
1351 request
1352}
1353
1354struct ActiveFocusRegistration {
1355 request: FocusRequest,
1356 callback: Callback<()>,
1357 _registration: FocusRegistration,
1358}
1359
1360#[derive(Clone, Debug, Props, PartialEq)]
1362pub struct FieldProps {
1363 #[props(into)]
1368 pub context: FieldContext,
1369 #[props(extends = GlobalAttributes)]
1374 pub attributes: Vec<Attribute>,
1375 pub children: Element,
1377}
1378
1379#[allow(non_snake_case)]
1391#[allow(
1392 clippy::missing_errors_doc,
1393 reason = "Dioxus Element uses Result as its renderer protocol"
1394)]
1395pub fn Field(props: FieldProps) -> Element {
1396 let has_meta_values = props.context.meta_values.is_some();
1397 let meta_values = props.context.meta_values.clone().unwrap_or_default();
1398 let synced_meta = use_synced_field_meta_state(&meta_values);
1399 let mut context = props.context;
1400
1401 if has_meta_values {
1402 context = context.with_meta(synced_meta);
1403 }
1404
1405 let focus_request = use_hook(|| context.focus_request());
1406 provide_context(context.with_focus_request(focus_request));
1407 let mut attributes = props.attributes;
1408 normalize_attributes(&mut attributes);
1409
1410 rsx! {
1411 div { ..attributes, {props.children} }
1412 }
1413}
1414
1415#[derive(Clone, Debug, Props, PartialEq)]
1417pub struct LabelProps {
1418 #[props(default)]
1425 pub id: Option<Rc<str>>,
1426 #[props(default)]
1428 pub meta: Option<FieldMeta>,
1429 #[props(default)]
1431 pub invalid: Option<bool>,
1432 #[props(default)]
1434 pub disabled: Option<bool>,
1435 #[props(default)]
1437 pub required: Option<bool>,
1438 #[props(extends = GlobalAttributes)]
1443 pub attributes: Vec<Attribute>,
1444 pub children: Element,
1446}
1447
1448#[allow(non_snake_case)]
1455#[allow(
1456 clippy::missing_errors_doc,
1457 reason = "Dioxus Element uses Result as its renderer protocol"
1458)]
1459pub fn Label(props: LabelProps) -> Element {
1460 let (meta, source) = use_resolved_field_meta(props.meta.as_ref());
1461 let id = use_part_id(props.id, "label");
1462 use_field_meta_id_registration(&meta, RegisteredIdKind::Label, Rc::clone(&id));
1463 let control_id = (source == FieldMetaSource::Shared).then(|| meta.id().to_string());
1466 let attributes = part_attributes(
1467 &meta,
1468 FieldStateOverrides {
1469 invalid: props.invalid,
1470 disabled: props.disabled,
1471 required: props.required,
1472 },
1473 props.attributes,
1474 );
1475
1476 rsx! {
1477 label { id: id.to_string(), r#for: control_id, ..attributes, {props.children} }
1478 }
1479}
1480
1481#[derive(Clone, Debug, Props, PartialEq)]
1483pub struct FieldDescriptionProps {
1484 #[props(into)]
1486 pub id: Rc<str>,
1487 #[props(default)]
1489 pub meta: Option<FieldMeta>,
1490 #[props(default)]
1492 pub invalid: Option<bool>,
1493 #[props(default)]
1495 pub disabled: Option<bool>,
1496 #[props(default)]
1498 pub required: Option<bool>,
1499 #[props(extends = GlobalAttributes)]
1504 pub attributes: Vec<Attribute>,
1505 pub children: Element,
1507}
1508
1509#[allow(non_snake_case)]
1513#[allow(
1514 clippy::missing_errors_doc,
1515 reason = "Dioxus Element uses Result as its renderer protocol"
1516)]
1517pub fn FieldDescription(props: FieldDescriptionProps) -> Element {
1518 let meta = use_field_meta(props.meta);
1519 use_field_meta_id_registration(&meta, RegisteredIdKind::Description, props.id.clone());
1520 let id = props.id.to_string();
1521 let attributes = part_attributes(
1522 &meta,
1523 FieldStateOverrides {
1524 invalid: props.invalid,
1525 disabled: props.disabled,
1526 required: props.required,
1527 },
1528 props.attributes,
1529 );
1530
1531 rsx! {
1532 div { id: id, ..attributes, {props.children} }
1533 }
1534}
1535
1536#[derive(Clone, Debug, Props, PartialEq)]
1538pub struct FieldErrorProps {
1539 #[props(into)]
1541 pub id: Rc<str>,
1542 #[props(default)]
1544 pub meta: Option<FieldMeta>,
1545 #[props(default)]
1547 pub invalid: Option<bool>,
1548 #[props(default)]
1550 pub disabled: Option<bool>,
1551 #[props(default)]
1553 pub required: Option<bool>,
1554 #[props(extends = GlobalAttributes)]
1559 pub attributes: Vec<Attribute>,
1560}
1561
1562#[allow(non_snake_case)]
1570#[allow(
1571 clippy::missing_errors_doc,
1572 reason = "Dioxus Element uses Result as its renderer protocol"
1573)]
1574pub fn FieldError(props: FieldErrorProps) -> Element {
1575 let meta = use_field_meta(props.meta);
1576 use_field_meta_id_registration(&meta, RegisteredIdKind::Error, props.id.clone());
1577 let invalid = props.invalid.unwrap_or_else(|| meta.invalid());
1578 let id = props.id.to_string();
1579 let errors = if invalid { meta.errors() } else { Vec::new() };
1580 let attributes = part_attributes(
1581 &meta,
1582 FieldStateOverrides {
1583 invalid: props.invalid,
1584 disabled: props.disabled,
1585 required: props.required,
1586 },
1587 props.attributes,
1588 );
1589
1590 rsx! {
1591 div {
1592 id: id,
1593 aria_live: "polite",
1594 ..attributes,
1595 for error in errors {
1596 div { "{error}" }
1597 }
1598 }
1599 }
1600}
1601
1602fn part_attributes(
1607 meta: &FieldMeta,
1608 overrides: FieldStateOverrides,
1609 forwarded: Vec<Attribute>,
1610) -> Vec<Attribute> {
1611 let required = overrides.required.unwrap_or_else(|| meta.required());
1612 let disabled = overrides.disabled.unwrap_or_else(|| meta.disabled());
1613 let invalid = overrides.invalid.unwrap_or_else(|| meta.invalid());
1614 let mut attributes = Vec::new();
1615
1616 push_state(&mut attributes, "data-required", required);
1617 push_state(&mut attributes, "data-disabled", disabled);
1618 push_state(&mut attributes, "data-invalid", invalid);
1619 push_state(&mut attributes, "data-touched", meta.touched());
1620 push_state(&mut attributes, "data-dirty", meta.dirty());
1621
1622 attributes.extend(forwarded);
1623 normalize_attributes(&mut attributes);
1624
1625 attributes
1626}
1627
1628fn use_field_meta_id_registration(meta: &FieldMeta, kind: RegisteredIdKind, id: Rc<str>) {
1629 let active = use_hook(|| Rc::new(RefCell::new(None::<ActiveFieldMetaIdRegistration>)));
1630 let should_replace = active
1631 .borrow()
1632 .as_ref()
1633 .is_none_or(|active| active.meta != *meta || active.kind != kind || active.id != id);
1634
1635 if should_replace {
1636 let mut writable_meta = *meta;
1637 let registration = writable_meta.register_id(kind, id.clone());
1638 active.borrow_mut().replace(ActiveFieldMetaIdRegistration {
1639 meta: *meta,
1640 kind,
1641 id,
1642 _registration: registration,
1643 });
1644 }
1645}
1646
1647struct ActiveFieldMetaIdRegistration {
1648 meta: FieldMeta,
1649 kind: RegisteredIdKind,
1650 id: Rc<str>,
1651 _registration: FieldMetaIdRegistration,
1652}
1653
1654#[derive(Clone)]
1655struct BindingIdentity(Rc<dyn ComparableIdentity>);
1656
1657impl BindingIdentity {
1658 fn new<I: PartialEq + 'static>(identity: I) -> Self {
1659 Self(Rc::new(identity))
1660 }
1661}
1662
1663impl PartialEq for BindingIdentity {
1664 fn eq(&self, other: &Self) -> bool {
1665 self.0.equals(other.0.as_ref())
1666 }
1667}
1668
1669trait ComparableIdentity: Any {
1670 fn equals(&self, other: &dyn ComparableIdentity) -> bool;
1671}
1672
1673impl<I: PartialEq + 'static> ComparableIdentity for I {
1674 fn equals(&self, other: &dyn ComparableIdentity) -> bool {
1675 let other = other as &dyn Any;
1676 other.downcast_ref::<I>().is_some_and(|other| self == other)
1677 }
1678}