@react-magma/charts 14.0.0-rc.3 → 14.0.0-rc.4

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/charts.js CHANGED
@@ -803,6 +803,148 @@ var curriedTransparentize = /*#__PURE__*/curry
803
803
  /* ::<number | string, string, string> */
804
804
  (transparentize);
805
805
 
806
+ var FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
807
+ function getFocusableElements(container) {
808
+ return Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)).filter(function (el) {
809
+ var style = window.getComputedStyle(el);
810
+ return (style.display !== 'none' &&
811
+ style.visibility !== 'hidden' &&
812
+ !el.hasAttribute('disabled'));
813
+ });
814
+ }
815
+ function findVisibleModal(wrapper) {
816
+ var modal = wrapper.querySelector('.cds--modal');
817
+ if (!modal)
818
+ return null;
819
+ var isVisible = modal.getAttribute('aria-modal') === 'true' ||
820
+ modal.style.visibility === 'visible' ||
821
+ modal.classList.contains('is-visible');
822
+ return isVisible ? modal : null;
823
+ }
824
+ function useCarbonModalFocusManagement(wrapperRef) {
825
+ var previouslyFocusedElement = React__namespace.useRef(null);
826
+ var keydownHandler = React__namespace.useRef(null);
827
+ var focusinHandler = React__namespace.useRef(null);
828
+ var currentModal = React__namespace.useRef(null);
829
+ React__namespace.useEffect(function () {
830
+ var wrapper = wrapperRef.current;
831
+ if (!wrapper)
832
+ return;
833
+ function focusModalCloseButton(modal) {
834
+ var closeButton = modal.querySelector('.cds--modal-close');
835
+ if (closeButton) {
836
+ closeButton.focus();
837
+ }
838
+ else {
839
+ var focusable = getFocusableElements(modal);
840
+ if (focusable.length > 0) {
841
+ focusable[0].focus();
842
+ }
843
+ }
844
+ }
845
+ function handleModalOpen(modal) {
846
+ currentModal.current = modal;
847
+ previouslyFocusedElement.current = document.activeElement;
848
+ // Permanent guard: redirect focus back into modal whenever it escapes
849
+ // (e.g. Carbon's overflow menu returning focus to its trigger).
850
+ focusinHandler.current = function (event) {
851
+ var target = event.target;
852
+ if (!modal.contains(target)) {
853
+ setTimeout(function () {
854
+ if (currentModal.current === modal) {
855
+ focusModalCloseButton(modal);
856
+ }
857
+ }, 0);
858
+ }
859
+ };
860
+ document.addEventListener('focusin', focusinHandler.current);
861
+ var pollAttempts = 0;
862
+ var pollAndFocus = function () {
863
+ if (currentModal.current !== modal)
864
+ return;
865
+ if (modal.contains(document.activeElement))
866
+ return;
867
+ var closeBtn = modal.querySelector('.cds--modal-close');
868
+ if (closeBtn &&
869
+ window.getComputedStyle(closeBtn).visibility !== 'hidden') {
870
+ closeBtn.focus();
871
+ return;
872
+ }
873
+ if (++pollAttempts < 30) {
874
+ requestAnimationFrame(pollAndFocus);
875
+ }
876
+ };
877
+ requestAnimationFrame(pollAndFocus);
878
+ keydownHandler.current = function (event) {
879
+ if (event.key !== 'Tab')
880
+ return;
881
+ var focusable = getFocusableElements(modal);
882
+ if (focusable.length === 0) {
883
+ event.preventDefault();
884
+ return;
885
+ }
886
+ if (focusable.length === 1) {
887
+ event.preventDefault();
888
+ if (focusable[0] !== document.activeElement) {
889
+ focusable[0].focus();
890
+ }
891
+ return;
892
+ }
893
+ var firstItem = focusable[0];
894
+ var lastItem = focusable[focusable.length - 1];
895
+ if (!event.shiftKey && document.activeElement === lastItem) {
896
+ event.preventDefault();
897
+ firstItem.focus();
898
+ }
899
+ else if (event.shiftKey && document.activeElement === firstItem) {
900
+ event.preventDefault();
901
+ lastItem.focus();
902
+ }
903
+ };
904
+ document.addEventListener('keydown', keydownHandler.current);
905
+ }
906
+ function handleModalClose() {
907
+ // Null out currentModal first so any pending setTimeout redirects
908
+ // (scheduled by the focusin guard) see a closed modal and bail out.
909
+ currentModal.current = null;
910
+ if (focusinHandler.current) {
911
+ document.removeEventListener('focusin', focusinHandler.current);
912
+ focusinHandler.current = null;
913
+ }
914
+ if (keydownHandler.current) {
915
+ document.removeEventListener('keydown', keydownHandler.current);
916
+ keydownHandler.current = null;
917
+ }
918
+ if (previouslyFocusedElement.current instanceof HTMLElement) {
919
+ previouslyFocusedElement.current.focus();
920
+ }
921
+ }
922
+ var observer = new MutationObserver(function () {
923
+ var visibleModal = findVisibleModal(wrapper);
924
+ if (visibleModal && !currentModal.current) {
925
+ handleModalOpen(visibleModal);
926
+ }
927
+ else if (!visibleModal && currentModal.current) {
928
+ handleModalClose();
929
+ }
930
+ });
931
+ observer.observe(wrapper, {
932
+ attributes: true,
933
+ attributeFilter: ['class', 'style', 'aria-modal'],
934
+ subtree: true,
935
+ });
936
+ return function () {
937
+ observer.disconnect();
938
+ if (keydownHandler.current) {
939
+ document.removeEventListener('keydown', keydownHandler.current);
940
+ }
941
+ if (focusinHandler.current) {
942
+ document.removeEventListener('focusin', focusinHandler.current);
943
+ }
944
+ };
945
+ }, [wrapperRef]);
946
+ }
947
+
806
948
  function styleInject(css, ref) {
807
949
  if ( ref === void 0 ) ref = {};
808
950
  var insertAt = ref.insertAt;
@@ -1011,6 +1153,17 @@ var CarbonChart = React__namespace.forwardRef(function (props, ref) {
1011
1153
  var testId = props.testId, isInverseProp = props.isInverse, type = props.type, dataSet = props.dataSet, options = props.options, ariaLabel = props.ariaLabel, rest = __rest(props, ["testId", "isInverse", "type", "dataSet", "options", "ariaLabel"]);
1012
1154
  var theme = React__namespace.useContext(reactMagmaDom.ThemeContext);
1013
1155
  var isInverse = reactMagmaDom.useIsInverse(isInverseProp);
1156
+ var internalRef = React__namespace.useRef(null);
1157
+ var mergedRef = React__namespace.useCallback(function (node) {
1158
+ internalRef.current = node;
1159
+ if (typeof ref === 'function') {
1160
+ ref(node);
1161
+ }
1162
+ else if (ref) {
1163
+ ref.current = node;
1164
+ }
1165
+ }, [ref]);
1166
+ useCarbonModalFocusManagement(internalRef);
1014
1167
  var allCharts = {
1015
1168
  area: chartsReact.AreaChart,
1016
1169
  areaStacked: chartsReact.StackedAreaChart,
@@ -1065,7 +1218,7 @@ var CarbonChart = React__namespace.forwardRef(function (props, ref) {
1065
1218
  }
1066
1219
  });
1067
1220
  var groupsLength = Object.keys(buildColors()).length;
1068
- return (React__namespace.createElement(CarbonChartWrapper, __assign({ "data-testid": testId, ref: ref, isInverse: isInverse, theme: theme, className: "carbon-chart-wrapper", groupsLength: groupsLength < 6 ? groupsLength : 14 }, rest),
1221
+ return (React__namespace.createElement(CarbonChartWrapper, __assign({ "data-testid": testId, ref: mergedRef, isInverse: isInverse, theme: theme, className: "carbon-chart-wrapper", groupsLength: groupsLength < 6 ? groupsLength : 14 }, rest),
1069
1222
  React__namespace.createElement(ChartType, { data: dataSet, options: newOptions })));
1070
1223
  });
1071
1224
  var templateObject_1;