@uncinc/uncinc-react-kitchen-sink 2.3.6 → 2.4.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.
Files changed (104) hide show
  1. package/dist/Context/ViewportContext.js +5 -5
  2. package/dist/components/AbbreviationAnnotation/stories.js +1 -1
  3. package/dist/components/AbbreviationAnnotation/stories.scss +1 -1
  4. package/dist/components/AbbreviationAnnotation/style.scss +0 -2
  5. package/dist/components/Accordion/AccordionItem/index.js +1 -0
  6. package/dist/components/Accordion/stories.js +1 -1
  7. package/dist/components/ApiAutocomplete/stories.js +1 -1
  8. package/dist/components/Button/index.js +11 -7
  9. package/dist/components/Button/stories.js +1 -1
  10. package/dist/components/Cookie/Checkbox/index.js +1 -1
  11. package/dist/components/Cookie/index.js +28 -13
  12. package/dist/components/Cookie/stories.js +16 -16
  13. package/dist/components/Dialog/index.js +89 -0
  14. package/dist/components/Dialog/stories.js +28 -0
  15. package/dist/components/Dialog/style.scss +72 -0
  16. package/dist/components/FontSizeSwitcher/index.js +10 -4
  17. package/dist/components/FontSizeSwitcher/stories.js +1 -1
  18. package/dist/components/Form/Checkbox/index.js +1 -1
  19. package/dist/components/Form/Checkboxes/index.js +1 -1
  20. package/dist/components/Form/Checkboxes/index.test.js +1 -1
  21. package/dist/components/Form/DatePicker/index.js +3 -3
  22. package/dist/components/Form/DateSelect/index.js +3 -4
  23. package/dist/components/Form/DateSelect/stories.js +1 -1
  24. package/dist/components/Form/GenericFormElement/index.js +2 -2
  25. package/dist/components/Form/Number/index.js +1 -1
  26. package/dist/components/Form/SearchField/index.js +2 -2
  27. package/dist/components/Form/Select/Input/index.js +21 -23
  28. package/dist/components/Form/Select/index.js +1 -1
  29. package/dist/components/Form/TextField/index.js +2 -2
  30. package/dist/components/Form/TextField/index.test.js +1 -1
  31. package/dist/components/Form/TextField/stories.js +1 -1
  32. package/dist/components/Form/Textarea/index.js +1 -1
  33. package/dist/components/Hamburger/stories.js +3 -3
  34. package/dist/components/Header/index.js +3 -3
  35. package/dist/components/Header/stories.js +1 -1
  36. package/dist/components/Image/Picture/index.js +0 -1
  37. package/dist/components/Image/stories.js +9 -9
  38. package/dist/components/InlineMenu/index.js +2 -4
  39. package/dist/components/InlineMenu/stories.js +1 -1
  40. package/dist/components/Layout/Container/stories.js +29 -29
  41. package/dist/components/Layout/QuickLayout/stories.js +1 -1
  42. package/dist/components/Lottie/LottieAnimation/index.js +21 -17
  43. package/dist/components/Lottie/LottieAnimationWithMousePosition.js +4 -4
  44. package/dist/components/Lottie/index.js +3 -2
  45. package/dist/components/Lottie/stories.js +1 -1
  46. package/dist/components/MagicLine/index.js +3 -3
  47. package/dist/components/MagicLine/stories.js +4 -4
  48. package/dist/components/NavigationScroll/index.js +8 -11
  49. package/dist/components/NavigationScroll/stories.js +1 -1
  50. package/dist/components/Pagination/index.js +6 -0
  51. package/dist/components/Pagination/stories.js +11 -11
  52. package/dist/components/SearchKit/Facets/ChipFacet/index.js +1 -1
  53. package/dist/components/SearchKit/Facets/ListFacet/index.js +1 -0
  54. package/dist/components/SearchKit/Facets/SelectFacet/index.js +2 -2
  55. package/dist/components/SearchKit/SearchFacets/index.js +1 -3
  56. package/dist/components/SearchKit/SearchField/index.js +4 -4
  57. package/dist/components/SearchKit/SearchPage/index.js +9 -2
  58. package/dist/components/SearchKit/SearchReset/index.js +20 -8
  59. package/dist/components/SearchKit/SearchResults/index.js +10 -6
  60. package/dist/components/SearchKit/SearchSelectedFilters/Filters/CustomValue/index.js +15 -0
  61. package/dist/components/SearchKit/SearchSelectedFilters/Filters/DateRange/index.js +17 -0
  62. package/dist/components/SearchKit/SearchSelectedFilters/Filters/NumericRange/index.js +17 -0
  63. package/dist/components/SearchKit/SearchSelectedFilters/Filters/Value/index.js +19 -0
  64. package/dist/components/SearchKit/SearchSelectedFilters/index.js +30 -23
  65. package/dist/components/SearchKit/SearchSelectedFilters/style.scss +4 -0
  66. package/dist/components/SearchKit/SearchSorting/index.js +1 -1
  67. package/dist/components/SearchKit/index.js +10 -3
  68. package/dist/components/SearchKit/stories.js +83 -77
  69. package/dist/components/Share/index.js +4 -4
  70. package/dist/components/Share/stories.js +1 -1
  71. package/dist/components/Sliders/SliderRelated/index.js +1 -1
  72. package/dist/components/Sliders/SliderRelated/stories.js +13 -13
  73. package/dist/components/Webform/WebformDate/index.js +5 -4
  74. package/dist/components/Webform/WebformNumber/index.js +1 -1
  75. package/dist/components/Webform/WebformTextField/index.js +1 -1
  76. package/dist/components/Webform/stories.actions.js +17 -17
  77. package/dist/components/Webform/stories.checkboxes.js +22 -22
  78. package/dist/components/Webform/stories.checked.js +35 -35
  79. package/dist/components/Webform/stories.container.js +29 -29
  80. package/dist/components/Webform/stories.date.js +17 -17
  81. package/dist/components/Webform/stories.filled.js +35 -35
  82. package/dist/components/Webform/stories.number.js +18 -18
  83. package/dist/components/Webform/stories.radiobuttons.js +22 -22
  84. package/dist/components/Webform/stories.select.js +22 -22
  85. package/dist/components/Webform/stories.textarea.js +1 -1
  86. package/dist/components/Webform/stories.textfield.js +1 -1
  87. package/dist/components/Webform/stories.wizard.js +115 -115
  88. package/dist/components/WysiwygField/index.js +0 -1
  89. package/dist/components/WysiwygField/stories.js +2 -2
  90. package/dist/config/i18next/locales/en.json +4 -0
  91. package/dist/config/i18next/locales/nl.json +7 -0
  92. package/dist/helpers/date.js +8 -0
  93. package/dist/hooks/useBoolean.ts +20 -0
  94. package/dist/hooks/useClickOutside.ts +38 -0
  95. package/dist/hooks/useDialog.ts +29 -0
  96. package/dist/hooks/useDialogHelpers.ts +95 -0
  97. package/dist/hooks/useEscape.ts +26 -0
  98. package/dist/hooks/usePrevious.js +1 -2
  99. package/dist/hooks/useTrapFocus.ts +80 -0
  100. package/dist/index.js +1 -0
  101. package/package.json +39 -17
  102. package/dist/components/Form/SearchField/stories.js +0 -0
  103. package/dist/components/SearchKit/Facets/HierarchicalFilterFacet/index.js +0 -60
  104. package/dist/components/SearchKit/Facets/HierarchicalMenuFacet/index.js +0 -60
