colibri-ui 0.8.76 → 0.8.78

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.
Files changed (128) hide show
  1. package/Autocomplete/Autocomplete.d.ts +6 -0
  2. package/Autocomplete/Autocomplete.js +298 -0
  3. package/Autocomplete/Autocomplete.module.css +147 -0
  4. package/Autocomplete/index.d.ts +2 -0
  5. package/Autocomplete/index.js +7 -0
  6. package/Autocomplete/index.props.d.ts +39 -0
  7. package/Autocomplete/package.json +9 -0
  8. package/Button/tokens.d.ts +78 -0
  9. package/Button/tokens.js +84 -0
  10. package/Calendar/Calendar.js +3 -1
  11. package/Input/index.d.ts +1 -1
  12. package/Input/index.js +20 -14
  13. package/Input/index.props.d.ts +4 -1
  14. package/List/List.d.ts +5 -0
  15. package/List/List.js +161 -0
  16. package/List/List.module.css +185 -0
  17. package/List/index.d.ts +2 -0
  18. package/List/index.js +9 -0
  19. package/List/index.props.d.ts +43 -0
  20. package/List/package.json +9 -0
  21. package/Modal/Modal.module.css +14 -7
  22. package/Modal/components/Body/Body.module.css +12 -0
  23. package/Modal/components/Body/index.d.ts +3 -0
  24. package/Modal/components/Body/index.js +27 -0
  25. package/Modal/components/Body/index.props.d.ts +1 -0
  26. package/Modal/components/Close/index.js +5 -2
  27. package/Modal/components/Close/index.props.d.ts +1 -0
  28. package/Modal/components/Footer/Footer.module.css +10 -0
  29. package/Modal/components/Footer/index.d.ts +3 -0
  30. package/Modal/components/Footer/index.js +23 -0
  31. package/Modal/components/Footer/index.props.d.ts +1 -0
  32. package/Modal/components/Header/Header.module.css +32 -0
  33. package/Modal/components/Header/index.d.ts +3 -0
  34. package/Modal/components/Header/index.js +37 -0
  35. package/Modal/components/Header/index.props.d.ts +1 -0
  36. package/Modal/components/Title/index.js +20 -7
  37. package/Modal/components/Title/index.props.d.ts +1 -2
  38. package/Modal/context.d.ts +9 -0
  39. package/Modal/context.js +15 -0
  40. package/Modal/index.d.ts +12 -2
  41. package/Modal/index.js +103 -31
  42. package/Modal/index.props.d.ts +25 -0
  43. package/Modal/useModalStack.d.ts +2 -0
  44. package/Modal/useModalStack.js +20 -0
  45. package/README.md +59 -0
  46. package/Theme/breakpoints.d.ts +20 -16
  47. package/Theme/breakpoints.js +21 -32
  48. package/Theme/colors/index.d.ts +4 -0
  49. package/Theme/colors/theme_buenos_aires.d.ts +4 -0
  50. package/Theme/colors/theme_buenos_aires.js +2 -0
  51. package/Theme/colors/theme_jaipur.d.ts +4 -0
  52. package/Theme/colors/theme_jaipur.js +2 -0
  53. package/Theme/index.d.ts +300 -152
  54. package/Theme/index.js +6 -2
  55. package/Theme/themes/buenos_aires.d.ts +73 -76
  56. package/Theme/themes/buenos_aires.js +73 -76
  57. package/Theme/themes/jaipur.d.ts +73 -76
  58. package/Theme/themes/jaipur.js +73 -76
  59. package/esm/Autocomplete/Autocomplete.d.ts +6 -0
  60. package/esm/Autocomplete/Autocomplete.js +288 -0
  61. package/esm/Autocomplete/Autocomplete.module.css +147 -0
  62. package/esm/Autocomplete/index.d.ts +2 -0
  63. package/esm/Autocomplete/index.props.d.ts +39 -0
  64. package/esm/Button/tokens.d.ts +78 -0
  65. package/esm/Button/tokens.js +82 -0
  66. package/esm/Calendar/Calendar.js +3 -1
  67. package/esm/Input/index.d.ts +1 -1
  68. package/esm/Input/index.js +16 -10
  69. package/esm/Input/index.props.d.ts +4 -1
  70. package/esm/List/List.d.ts +5 -0
  71. package/esm/List/List.js +150 -0
  72. package/esm/List/List.module.css +185 -0
  73. package/esm/List/index.d.ts +2 -0
  74. package/esm/List/index.props.d.ts +43 -0
  75. package/esm/Modal/Modal.module.css +14 -7
  76. package/esm/Modal/components/Body/Body.module.css +12 -0
  77. package/esm/Modal/components/Body/index.d.ts +3 -0
  78. package/esm/Modal/components/Body/index.js +18 -0
  79. package/esm/Modal/components/Body/index.props.d.ts +1 -0
  80. package/esm/Modal/components/Close/index.js +4 -2
  81. package/esm/Modal/components/Close/index.props.d.ts +1 -0
  82. package/esm/Modal/components/Footer/Footer.module.css +10 -0
  83. package/esm/Modal/components/Footer/index.d.ts +3 -0
  84. package/esm/Modal/components/Footer/index.js +15 -0
  85. package/esm/Modal/components/Footer/index.props.d.ts +1 -0
  86. package/esm/Modal/components/Header/Header.module.css +32 -0
  87. package/esm/Modal/components/Header/index.d.ts +3 -0
  88. package/esm/Modal/components/Header/index.js +28 -0
  89. package/esm/Modal/components/Header/index.props.d.ts +1 -0
  90. package/esm/Modal/components/Title/index.js +20 -8
  91. package/esm/Modal/components/Title/index.props.d.ts +1 -2
  92. package/esm/Modal/context.d.ts +9 -0
  93. package/esm/Modal/context.js +12 -0
  94. package/esm/Modal/index.d.ts +12 -2
  95. package/esm/Modal/index.js +92 -25
  96. package/esm/Modal/index.props.d.ts +25 -0
  97. package/esm/Modal/useModalStack.d.ts +2 -0
  98. package/esm/Modal/useModalStack.js +18 -0
  99. package/esm/Theme/breakpoints.d.ts +20 -16
  100. package/esm/Theme/breakpoints.js +20 -26
  101. package/esm/Theme/colors/index.d.ts +4 -0
  102. package/esm/Theme/colors/theme_buenos_aires.d.ts +4 -0
  103. package/esm/Theme/colors/theme_buenos_aires.js +2 -0
  104. package/esm/Theme/colors/theme_jaipur.d.ts +4 -0
  105. package/esm/Theme/colors/theme_jaipur.js +2 -0
  106. package/esm/Theme/index.d.ts +300 -152
  107. package/esm/Theme/index.js +3 -2
  108. package/esm/Theme/themes/buenos_aires.d.ts +73 -76
  109. package/esm/Theme/themes/buenos_aires.js +73 -76
  110. package/esm/Theme/themes/jaipur.d.ts +73 -76
  111. package/esm/Theme/themes/jaipur.js +73 -76
  112. package/esm/hooks/useMediaSizes/index.d.ts +2 -2
  113. package/esm/hooks/useMediaSizes/index.js +2 -2
  114. package/esm/hooks/useTrapFocus/index.d.ts +1 -1
  115. package/esm/hooks/useTrapFocus/index.js +3 -2
  116. package/esm/index.d.ts +8 -3
  117. package/esm/index.js +7 -0
  118. package/esm/theme_ba_variables.css +149 -76
  119. package/esm/theme_jaipur_variables.css +149 -76
  120. package/hooks/useMediaSizes/index.d.ts +2 -2
  121. package/hooks/useMediaSizes/index.js +1 -1
  122. package/hooks/useTrapFocus/index.d.ts +1 -1
  123. package/hooks/useTrapFocus/index.js +3 -2
  124. package/index.d.ts +8 -3
  125. package/index.js +54 -35
  126. package/package.json +1 -1
  127. package/theme_ba_variables.css +149 -76
  128. package/theme_jaipur_variables.css +149 -76
