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