@@ -1,32 +1,32 @@
1
1
  import React from 'react';
2
2
  import Immutable from 'immutable';
3
- import Webform from './';
3
+ import Webform from '.';
4
4
  export default {
5
5
  title: 'Organisms/Webform/Mapper'
6
6
  };
7
7
  const entities = Immutable.fromJS({
8
- "field_3": {
9
- "#type": "select",
10
- "#title": "Pick your poison",
11
- '#description': "Please select your poison.",
12
- "#options": {
13
- "beer": "Beer",
14
- "wine": "Wine",
15
- "rum": "Rum",
16
- "whisky": "Whisk(e)y"
8
+ 'field_3': {
9
+ '#type': 'select',
10
+ '#title': 'Pick your poison',
11
+ '#description': 'Please select your poison.',
12
+ '#options': {
13
+ 'beer': 'Beer',
14
+ 'wine': 'Wine',
15
+ 'rum': 'Rum',
16
+ 'whisky': 'Whisk(e)y'
17
17
  },
18
- "#webform": "test_conditionals",
19
- "#webform_id": "test_conditionals--field_3",
20
- "#webform_key": "field_3",
21
- "#webform_parent_key": "",
22
- "#webform_parent_flexbox": false,
23
- "#webform_depth": 0,
24
- "#webform_children": [],
25
- "#webform_multiple": false,
26
- "#webform_composite": false,
27
- "#webform_parents": ["field_3"],
28
- "#admin_title": "Pick your poison",
29
- "#webform_plugin_id": "select"
18
+ '#webform': 'test_conditionals',
19
+ '#webform_id': 'test_conditionals--field_3',
20
+ '#webform_key': 'field_3',
21
+ '#webform_parent_key': '',
22
+ '#webform_parent_flexbox': false,
23
+ '#webform_depth': 0,
24
+ '#webform_children': [],
25
+ '#webform_multiple': false,
26
+ '#webform_composite': false,
27
+ '#webform_parents': ['field_3'],
28
+ '#admin_title': 'Pick your poison',
29
+ '#webform_plugin_id': 'select'
30
30
  }
31
31
  });
32
32
  export const Select = () => /*#__PURE__*/React.createElement(Webform, {
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import Immutable from 'immutable';
3
- import Webform from './';
3
+ import Webform from '.';
4
4
  export default {
5
5
  title: 'Organisms/Webform/Mapper'
6
6
  };
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import Immutable from 'immutable';
3
- import Webform from './';
3
+ import Webform from '.';
4
4
  export default {
5
5
  title: 'Organisms/Webform/Mapper'
6
6
  };
@@ -1,133 +1,133 @@
1
1
  import React from 'react';
2
2
  import Immutable from 'immutable';
3
- import Webform from './';
3
+ import Webform from '.';
4
4
  export default {
5
5
  title: 'Organisms/Webform/Mapper'
6
6
  };
7
7
  const entities = Immutable.fromJS({
8
- "page_1": {
9
- "#type": "webform_wizard_page",
10
- "#title": "Page 1",
11
- "number": {
12
- "#type": "number",
13
- "#title": "Number",
14
- "#min": 1,
15
- "#max": 100,
16
- "#step": 1,
17
- "#webform": "test",
18
- "#webform_id": "test--number",
19
- "#webform_key": "number",
20
- "#webform_parent_key": "page_1",
21
- "#webform_parent_flexbox": false,
22
- "#webform_depth": 1,
8
+ 'page_1': {
9
+ '#type': 'webform_wizard_page',
10
+ '#title': 'Page 1',
11
+ 'number': {
12
+ '#type': 'number',
13
+ '#title': 'Number',
14
+ '#min': 1,
15
+ '#max': 100,
16
+ '#step': 1,
17
+ '#webform': 'test',
18
+ '#webform_id': 'test--number',
19
+ '#webform_key': 'number',
20
+ '#webform_parent_key': 'page_1',
21
+ '#webform_parent_flexbox': false,
22
+ '#webform_depth': 1,
23
23
  '#required': true,
24
- "#webform_multiple": false,
25
- "#webform_composite": false,
26
- "#webform_parents": ["page_1", "number"],
27
- "#admin_title": "Number",
28
- "#webform_plugin_id": "number"
24
+ '#webform_multiple': false,
25
+ '#webform_composite': false,
26
+ '#webform_parents': ['page_1', 'number'],
27
+ '#admin_title': 'Number',
28
+ '#webform_plugin_id': 'number'
29
29
  },
30
- "#webform": "test",
31
- "#webform_id": "test--page_1",
32
- "#webform_key": "page_1",
33
- "#webform_parent_key": "",
34
- "#webform_parent_flexbox": false,
35
- "#webform_depth": 0,
36
- "#webform_children": [],
37
- "#webform_multiple": false,
38
- "#webform_composite": false,
39
- "#webform_parents": ["page_1"],
40
- "#admin_title": "Page 1",
41
- "#webform_plugin_id": "webform_wizard_page"
30
+ '#webform': 'test',
31
+ '#webform_id': 'test--page_1',
32
+ '#webform_key': 'page_1',
33
+ '#webform_parent_key': '',
34
+ '#webform_parent_flexbox': false,
35
+ '#webform_depth': 0,
36
+ '#webform_children': [],
37
+ '#webform_multiple': false,
38
+ '#webform_composite': false,
39
+ '#webform_parents': ['page_1'],
40
+ '#admin_title': 'Page 1',
41
+ '#webform_plugin_id': 'webform_wizard_page'
42
42
  },
43
- "page_2": {
44
- "#type": "webform_wizard_page",
45
- "#title": "Page 2",
46
- "radio": {
47
- "#type": "radios",
48
- "#title": "Radio",
49
- "#options": {
50
- "1": "Optie 1",
51
- "2": "Optie 2"
43
+ 'page_2': {
44
+ '#type': 'webform_wizard_page',
45
+ '#title': 'Page 2',
46
+ 'radio': {
47
+ '#type': 'radios',
48
+ '#title': 'Radio',
49
+ '#options': {
50
+ '1': 'Optie 1',
51
+ '2': 'Optie 2'
52
52
  },
53
- "#webform": "test",
54
- "#webform_id": "test--radio",
55
- "#webform_key": "radio",
56
- "#webform_parent_key": "page_2",
57
- "#webform_parent_flexbox": false,
58
- "#webform_depth": 1,
59
- "#webform_children": [],
60
- "#webform_multiple": false,
61
- "#webform_composite": false,
62
- "#webform_parents": ["page_2", "radio"],
63
- "#admin_title": "Radio",
64
- "#webform_plugin_id": "radios"
53
+ '#webform': 'test',
54
+ '#webform_id': 'test--radio',
55
+ '#webform_key': 'radio',
56
+ '#webform_parent_key': 'page_2',
57
+ '#webform_parent_flexbox': false,
58
+ '#webform_depth': 1,
59
+ '#webform_children': [],
60
+ '#webform_multiple': false,
61
+ '#webform_composite': false,
62
+ '#webform_parents': ['page_2', 'radio'],
63
+ '#admin_title': 'Radio',
64
+ '#webform_plugin_id': 'radios'
65
65
  },
66
- "#webform": "test",
67
- "#webform_id": "test--page_2",
68
- "#webform_key": "page_2",
69
- "#webform_parent_key": "",
70
- "#webform_parent_flexbox": false,
71
- "#webform_depth": 0,
72
- "#webform_children": [],
73
- "#webform_multiple": false,
74
- "#webform_composite": false,
75
- "#webform_parents": ["page_2"],
76
- "#admin_title": "Page 2",
77
- "#webform_plugin_id": "webform_wizard_page"
66
+ '#webform': 'test',
67
+ '#webform_id': 'test--page_2',
68
+ '#webform_key': 'page_2',
69
+ '#webform_parent_key': '',
70
+ '#webform_parent_flexbox': false,
71
+ '#webform_depth': 0,
72
+ '#webform_children': [],
73
+ '#webform_multiple': false,
74
+ '#webform_composite': false,
75
+ '#webform_parents': ['page_2'],
76
+ '#admin_title': 'Page 2',
77
+ '#webform_plugin_id': 'webform_wizard_page'
78
78
  },
79
- "page_3": {
80
- "#type": "webform_wizard_page",
81
- "#title": "Page 3",
82
- "checkboxes": {
83
- "#type": "checkboxes",
84
- "#title": "Checkboxes",
85
- "#options": {
86
- "1": "1",
87
- "2": "2"
79
+ 'page_3': {
80
+ '#type': 'webform_wizard_page',
81
+ '#title': 'Page 3',
82
+ 'checkboxes': {
83
+ '#type': 'checkboxes',
84
+ '#title': 'Checkboxes',
85
+ '#options': {
86
+ '1': '1',
87
+ '2': '2'
88
88
  },
89
- "#webform": "test",
90
- "#webform_id": "test--checkboxes",
91
- "#webform_key": "checkboxes",
92
- "#webform_parent_key": "page_3",
93
- "#webform_parent_flexbox": false,
94
- "#webform_depth": 1,
95
- "#webform_children": [],
96
- "#webform_multiple": true,
97
- "#webform_composite": false,
98
- "#webform_parents": ["page_3", "checkboxes"],
99
- "#admin_title": "Checkboxes",
100
- "#webform_plugin_id": "checkboxes"
89
+ '#webform': 'test',
90
+ '#webform_id': 'test--checkboxes',
91
+ '#webform_key': 'checkboxes',
92
+ '#webform_parent_key': 'page_3',
93
+ '#webform_parent_flexbox': false,
94
+ '#webform_depth': 1,
95
+ '#webform_children': [],
96
+ '#webform_multiple': true,
97
+ '#webform_composite': false,
98
+ '#webform_parents': ['page_3', 'checkboxes'],
99
+ '#admin_title': 'Checkboxes',
100
+ '#webform_plugin_id': 'checkboxes'
101
101
  },
102
- "#webform": "test",
103
- "#webform_id": "test--page_3",
104
- "#webform_key": "page_3",
105
- "#webform_parent_key": "",
106
- "#webform_parent_flexbox": false,
107
- "#webform_depth": 0,
108
- "#webform_children": [],
109
- "#webform_multiple": false,
110
- "#webform_composite": false,
111
- "#webform_parents": ["page_3"],
112
- "#admin_title": "Page 3",
113
- "#webform_plugin_id": "webform_wizard_page"
102
+ '#webform': 'test',
103
+ '#webform_id': 'test--page_3',
104
+ '#webform_key': 'page_3',
105
+ '#webform_parent_key': '',
106
+ '#webform_parent_flexbox': false,
107
+ '#webform_depth': 0,
108
+ '#webform_children': [],
109
+ '#webform_multiple': false,
110
+ '#webform_composite': false,
111
+ '#webform_parents': ['page_3'],
112
+ '#admin_title': 'Page 3',
113
+ '#webform_plugin_id': 'webform_wizard_page'
114
114
  },
115
- "actions": {
116
- "#type": "webform_actions",
117
- "#title": "Submit button(s)",
118
- "#submit__label": "Verstuur",
119
- "#webform": "test",
120
- "#webform_id": "test--actions",
121
- "#webform_key": "actions",
122
- "#webform_parent_key": "",
123
- "#webform_parent_flexbox": false,
124
- "#webform_depth": 0,
125
- "#webform_children": [],
126
- "#webform_multiple": false,
127
- "#webform_composite": false,
128
- "#webform_parents": ["actions"],
129
- "#admin_title": "Submit button(s)",
130
- "#webform_plugin_id": "webform_actions"
115
+ 'actions': {
116
+ '#type': 'webform_actions',
117
+ '#title': 'Submit button(s)',
118
+ '#submit__label': 'Verstuur',
119
+ '#webform': 'test',
120
+ '#webform_id': 'test--actions',
121
+ '#webform_key': 'actions',
122
+ '#webform_parent_key': '',
123
+ '#webform_parent_flexbox': false,
124
+ '#webform_depth': 0,
125
+ '#webform_children': [],
126
+ '#webform_multiple': false,
127
+ '#webform_composite': false,
128
+ '#webform_parents': ['actions'],
129
+ '#admin_title': 'Submit button(s)',
130
+ '#webform_plugin_id': 'webform_actions'
131
131
  }
132
132
  });
133
133
  export const Wizard = () => /*#__PURE__*/React.createElement(Webform, {
@@ -47,7 +47,6 @@ WysiwygField.propTypes = {
47
47
  customElementHandler: PropTypes.func
48
48
  };
49
49
  WysiwygField.defaultProps = {
50
- html: '',
51
50
  customElementHandler: () => {}
52
51
  };
53
52
  export default WysiwygField;
@@ -1,11 +1,11 @@
1
1
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
2
  import React from 'react';
3
- import WysiwygField from './';
3
+ import WysiwygField from '.';
4
4
  import AbbreviationAnnotation from '../AbbreviationAnnotation';
5
5
  export default {
6
6
  title: 'Atoms/WysiwygField'
7
7
  };
8
- const html = "<p>This is <em>some</em> example <a href='https://www.uncinc.nl'>HTML</a>.</p>";
8
+ const html = '<p>This is <em>some</em> example <a href=\'https://www.uncinc.nl\'>HTML</a>.</p>';
9
9
  export const Primary = () => /*#__PURE__*/React.createElement(WysiwygField, {
10
10
  html: html
11
11
  });
@@ -1,4 +1,8 @@
1
1
  {
2
+ "general": {
3
+ "open": "Open",
4
+ "close": "Close"
5
+ },
2
6
  "dateselect": {
3
7
  "months": {
4
8
  "jan": "January",
@@ -1,4 +1,8 @@
1
1
  {
2
+ "general": {
3
+ "open": "Openen",
4
+ "close": "Sluiten"
5
+ },
2
6
  "dateselect" : {
3
7
  "months": {
4
8
  "jan": "Januari",
@@ -18,5 +22,8 @@
18
22
  "form": {
19
23
  "required": "vereist",
20
24
  "optional": "optioneel"
25
+ },
26
+ "searchkit": {
27
+ "reset_search": "Zoeken opnieuw instellen"
21
28
  }
22
29
  }
@@ -0,0 +1,8 @@
1
+ export const parseDateWithTimezoneOffset = date => {
2
+ const offset = date.getTimezoneOffset();
3
+ const dateWithTimezoneOffset = new Date(date.getTime() - offset * 60 * 1000);
4
+ return dateWithTimezoneOffset.toISOString();
5
+ };
6
+ export const isValidDate = date => {
7
+ return date instanceof Date && !Number.isNaN(date);
8
+ };
@@ -0,0 +1,20 @@
1
+ import { useCallback, useState } from 'react';
2
+
3
+ export default function useBoolean(defaultValue: boolean = false) {
4
+ const [value, setValue] = useState(defaultValue);
5
+
6
+ const toggle = useCallback(() => {
7
+ setValue((current) => !current);
8
+ }, [setValue]);
9
+
10
+ const setTrue = useCallback(() => { setValue(true); }, []);
11
+ const setFalse = useCallback(() => { setValue(false); }, []);
12
+
13
+ return {
14
+ value,
15
+ setValue,
16
+ setTrue,
17
+ setFalse,
18
+ toggle,
19
+ } as const;
20
+ }
@@ -0,0 +1,38 @@
1
+ import { MutableRefObject, useEffect } from 'react';
2
+
3
+ interface UseClickOutsideOptions {
4
+ refs: MutableRefObject<HTMLElement>[] | MutableRefObject<HTMLElement>;
5
+ unless?: boolean | ((e: globalThis.MouseEvent) => boolean);
6
+ onClick: (e: globalThis.MouseEvent) => void;
7
+ }
8
+
9
+ export default function useClickOutside({
10
+ refs,
11
+ unless = false,
12
+ onClick,
13
+ }: UseClickOutsideOptions) {
14
+ const refsAsArray = Array.isArray(refs) ? refs : [refs];
15
+
16
+ useEffect(() => {
17
+ function handleOutsideClick(e: globalThis.MouseEvent) {
18
+ const allRefsHaveCurrent = refsAsArray.every((ref) => !!ref.current);
19
+ const shouldNotExecute = typeof unless === 'function' ? unless(e) : unless;
20
+
21
+ if (!allRefsHaveCurrent || shouldNotExecute) {
22
+ return;
23
+ }
24
+
25
+ const isOutsideClick = refsAsArray.every((ref) => !ref.current.contains(e.target as Node));
26
+
27
+ if (isOutsideClick) {
28
+ onClick(e);
29
+ }
30
+ }
31
+
32
+ document.addEventListener('mousedown', handleOutsideClick);
33
+
34
+ return () => {
35
+ document.removeEventListener('mousedown', handleOutsideClick);
36
+ };
37
+ }, [unless, onClick, refs]);
38
+ }
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+
3
+ import Dialog from '../components/Dialog';
4
+ import useBoolean from './useBoolean';
5
+
6
+ export default function useDialog() {
7
+ const {
8
+ value: dialogIsOpen,
9
+ setTrue: openDialog,
10
+ setFalse: closeDialog,
11
+ } = useBoolean();
12
+
13
+ const DialogComponent = ({ children, ...props }) => React.createElement(
14
+ Dialog,
15
+ {
16
+ ...props,
17
+ isOpen: dialogIsOpen,
18
+ closeFunction: closeDialog,
19
+ },
20
+ children,
21
+ );
22
+
23
+ return {
24
+ dialogIsOpen,
25
+ openDialog,
26
+ closeDialog,
27
+ DialogComponent,
28
+ } as const;
29
+ }
@@ -0,0 +1,95 @@
1
+ import { useEffect, MutableRefObject } from 'react';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+
4
+ import useClickOutside from './useClickOutside';
5
+ import useEscape from './useEscape';
6
+ import useTrapFocus from './useTrapFocus';
7
+
8
+ export interface UseDialogHelpersOptions<T extends HTMLElement> {
9
+ id?: string;
10
+ wrapperElement: MutableRefObject<T>;
11
+ closeFunction: () => void;
12
+ isOpen: boolean;
13
+ }
14
+
15
+ /**
16
+ * Hook to allow you to implement your own Dialog component.
17
+ * This takes care of focus, and closing the Dialog by
18
+ * click outside it, or hitting Escape.
19
+ */
20
+ export default function useDialogHelpers<T extends HTMLElement>(
21
+ options: UseDialogHelpersOptions<T>,
22
+ ) {
23
+ const {
24
+ id = uuidv4(),
25
+ wrapperElement,
26
+ closeFunction,
27
+ isOpen,
28
+ } = options;
29
+
30
+ /**
31
+ * Close the Dialog by pressing Escape.
32
+ */
33
+ useClickOutside({
34
+ refs: [wrapperElement],
35
+ onClick: closeFunction,
36
+ });
37
+
38
+ /**
39
+ * Close the Dialog by pressing Escape.
40
+ */
41
+ useEscape({
42
+ onPress: closeFunction,
43
+ });
44
+
45
+ /**
46
+ * Make sure focus does not leave the Dialog while it's open.
47
+ */
48
+ useTrapFocus({
49
+ ref: wrapperElement,
50
+ isActive: isOpen,
51
+ });
52
+
53
+ /**
54
+ * Sets focus on the popup so that screen readers announce
55
+ * that the user is currently on a dialog.
56
+ */
57
+ useEffect(() => {
58
+ if (isOpen && wrapperElement.current) {
59
+ wrapperElement.current.setAttribute('tabindex', '-1');
60
+ wrapperElement.current.focus();
61
+ }
62
+
63
+ function removeFocusProperties() {
64
+ if (wrapperElement.current) {
65
+ wrapperElement.current.removeAttribute('tabindex');
66
+ }
67
+ }
68
+
69
+ if (wrapperElement.current) {
70
+ wrapperElement.current.addEventListener('blur', removeFocusProperties);
71
+ }
72
+
73
+ return () => {
74
+ if (wrapperElement.current) {
75
+ wrapperElement.current.removeEventListener('blur', removeFocusProperties);
76
+ }
77
+ };
78
+ }, [isOpen]);
79
+
80
+ /**
81
+ * Create a labelId for A11Y
82
+ */
83
+ const labelId = `${id}-label`;
84
+
85
+ const dialogHelpersProps = {
86
+ 'aria-role': 'dialog',
87
+ 'aria-labelledby': labelId,
88
+ };
89
+
90
+ return {
91
+ id,
92
+ labelId,
93
+ dialogHelpersProps,
94
+ } as const;
95
+ }
@@ -0,0 +1,26 @@
1
+ import { useEffect } from 'react';
2
+
3
+ interface UseEscapeOption {
4
+ unless?: boolean | ((e: KeyboardEvent) => boolean);
5
+ onPress: (e: KeyboardEvent) => void;
6
+ }
7
+
8
+ export default function useEscape({ unless, onPress }: UseEscapeOption) {
9
+ useEffect(() => {
10
+ function handleOnPress(e: KeyboardEvent) {
11
+ const shouldNotExecute = typeof unless === 'function' ? unless(e) : unless;
12
+
13
+ if (shouldNotExecute || e.key !== 'Escape') {
14
+ return;
15
+ }
16
+
17
+ onPress(e);
18
+ }
19
+
20
+ document.addEventListener('keyup', handleOnPress);
21
+
22
+ return () => {
23
+ document.removeEventListener('keyup', handleOnPress);
24
+ };
25
+ }, [unless]);
26
+ }
@@ -11,5 +11,4 @@ export default function usePrevious(value) {
11
11
 
12
12
  // Return previous value (happens before update in useEffect above)
13
13
  return ref.current;
14
- }
15
- ;
14
+ }