@@ -0,0 +1,12 @@
1
+ .root {
2
+ box-sizing: border-box;
3
+ display: flex;
4
+ flex: 1 1 auto;
5
+ flex-direction: column;
6
+ min-height: 0;
7
+ overflow-y: auto;
8
+ padding: var(--space-4) var(--space-5);
9
+ }
10
+ .root_bleed {
11
+ padding: 0;
12
+ }
@@ -0,0 +1,3 @@
1
+ import type { FC } from "react";
2
+ import type { BodyProps } from "./index.props";
3
+ export declare const Body: FC<BodyProps>;
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ var _defineProperty = require('@babel/runtime/helpers/defineProperty');
4
+ var tslib = require('tslib');
5
+ var React = require('react');
6
+ var clsx = require('clsx');
7
+ var styles = require('./Body.module.css');
8
+
9
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
+
11
+ var _defineProperty__default = /*#__PURE__*/_interopDefault(_defineProperty);
12
+ var React__default = /*#__PURE__*/_interopDefault(React);
13
+ var clsx__default = /*#__PURE__*/_interopDefault(clsx);
14
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
15
+
16
+ var Body = function Body(_a) {
17
+ var children = _a.children,
18
+ className = _a.className,
19
+ _a$bleed = _a.bleed,
20
+ bleed = _a$bleed === void 0 ? false : _a$bleed,
21
+ props = tslib.__rest(_a, ["children", "className", "bleed"]);
22
+ return /*#__PURE__*/React__default.default.createElement("div", Object.assign({}, props, {
23
+ className: clsx__default.default(styles__default.default.root, _defineProperty__default.default({}, styles__default.default.root_bleed, bleed), className)
24
+ }), children);
25
+ };
26
+
27
+ exports.Body = Body;
@@ -0,0 +1 @@
1
+ export type { ModalBodyProps as BodyProps } from "../../index.props";
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var React = require('react');
4
+ var clsx = require('clsx');
4
5
  require('../../../Icons/index.js');
