@entur/form 8.4.2 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/form.esm.js CHANGED
@@ -875,142 +875,217 @@ const ClearButton = ({ onClear, ...props }) => {
875
875
  const SegmentedContext = React__default.createContext(
876
876
  null
877
877
  );
878
- const SegmentedProvider = ({
879
- name,
880
- onChange = () => void 0,
881
- selectedValue,
882
- multiple,
883
- size,
884
- ...rest
885
- }) => {
886
- const generatedName = useRandomId("eds-segmented-control");
887
- const contextValue = React__default.useMemo(
888
- () => ({
889
- name: name || generatedName,
890
- onChange,
891
- multiple,
892
- selectedValue,
893
- size
894
- }),
895
- [generatedName, multiple, name, onChange, selectedValue, size]
896
- );
897
- return /* @__PURE__ */ jsx(SegmentedContext.Provider, { value: contextValue, ...rest });
898
- };
899
878
  const useSegmentedContext = () => {
900
879
  const context = React__default.useContext(SegmentedContext);
901
880
  if (!context) {
902
881
  throw new Error(
903
- "You need to wrap your SegmentedChoice in either SegmentedControl or MultipleSegmentedControl"
882
+ "You need to wrap your SegmentedChoice in SegmentedControl"
904
883
  );
905
884
  }
906
885
  return context;
907
886
  };
908
- const SegmentedChoice = React__default.forwardRef(
887
+ const SegmentedControl = React__default.forwardRef(
909
888
  ({
910
889
  children,
911
- className,
912
- style,
913
- value,
890
+ label,
914
891
  name,
915
- onChange = () => void 0,
892
+ onChange,
893
+ value,
894
+ defaultValue,
895
+ size = "medium",
896
+ className,
897
+ selectedValue: deprecatedValue,
916
898
  ...rest
917
899
  }, ref) => {
918
- const {
919
- name: commonName,
920
- selectedValue,
921
- onChange: commonOnChange,
922
- multiple,
923
- size
924
- } = useSegmentedContext();
925
- const isChecked = multiple ? selectedValue[value] : selectedValue === value;
926
- const handleChange = (e) => {
927
- onChange(e);
928
- if (multiple) {
929
- commonOnChange({
930
- ...selectedValue,
931
- [value]: e.target.checked
932
- });
933
- } else if (e.target.checked) {
934
- commonOnChange(value);
935
- }
936
- };
937
- return /* @__PURE__ */ jsxs(
938
- "label",
900
+ const [internalValue, setInternalValue] = React__default.useState(
901
+ defaultValue ?? null
902
+ );
903
+ const [focusedValue, setFocusedValue] = React__default.useState(null);
904
+ const id = useRandomId("eds-segmented-control");
905
+ const isControlled = deprecatedValue !== void 0 || value !== void 0;
906
+ const currentValue = deprecatedValue !== void 0 ? deprecatedValue : value !== void 0 ? value : internalValue;
907
+ const handleChange = React__default.useCallback(
908
+ (newValue) => {
909
+ if (!isControlled) {
910
+ setInternalValue(newValue);
911
+ }
912
+ onChange?.(newValue);
913
+ },
914
+ [isControlled, onChange]
915
+ );
916
+ const contextValue = React__default.useMemo(
917
+ () => ({
918
+ name: name ?? label ?? id,
919
+ onChange: handleChange,
920
+ value: currentValue,
921
+ size,
922
+ focusedValue,
923
+ setFocusedValue
924
+ }),
925
+ [id, name, handleChange, currentValue, size, focusedValue, label]
926
+ );
927
+ const labelId = `${id}-label`;
928
+ return /* @__PURE__ */ jsx(SegmentedContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs(
929
+ "div",
939
930
  {
940
- className: classNames("eds-segmented-choice", className),
941
- style,
931
+ className: classNames("eds-segmented-control", className),
932
+ role: "radiogroup",
933
+ "aria-labelledby": label ? labelId : void 0,
934
+ ref,
935
+ ...rest,
942
936
  children: [
937
+ label !== void 0 && /* @__PURE__ */ jsx(Label, { htmlFor: id, id: labelId, children: label }),
943
938
  /* @__PURE__ */ jsx(
944
939
  "input",
945
940
  {
946
- type: multiple ? "checkbox" : "radio",
947
- name: name || commonName,
948
- checked: isChecked,
949
- value,
950
- onChange: handleChange,
951
- ref,
952
- ...rest
941
+ name: name ?? label ?? id,
942
+ value: currentValue ?? void 0,
943
+ type: "hidden",
944
+ id
953
945
  }
954
946
  ),
955
- /* @__PURE__ */ jsx(
956
- "div",
957
- {
958
- className: classNames("eds-base-segmented", {
959
- "eds-base-segmented--large": size === "large"
960
- }),
961
- children
962
- }
963
- )
947
+ /* @__PURE__ */ jsx("div", { className: "eds-segmented-control__choices", children: React__default.Children.map(children, (child, index) => {
948
+ if (index === 0 && React__default.isValidElement(child))
949
+ return React__default.cloneElement(child, {
950
+ "data-first-child": true
951
+ });
952
+ return child;
953
+ }) })
964
954
  ]
965
955
  }
966
- );
956
+ ) });
967
957
  }
968
958
  );
969
- const SegmentedControl = ({
959
+ const defaultElement = "button";
960
+ const SegmentedChoice = React__default.forwardRef(function SegmentedChoice2({
970
961
  children,
971
- label,
972
- name,
973
- onChange,
974
- selectedValue,
975
- size = "medium",
976
962
  className,
963
+ style,
964
+ value,
965
+ as,
966
+ onChange,
967
+ "data-first-child": isFirstChild,
977
968
  ...rest
978
- }) => {
979
- return /* @__PURE__ */ jsx(
980
- SegmentedProvider,
981
- {
982
- name,
983
- selectedValue,
984
- onChange,
985
- multiple: false,
986
- size,
987
- children: /* @__PURE__ */ jsx(Fieldset, { label, children: /* @__PURE__ */ jsx(
988
- "div",
989
- {
990
- className: classNames("eds-segmented-control", className),
991
- ...rest,
992
- children
969
+ }, ref) {
970
+ const Element = as || defaultElement;
971
+ const {
972
+ value: selectedValue,
973
+ onChange: contextOnChange,
974
+ size,
975
+ focusedValue,
976
+ setFocusedValue
977
+ } = useSegmentedContext();
978
+ const isChecked = selectedValue === value;
979
+ const tabIndex = React__default.useMemo(() => {
980
+ if (selectedValue !== null) return isChecked ? 0 : -1;
981
+ return isFirstChild ? 0 : -1;
982
+ }, [isChecked, selectedValue, isFirstChild]);
983
+ const handleSelection = () => {
984
+ contextOnChange(value);
985
+ onChange?.(value);
986
+ };
987
+ const handleKeyDown = (e) => {
988
+ switch (e.key) {
989
+ case "ArrowLeft":
990
+ case "ArrowUp": {
991
+ e.preventDefault();
992
+ const prevSibling = e.currentTarget.previousElementSibling;
993
+ if (prevSibling && prevSibling instanceof HTMLElement) {
994
+ prevSibling.focus();
995
+ setFocusedValue(prevSibling.getAttribute("data-value") || null);
996
+ }
997
+ break;
998
+ }
999
+ case "ArrowRight":
1000
+ case "ArrowDown": {
1001
+ e.preventDefault();
1002
+ const nextSibling = e.currentTarget.nextElementSibling;
1003
+ if (nextSibling && nextSibling instanceof HTMLElement) {
1004
+ nextSibling.focus();
1005
+ setFocusedValue(nextSibling.getAttribute("data-value") || null);
1006
+ }
1007
+ break;
1008
+ }
1009
+ case "Home": {
1010
+ e.preventDefault();
1011
+ const firstSibling = e.currentTarget.parentElement?.firstElementChild;
1012
+ if (firstSibling && firstSibling instanceof HTMLElement) {
1013
+ firstSibling.focus();
1014
+ setFocusedValue(firstSibling.getAttribute("data-value") || null);
1015
+ }
1016
+ break;
1017
+ }
1018
+ case "End": {
1019
+ e.preventDefault();
1020
+ const lastSibling = e.currentTarget.parentElement?.lastElementChild;
1021
+ if (lastSibling && lastSibling instanceof HTMLElement) {
1022
+ lastSibling.focus();
1023
+ setFocusedValue(lastSibling.getAttribute("data-value") || null);
1024
+ }
1025
+ break;
1026
+ }
1027
+ case " ":
1028
+ e.preventDefault();
1029
+ if (as === "a") {
1030
+ handleSelection();
1031
+ const linkElement = e.currentTarget;
1032
+ if (linkElement.href) {
1033
+ queueMicrotask(() => {
1034
+ linkElement.click();
1035
+ });
1036
+ }
1037
+ } else {
1038
+ handleSelection();
993
1039
  }
994
- ) })
1040
+ break;
1041
+ case "Enter":
1042
+ handleSelection();
1043
+ break;
1044
+ case "Escape":
1045
+ e.preventDefault();
1046
+ setFocusedValue(null);
1047
+ break;
995
1048
  }
996
- );
997
- };
998
- const MultipleSegmentedControl = ({ children, label, name, onChange, selectedValue, ...rest }) => {
999
- return /* @__PURE__ */ jsxs(
1000
- SegmentedProvider,
1001
- {
1002
- name,
1003
- selectedValue,
1004
- onChange,
1005
- multiple: true,
1006
- size: "medium",
1007
- children: [
1008
- /* @__PURE__ */ jsx(Label, { as: "div", children: label }),
1009
- /* @__PURE__ */ jsx("div", { className: "eds-segmented-control", ...rest, children })
1010
- ]
1049
+ };
1050
+ const onFocus = React__default.useCallback(() => {
1051
+ setFocusedValue(value);
1052
+ }, [setFocusedValue, value]);
1053
+ const onBlur = React__default.useCallback(() => {
1054
+ if (focusedValue === value) {
1055
+ setFocusedValue(null);
1011
1056
  }
1012
- );
1013
- };
1057
+ }, [focusedValue, value, setFocusedValue]);
1058
+ const elementProps = {
1059
+ className: classNames(
1060
+ "eds-segmented-choice",
1061
+ {
1062
+ "eds-segmented-choice--large": size === "large"
1063
+ },
1064
+ className
1065
+ ),
1066
+ style,
1067
+ "aria-checked": isChecked,
1068
+ "data-value": value,
1069
+ ref,
1070
+ tabIndex,
1071
+ onClick: handleSelection,
1072
+ onKeyDown: handleKeyDown,
1073
+ onFocus,
1074
+ onBlur,
1075
+ role: "radio",
1076
+ // For defaultElement button we override type submit
1077
+ ...as === void 0 && { type: "button" },
1078
+ ...rest
1079
+ };
1080
+ return /* @__PURE__ */ jsx(Element, { ...elementProps, children });
1081
+ });
1082
+ const SegmentedControlComponent = Object.assign(
1083
+ SegmentedControl,
1084
+ {
1085
+ Item: SegmentedChoice
1086
+ }
1087
+ );
1088
+ SegmentedControlComponent.Item.displayName = "SegmentedControl.Item";
1014
1089
  warnAboutMissingStyles("form", "icons", "typography");
1015
1090
  export {
1016
1091
  BaseFormControl,
@@ -1020,12 +1095,12 @@ export {
1020
1095
  Fieldset,
1021
1096
  InputGroupContextProvider,
1022
1097
  InputGroupLabel,
1023
- MultipleSegmentedControl,
1024
1098
  Radio,
1025
1099
  RadioGroup,
1026
1100
  RadioPanel,
1027
1101
  SegmentedChoice,
1028
- SegmentedControl,
1102
+ SegmentedControlComponent as SegmentedControl,
1103
+ SegmentedControlComponent,
1029
1104
  Switch,
1030
1105
  TextArea,
1031
1106
  TextField,