5
6
  var styles = require('./Close.module.css');
6
7
  var Close$1 = require('../../../Icons/Close.js');
@@ -8,16 +9,18 @@ var Close$1 = require('../../../Icons/Close.js');
8
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
10
 
10
11
  var React__default = /*#__PURE__*/_interopDefault(React);
12
+ var clsx__default = /*#__PURE__*/_interopDefault(clsx);
11
13
  var styles__default = /*#__PURE__*/_interopDefault(styles);
12
14
 
13
15
  var Close = function Close(_ref) {
14
- var onClick = _ref.onClick,
16
+ var className = _ref.className,
17
+ onClick = _ref.onClick,
15
18
  ariaLabel = _ref["aria-label"];
16
19
  return /*#__PURE__*/React__default.default.createElement("button", {
17
20
  type: "button",
18
21
  onClick: onClick,
19
22
  "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : "Close",
20
- className: styles__default.default["close-button"]
23
+ className: clsx__default.default(styles__default.default["close-button"], className)
21
24
  }, /*#__PURE__*/React__default.default.createElement(Close$1.Close, {
22
25
  width: 18,
23
26
  height: 18
@@ -1,4 +1,5 @@
1
1
  export type CloseButtonProps = {
2
+ className?: string;
2
3
  onClick?: () => void;
3
4
  "aria-label"?: string;
4
5
  };
@@ -0,0 +1,10 @@
1
+ .root {
2
+ align-items: center;
3
+ border-top: var(--border-width-hairline) solid var(--component-modal-border);
4
+ box-sizing: border-box;
5
+ display: flex;
6
+ flex: none;
7
+ gap: var(--space-3);
8
+ justify-content: flex-end;
9
+ padding: var(--space-4) var(--space-5);
10
+ }
@@ -0,0 +1,3 @@
1
+ import type { FC } from "react";
2
+ import type { FooterProps } from "./index.props";
3
+ export declare const Footer: FC<FooterProps>;
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ var tslib = require('tslib');
4
+ var React = require('react');
5
+ var clsx = require('clsx');
6
+ var styles = require('./Footer.module.css');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var React__default = /*#__PURE__*/_interopDefault(React);
11
+ var clsx__default = /*#__PURE__*/_interopDefault(clsx);
12
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
13
+
14
+ var Footer = function Footer(_a) {
15
+ var children = _a.children,
16
+ className = _a.className,
17
+ props = tslib.__rest(_a, ["children", "className"]);
18
+ return /*#__PURE__*/React__default.default.createElement("div", Object.assign({}, props, {
19
+ className: clsx__default.default(styles__default.default.root, className)
20
+ }), children);
21
+ };
22
+
23
+ exports.Footer = Footer;
@@ -0,0 +1 @@
1
+ export type { ModalFooterProps as FooterProps } from "../../index.props";
@@ -0,0 +1,32 @@
1
+ .root {
2
+ align-items: center;
3
+ border-bottom: var(--border-width-hairline) solid var(--component-modal-border);
4
+ box-sizing: border-box;
5
+ display: flex;
6
+ flex: none;
7
+ gap: var(--space-3);
8
+ padding: var(--space-4) var(--space-5);
9
+ }
10
+ .root_overlay {
11
+ align-items: flex-start;
12
+ background: transparent;
13
+ border-bottom: none;
14
+ left: 0;
15
+ pointer-events: none;
16
+ position: absolute;
17
+ right: 0;
18
+ top: 0;
19
+ z-index: 1;
20
+ }
21
+ .root_overlay > * {
22
+ pointer-events: auto;
23
+ }
24
+
25
+ .close {
26
+ margin-left: auto;
27
+ }
28
+ .root_overlay .close {
29
+ background-color: var(--component-modal-bg);
30
+ border-radius: var(--component-modal-radius);
31
+ box-shadow: var(--shadow-raised);
32
+ }
@@ -0,0 +1,3 @@
1
+ import type { FC } from "react";
2
+ import type { HeaderProps } from "./index.props";
3
+ export declare const Header: FC<HeaderProps>;
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ var _defineProperty = require('@babel/runtime/helpers/defineProperty');
4
+ var tslib = require('tslib');
5
+ var React = require('react');
6
+ var clsx = require('clsx');
7
+ var context = require('../../context.js');
8
+ var index = require('../Close/index.js');
9
+ var styles = require('./Header.module.css');
10
+
11
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
+
13
+ var _defineProperty__default = /*#__PURE__*/_interopDefault(_defineProperty);
14
+ var React__default = /*#__PURE__*/_interopDefault(React);
15
+ var clsx__default = /*#__PURE__*/_interopDefault(clsx);
16
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
17
+
18
+ var Header = function Header(_a) {
19
+ var children = _a.children,
20
+ className = _a.className,
21
+ _a$overlay = _a.overlay,
22
+ overlay = _a$overlay === void 0 ? false : _a$overlay,
23
+ _a$closeAriaLabel = _a.closeAriaLabel,
24
+ closeAriaLabel = _a$closeAriaLabel === void 0 ? "Close modal" : _a$closeAriaLabel,
25
+ props = tslib.__rest(_a, ["children", "className", "overlay", "closeAriaLabel"]);
26
+ var _useModalContext = context.useModalContext(),
27
+ onClose = _useModalContext.onClose;
28
+ return /*#__PURE__*/React__default.default.createElement("div", Object.assign({}, props, {
29
+ className: clsx__default.default(styles__default.default.root, _defineProperty__default.default({}, styles__default.default.root_overlay, overlay), className)
30
+ }), children, /*#__PURE__*/React__default.default.createElement(index.Close, {
31
+ className: styles__default.default.close,
32
+ onClick: onClose,
33
+ "aria-label": closeAriaLabel
34
+ }));
35
+ };
36
+
37
+ exports.Header = Header;
@@ -0,0 +1 @@
1
+ export type { ModalHeaderProps as HeaderProps } from "../../index.props";
@@ -1,23 +1,36 @@
1
1
  'use strict';
2
2
 
3
+ var tslib = require('tslib');
3
4
  var React = require('react');
5
+ var clsx = require('clsx');
4
6
  var index = require('../../../Typography/index.js');
7
+ var context = require('../../context.js');
5
8
  var styles = require('./Title.module.css');
6
9
 
7
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
11
 
9
12
  var React__default = /*#__PURE__*/_interopDefault(React);
13
+ var clsx__default = /*#__PURE__*/_interopDefault(clsx);
10
14
  var styles__default = /*#__PURE__*/_interopDefault(styles);
11
15
 
12
- var Title = function Title(_ref) {
13
- var children = _ref.children;
16
+ var Title = function Title(_a) {
17
+ var children = _a.children,
18
+ className = _a.className,
19
+ props = tslib.__rest(_a, ["children", "className"]);
20
+ var _useModalContext = context.useModalContext(),
21
+ titleId = _useModalContext.titleId,
22
+ registerTitle = _useModalContext.registerTitle;
23
+ React.useEffect(function () {
24
+ return registerTitle();
25
+ }, [registerTitle]);
14
26
  return /*#__PURE__*/React__default.default.createElement(index.Typography, {
15
- tag: "h3",
16
- size: "h3",
27
+ tag: "h4",
28
+ size: "h4",
17
29
  className: styles__default.default.title
18
- }, /*#__PURE__*/React__default.default.createElement("div", {
19
- className: styles__default.default.wrapper
20
- }, children));
30
+ }, /*#__PURE__*/React__default.default.createElement("div", Object.assign({}, props, {
31
+ id: titleId,
32
+ className: clsx__default.default(styles__default.default.wrapper, className)
33
+ }), children));
21
34
  };
22
35
 
23
36
  exports.Title = Title;
@@ -1,2 +1 @@
1
- import type { PropsWithChildren } from "react";
2
- export type TitleProps = PropsWithChildren;
1
+ export type { ModalTitleProps as TitleProps } from "../../index.props";
@@ -0,0 +1,9 @@
1
+ type ModalContextValue = {
2
+ onClose: () => void;
3
+ titleId: string;
4
+ registerTitle: () => () => void;
5
+ container: HTMLDivElement | null;
6
+ };
7
+ export declare const ModalContext: import("react").Context<ModalContextValue | null>;
8
+ export declare const useModalContext: () => ModalContextValue;
9
+ export {};
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+
5
+ var ModalContext = /*#__PURE__*/React.createContext(null);
6
+ var useModalContext = function useModalContext() {
7
+ var context = React.useContext(ModalContext);
8
+ if (!context) {
9
+ throw new Error("Modal compound components must be used inside <Modal>");
10
+ }
11
+ return context;
12
+ };
13
+
14
+ exports.ModalContext = ModalContext;
15
+ exports.useModalContext = useModalContext;
package/Modal/index.d.ts CHANGED
@@ -1,3 +1,13 @@
1
- import type { FC, PropsWithChildren } from "react";
1
+ import React from "react";
2
+ import { Body } from "./components/Body";
3
+ import { Footer } from "./components/Footer";
4
+ import { Header } from "./components/Header";
5
+ import { Title } from "./components/Title";
2
6
  import type { ModalProps } from "./index.props";
3
- export declare const Modal: FC<PropsWithChildren<ModalProps>>;
7
+ export declare const Modal: React.FC<React.PropsWithChildren<ModalProps>> & {
8
+ Header: React.FC<import("./index.props").ModalHeaderProps>;
9
+ Title: React.FC<import("./index.props").ModalTitleProps>;
10
+ Body: React.FC<import("./index.props").ModalBodyProps>;
11
+ Footer: React.FC<import("./index.props").ModalFooterProps>;
12
+ };
13
+ export { Header as ModalHeader, Title as ModalTitle, Body as ModalBody, Footer as ModalFooter, };
package/Modal/index.js CHANGED
@@ -1,38 +1,91 @@
1
1
  'use strict';
2
2
 
3
3
  var _defineProperty = require('@babel/runtime/helpers/defineProperty');
4
+ var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
4
5
  var React = require('react');
5
6
  var clsx = require('clsx');
6
- var index$1 = require('../hooks/useScrollLock/index.js');
7
- var index$2 = require('../hooks/useTrapFocus/index.js');
8
- var index$3 = require('../Portal/index.js');
9
- var index = require('../hooks/useMediaSizes/index.js');
10
- var index$6 = require('./components/Content/index.js');
11
- var index$5 = require('./components/Close/index.js');
12
- var index$4 = require('./components/Title/index.js');
7
+ var index$5 = require('../hooks/useScrollLock/index.js');
8
+ var index$6 = require('../hooks/useTrapFocus/index.js');
9
+ var index$9 = require('../Portal/index.js');
10
+ var index$4 = require('../hooks/useMediaSizes/index.js');
11
+ var index = require('./components/Body/index.js');
12
+ var index$7 = require('./components/Close/index.js');
13
+ var index$8 = require('./components/Content/index.js');
14
+ var index$1 = require('./components/Footer/index.js');
15
+ var index$2 = require('./components/Header/index.js');
16
+ var index$3 = require('./components/Title/index.js');
17
+ var context = require('./context.js');
18
+ var useModalStack = require('./useModalStack.js');
13
19
  var styles = require('./Modal.module.css');
14
20
 
15
21
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
22
 
17
23
  var _defineProperty__default = /*#__PURE__*/_interopDefault(_defineProperty);
24
+ var _slicedToArray__default = /*#__PURE__*/_interopDefault(_slicedToArray);
18
25
  var React__default = /*#__PURE__*/_interopDefault(React);
19
26
  var clsx__default = /*#__PURE__*/_interopDefault(clsx);
20
27
  var styles__default = /*#__PURE__*/_interopDefault(styles);
21
28
 
22
- var Modal = function Modal(_ref) {
29
+ var compoundParts = [index$2.Header, index.Body, index$1.Footer];
30
+ var isCompoundPart = function isCompoundPart(child) {
31
+ return /*#__PURE__*/React.isValidElement(child) && compoundParts.includes(child.type);
32
+ };
33
+ var hasHeader = function hasHeader(children) {
34
+ return React.Children.toArray(children).some(function (child) {
35
+ return /*#__PURE__*/React.isValidElement(child) && child.type === index$2.Header;
36
+ });
37
+ };
38
+ var ModalRoot = function ModalRoot(_ref) {
23
39
  var children = _ref.children,
24
40
  className = _ref.className,
25
41
  onClose = _ref.onClose,
26
- title = _ref.title;
42
+ title = _ref.title,
43
+ _ref$withinParent = _ref.withinParent,
44
+ withinParent = _ref$withinParent === void 0 ? false : _ref$withinParent;
27
45
  var modalRef = React.useRef(null);
28
- var isDesktop = index.useMediaSizes(function (bp) {
29
- return bp.up("md");
46
+ var _useState = React.useState(null),
47
+ _useState2 = _slicedToArray__default.default(_useState, 2),
48
+ container = _useState2[0],
49
+ setContainer = _useState2[1];
50
+ var parentModal = React.useContext(context.ModalContext);
51
+ var parentContainer = withinParent ? parentModal === null || parentModal === void 0 ? void 0 : parentModal.container : undefined;
52
+ var isContained = Boolean(parentContainer);
53
+ var isDesktop = index$4.useMediaSizes(function (bp) {
54
+ return bp.up("lg");
30
55
  });
31
- index$1.useScrollLock();
32
- index$2.useTrapFocus(modalRef);
56
+ var titleId = React.useId();
57
+ var _useState3 = React.useState(0),
58
+ _useState4 = _slicedToArray__default.default(_useState3, 2),
59
+ titleCount = _useState4[0],
60
+ setTitleCount = _useState4[1];
61
+ var isTopmost = useModalStack.useModalStack(titleId);
62
+ index$5.useScrollLock();
63
+ index$6.useTrapFocus(modalRef, isTopmost);
33
64
  var handleClose = React.useCallback(function () {
34
65
  onClose === null || onClose === void 0 ? void 0 : onClose();
35
66
  }, [onClose]);
67
+ var registerTitle = React.useCallback(function () {
68
+ setTitleCount(function (count) {
69
+ return count + 1;
70
+ });
71
+ return function () {
72
+ return setTitleCount(function (count) {
73
+ return count - 1;
74
+ });
75
+ };
76
+ }, []);
77
+ var setWrapperRef = React.useCallback(function (node) {
78
+ modalRef.current = node;
79
+ setContainer(node);
80
+ }, []);
81
+ var contextValue = React.useMemo(function () {
82
+ return {
83
+ onClose: handleClose,
84
+ titleId: titleId,
85
+ registerTitle: registerTitle,
86
+ container: container
87
+ };
88
+ }, [handleClose, titleId, registerTitle, container]);
36
89
  var handleOverlayClick = React.useCallback(function (event) {
37
90
  if (event.target === event.currentTarget) {
38
91
  handleClose();
@@ -40,7 +93,7 @@ var Modal = function Modal(_ref) {
40
93
  }, [handleClose]);
41
94
  React.useEffect(function () {
42
95
  var handleEscape = function handleEscape(event) {
43
- if (event.key === "Escape") {
96
+ if (event.key === "Escape" && isTopmost()) {
44
97
  handleClose();
45
98
  }
46
99
  };
@@ -48,30 +101,49 @@ var Modal = function Modal(_ref) {
48
101
  return function () {
49
102
  return document.removeEventListener("keydown", handleEscape);
50
103
  };
51
- }, [handleClose]);
52
- return /*#__PURE__*/React__default.default.createElement(index$3.Portal, null, /*#__PURE__*/React__default.default.createElement("div", {
53
- className: styles__default.default.root,
104
+ }, [handleClose, isTopmost]);
105
+ var isCompound = React.Children.toArray(children).some(isCompoundPart);
106
+ var standaloneClose = /*#__PURE__*/React__default.default.createElement("div", {
107
+ className: styles__default.default["close-wrapper"]
108
+ }, /*#__PURE__*/React__default.default.createElement(index$7.Close, {
109
+ onClick: handleClose,
110
+ "aria-label": "Close modal"
111
+ }));
112
+ var content;
113
+ if (isCompound) {
114
+ content = /*#__PURE__*/React__default.default.createElement(React__default.default.Fragment, null, !hasHeader(children) && standaloneClose, children);
115
+ } else {
116
+ content = /*#__PURE__*/React__default.default.createElement(React__default.default.Fragment, null, title ? (/*#__PURE__*/React__default.default.createElement(index$2.Header, null, /*#__PURE__*/React__default.default.createElement(index$3.Title, null, title))) : standaloneClose, /*#__PURE__*/React__default.default.createElement(index$8.Content, null, children));
117
+ }
118
+ // Wait for the parent window node instead of flashing in document.body.
119
+ if (withinParent && parentModal && !parentContainer) return null;
120
+ return /*#__PURE__*/React__default.default.createElement(index$9.Portal, {
121
+ node: parentContainer
122
+ }, /*#__PURE__*/React__default.default.createElement(context.ModalContext.Provider, {
123
+ value: contextValue
124
+ }, /*#__PURE__*/React__default.default.createElement("div", {
125
+ className: clsx__default.default(styles__default.default.root, _defineProperty__default.default({}, styles__default.default.root_contained, isContained)),
54
126
  role: "dialog",
55
127
  "aria-modal": "true",
56
- "aria-labelledby": title ? "modal-title" : undefined
128
+ "aria-labelledby": titleCount > 0 ? titleId : undefined
57
129
  }, /*#__PURE__*/React__default.default.createElement("div", {
58
130
  className: styles__default.default["modal-overlay"],
59
131
  onClick: handleOverlayClick,
60
132
  "aria-hidden": "true"
61
133
  }), /*#__PURE__*/React__default.default.createElement("div", {
62
- className: clsx__default.default(styles__default.default["modal-wrapper"], _defineProperty__default.default(_defineProperty__default.default({}, styles__default.default["modal-wrapper_desktop"], isDesktop), styles__default.default["modal-wrapper_mobile"], !isDesktop), className),
63
- ref: modalRef
64
- }, title ? (/*#__PURE__*/React__default.default.createElement("div", {
65
- className: styles__default.default["title-wrapper"]
66
- }, /*#__PURE__*/React__default.default.createElement(index$4.Title, null, title), /*#__PURE__*/React__default.default.createElement(index$5.Close, {
67
- onClick: handleClose,
68
- "aria-label": "Close modal"
69
- }))) : (/*#__PURE__*/React__default.default.createElement("div", {
70
- className: styles__default.default["close-wrapper"]
71
- }, /*#__PURE__*/React__default.default.createElement(index$5.Close, {
72
- onClick: handleClose,
73
- "aria-label": "Close modal"
74
- }))), /*#__PURE__*/React__default.default.createElement(index$6.Content, null, children))));
134
+ className: clsx__default.default(styles__default.default["modal-wrapper"], _defineProperty__default.default(_defineProperty__default.default(_defineProperty__default.default({}, styles__default.default["modal-wrapper_desktop"], isDesktop), styles__default.default["modal-wrapper_mobile"], !isDesktop), styles__default.default["modal-wrapper_contained"], isContained), className),
135
+ ref: setWrapperRef
136
+ }, content))));
75
137
  };
138
+ var Modal = Object.assign(ModalRoot, {
139
+ Header: index$2.Header,
140
+ Title: index$3.Title,
141
+ Body: index.Body,
142
+ Footer: index$1.Footer
143
+ });
76
144
 
145
+ exports.ModalBody = index.Body;
146
+ exports.ModalFooter = index$1.Footer;
147
+ exports.ModalHeader = index$2.Header;
148
+ exports.ModalTitle = index$3.Title;
77
149
  exports.Modal = Modal;
@@ -1,5 +1,30 @@
1
+ import type { HTMLAttributes } from "react";
1
2
  export type ModalProps = {
2
3
  className?: string;
3
4
  onClose?: () => void;
5
+ /**
6
+ * Legacy shorthand. Used only when children do not contain
7
+ * `Modal.Header`, `Modal.Body` or `Modal.Footer`.
8
+ */
4
9
  title?: string;
10
+ /**
11
+ * Renders the modal inside the parent modal window: the overlay covers
12
+ * only the parent, not the whole viewport. Works when the modal is
13
+ * rendered inside another `Modal`.
14
+ */
15
+ withinParent?: boolean;
5
16
  };
17
+ export interface ModalHeaderProps extends HTMLAttributes<HTMLDivElement> {
18
+ /**
19
+ * Places the header above the body instead of before it.
20
+ * The header becomes transparent and the body takes the whole modal.
21
+ */
22
+ overlay?: boolean;
23
+ closeAriaLabel?: string;
24
+ }
25
+ export type ModalTitleProps = HTMLAttributes<HTMLDivElement>;
26
+ export interface ModalBodyProps extends HTMLAttributes<HTMLDivElement> {
27
+ /** Removes the body padding so content goes edge to edge. */
28
+ bleed?: boolean;
29
+ }
30
+ export type ModalFooterProps = HTMLAttributes<HTMLDivElement>;
@@ -0,0 +1,2 @@
1
+ /** Tracks open modals so only the topmost one reacts to Escape and Tab. */
2
+ export declare const useModalStack: (id: string) => () => boolean;
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+
5
+ var stack = [];
6
+ /** Tracks open modals so only the topmost one reacts to Escape and Tab. */
7
+ var useModalStack = function useModalStack(id) {
8
+ React.useEffect(function () {
9
+ stack.push(id);
10
+ return function () {
11
+ var index = stack.lastIndexOf(id);
12
+ if (index !== -1) stack.splice(index, 1);
13
+ };
14
+ }, [id]);
15
+ return React.useCallback(function () {
16
+ return stack[stack.length - 1] === id;
17
+ }, [id]);
18
+ };
19
+
20
+ exports.useModalStack = useModalStack;
package/README.md CHANGED
@@ -131,6 +131,65 @@ If you add a theme inside the library itself, create both layers:
131
131
 
132
132
  Then export it from `src/components/Theme/index.tsx` and update `scripts/variables.ts` if you want a generated CSS variables file in the package build.
133
133
 
134
+ ## Breakpoints
135
+
136
+ Colibri UI uses one fixed set of breakpoints, shared with the apps that use the kit.
137
+
138
+ | Name | Value | px |
139
+ | ----- | ------- | ---- |
140
+ | `sm` | `40rem` | 640 |
141
+ | `md` | `48rem` | 768 |
142
+ | `lg` | `64rem` | 1024 |
143
+ | `xl` | `80rem` | 1280 |
144
+ | `2xl` | `96rem` | 1536 |
145
+
146
+ The values match the Tailwind CSS defaults on purpose, because they are an industry standard. Tailwind is not a dependency of Colibri UI.
147
+
148
+ Rules:
149
+
150
+ - **Mobile first.** Base styles are for screens narrower than `sm`. There is no `xs`.
151
+ - **rem, not px.** Layout follows the user's browser font size.
152
+ - **Range syntax only.** Write `(width >= 48rem)` and `(width < 48rem)`. Do not use `min-width` / `max-width` or `- 1px` arithmetic: each edge belongs to exactly one side.
153
+
154
+ ### CSS
155
+
156
+ CSS cannot read variables inside media queries, so write the value as a literal:
157
+
158
+ ```css
159
+ .card {
160
+ padding: var(--space-4);
161
+ }
162
+
163
+ @media (width >= 64rem) {
164
+ .card {
165
+ padding: var(--space-6);
166
+ }
167
+ }
168
+ ```
169
+
170
+ Inside the kit, a test and a stylelint rule reject `min-width` / `max-width` and any width value outside the table.
171
+
172
+ ### JavaScript
173
+
174
+ ```ts
175
+ import { BREAKPOINTS, media, useMediaSizes } from "colibri-ui";
176
+
177
+ BREAKPOINTS.md; // "48rem"
178
+ media.up("md"); // "(width >= 48rem)"
179
+ media.down("md"); // "(width < 48rem)"
180
+ media.between("md", "lg"); // "(48rem <= width < 64rem)"
181
+
182
+ window.matchMedia(media.up("lg")).matches;
183
+
184
+ // in a component
185
+ const isDesktop = useMediaSizes((bp) => bp.up("lg"));
186
+
187
+ // CSS-in-JS: add the @media prefix yourself
188
+ const css = `@media ${media.down("sm")} { … }`;
189
+ ```
190
+
191
+ `BREAKPOINTS` and `media` are also exported from `colibri-ui/Theme`.
192
+
134
193
  ## License
135
194
 
136
195
  MIT
@@ -1,18 +1,22 @@
1
- export type Breakpoints = {
2
- hd: number;
3
- xl: number;
4
- lg: number;
5
- md: number;
6
- sm: number;
7
- xs: number;
1
+ /**
2
+ * Breakpoints shared with the apps that use the kit.
3
+ * The values match the Tailwind CSS defaults on purpose; Tailwind is not a dependency.
4
+ */
5
+ export declare const BREAKPOINTS: {
6
+ readonly sm: "40rem";
7
+ readonly md: "48rem";
8
+ readonly lg: "64rem";
9
+ readonly xl: "80rem";
10
+ readonly "2xl": "96rem";
8
11
  };
9
- export type BreakpointName = keyof Breakpoints;
10
- export type BreakpointsTheme = {
11
- values: Breakpoints;
12
- up: (breakpoint: BreakpointName, isMedial?: boolean) => string;
13
- under: (breakpoint: BreakpointName, isMedial?: boolean) => string;
14
- between: ([from, to]: [BreakpointName, BreakpointName], isMedial?: boolean) => string;
12
+ export type BreakpointName = keyof typeof BREAKPOINTS;
13
+ export type Media = {
14
+ /** `(width >= <name>)` */
15
+ up: (name: BreakpointName) => string;
16
+ /** `(width < <name>)` */
17
+ down: (name: BreakpointName) => string;
18
+ /** `(<from> <= width < <to>)` */
19
+ between: (from: BreakpointName, to: BreakpointName) => string;
15
20
  };
16
- export type BreakpointMap<T = number> = Map<BreakpointName, T>;
17
- declare const Breakpoints: BreakpointsTheme;
18
- export default Breakpoints;
21
+ /** Bare media queries, usable in `window.matchMedia`. Prefix with `@media ` in CSS-in-JS. */
22
+ export declare const media: Media;