@opengovsg/oui 0.0.0-snapshot-20251103063040 → 0.0.0-snapshot-20251203091804

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 (184) hide show
  1. package/dist/cjs/badge/badge.cjs +4 -4
  2. package/dist/cjs/badge/use-badge.cjs +6 -6
  3. package/dist/cjs/banner/banner.cjs +4 -4
  4. package/dist/cjs/button/button.cjs +3 -3
  5. package/dist/cjs/calendar/calendar-bottom-content.cjs +3 -3
  6. package/dist/cjs/calendar/calendar-header.cjs +2 -2
  7. package/dist/cjs/calendar/calendar-month-day-selector.cjs +4 -3
  8. package/dist/cjs/calendar/calendar.cjs +4 -4
  9. package/dist/cjs/calendar/hooks/use-calendar-selectors.cjs +4 -4
  10. package/dist/cjs/calendar/utils.cjs +3 -3
  11. package/dist/cjs/combo-box/combo-box-fuzzy.cjs +6 -6
  12. package/dist/cjs/combo-box/combo-box-item.cjs +2 -2
  13. package/dist/cjs/combo-box/combo-box.cjs +3 -3
  14. package/dist/cjs/date-field/date-field.cjs +15 -6
  15. package/dist/cjs/date-picker/date-picker.cjs +39 -13
  16. package/dist/cjs/date-range-picker/date-range-picker.cjs +4 -4
  17. package/dist/cjs/field/field.cjs +20 -6
  18. package/dist/cjs/field/index.cjs +1 -0
  19. package/dist/cjs/file-dropzone/contexts.cjs +18 -0
  20. package/dist/cjs/file-dropzone/file-dropzone.cjs +311 -0
  21. package/dist/cjs/file-dropzone/file-info.cjs +146 -0
  22. package/dist/cjs/file-dropzone/index.cjs +13 -0
  23. package/dist/cjs/file-dropzone/types.cjs +3 -0
  24. package/dist/cjs/file-dropzone/utils.cjs +31 -0
  25. package/dist/cjs/govt-banner/govt-banner.cjs +3 -3
  26. package/dist/cjs/hooks/index.cjs +2 -0
  27. package/dist/cjs/hooks/use-callback-ref.cjs +4 -4
  28. package/dist/cjs/hooks/use-controllable-state.cjs +2 -2
  29. package/dist/cjs/hooks/use-draggable.cjs +88 -0
  30. package/dist/cjs/index.cjs +47 -22
  31. package/dist/cjs/input/input.cjs +2 -2
  32. package/dist/cjs/menu/menu.cjs +6 -6
  33. package/dist/cjs/modal/i18n.cjs +19 -0
  34. package/dist/cjs/modal/index.cjs +19 -0
  35. package/dist/cjs/modal/modal-body.cjs +26 -0
  36. package/dist/cjs/modal/modal-content.cjs +54 -0
  37. package/dist/cjs/modal/modal-footer.cjs +27 -0
  38. package/dist/cjs/modal/modal-header.cjs +25 -0
  39. package/dist/cjs/modal/modal-variant-context.cjs +13 -0
  40. package/dist/cjs/modal/modal.cjs +66 -0
  41. package/dist/cjs/node_modules/.pnpm/lucide-react@0.475.0_react@19.0.0/node_modules/lucide-react/dist/esm/Icon.cjs +4 -4
  42. package/dist/cjs/node_modules/.pnpm/lucide-react@0.475.0_react@19.0.0/node_modules/lucide-react/dist/esm/createLucideIcon.cjs +3 -3
  43. package/dist/cjs/node_modules/.pnpm/lucide-react@0.475.0_react@19.0.0/node_modules/lucide-react/dist/esm/icons/plus.cjs +22 -0
  44. package/dist/cjs/node_modules/.pnpm/lucide-react@0.475.0_react@19.0.0/node_modules/lucide-react/dist/esm/icons/trash-2.cjs +25 -0
  45. package/dist/cjs/node_modules/.pnpm/lucide-react@0.475.0_react@19.0.0/node_modules/lucide-react/dist/esm/icons/upload.cjs +23 -0
  46. package/dist/cjs/number-field/index.cjs +8 -0
  47. package/dist/cjs/number-field/number-field.cjs +136 -0
  48. package/dist/cjs/pagination/hooks/use-pagination.cjs +7 -7
  49. package/dist/cjs/pagination/pagination.cjs +6 -6
  50. package/dist/cjs/pagination/use-pagination-item.cjs +7 -8
  51. package/dist/cjs/pagination/use-pagination.cjs +8 -8
  52. package/dist/cjs/range-calendar/range-calendar.cjs +7 -7
  53. package/dist/cjs/ripple/use-ripple.cjs +4 -4
  54. package/dist/cjs/select/select.cjs +3 -3
  55. package/dist/cjs/spinner/use-spinner.cjs +3 -3
  56. package/dist/cjs/system/react-utils/context.cjs +3 -3
  57. package/dist/cjs/system/react-utils/refs.cjs +3 -3
  58. package/dist/cjs/system/utils.cjs +3 -3
  59. package/dist/cjs/tabs/tabs.cjs +2 -2
  60. package/dist/cjs/tag-field/tag-field-item.cjs +2 -2
  61. package/dist/cjs/tag-field/tag-field-list.cjs +4 -4
  62. package/dist/cjs/tag-field/tag-field-root.cjs +14 -14
  63. package/dist/cjs/tag-field/tag-field-state-context.cjs +2 -2
  64. package/dist/cjs/tag-field/tag-field-tag-list.cjs +3 -3
  65. package/dist/cjs/tag-field/tag-field-trigger.cjs +2 -2
  66. package/dist/cjs/tag-field/tag-field.cjs +2 -2
  67. package/dist/cjs/tag-field/use-tag-field-state.cjs +6 -6
  68. package/dist/cjs/tag-field/use-tag-field.cjs +4 -4
  69. package/dist/cjs/text-area/text-area.cjs +2 -2
  70. package/dist/cjs/toggle/toggle.cjs +3 -3
  71. package/dist/esm/banner/banner.js +1 -1
  72. package/dist/esm/calendar/calendar-month-day-selector.js +2 -1
  73. package/dist/esm/date-field/date-field.js +12 -3
  74. package/dist/esm/date-picker/date-picker.js +37 -11
  75. package/dist/esm/date-range-picker/date-range-picker.js +1 -1
  76. package/dist/esm/field/field.js +20 -7
  77. package/dist/esm/field/index.js +1 -1
  78. package/dist/esm/file-dropzone/contexts.js +13 -0
  79. package/dist/esm/file-dropzone/file-dropzone.js +309 -0
  80. package/dist/esm/file-dropzone/file-info.js +144 -0
  81. package/dist/esm/file-dropzone/index.js +4 -0
  82. package/dist/esm/file-dropzone/types.js +1 -0
  83. package/dist/esm/file-dropzone/utils.js +28 -0
  84. package/dist/esm/hooks/index.js +1 -0
  85. package/dist/esm/hooks/use-draggable.js +86 -0
  86. package/dist/esm/index.js +19 -8
  87. package/dist/esm/modal/i18n.js +17 -0
  88. package/dist/esm/modal/index.js +7 -0
  89. package/dist/esm/modal/modal-body.js +24 -0
  90. package/dist/esm/modal/modal-content.js +52 -0
  91. package/dist/esm/modal/modal-footer.js +25 -0
  92. package/dist/esm/modal/modal-header.js +23 -0
  93. package/dist/esm/modal/modal-variant-context.js +10 -0
  94. package/dist/esm/modal/modal.js +64 -0
  95. package/dist/esm/node_modules/.pnpm/lucide-react@0.475.0_react@19.0.0/node_modules/lucide-react/dist/esm/icons/plus.js +17 -0
  96. package/dist/esm/node_modules/.pnpm/lucide-react@0.475.0_react@19.0.0/node_modules/lucide-react/dist/esm/icons/trash-2.js +20 -0
  97. package/dist/esm/node_modules/.pnpm/lucide-react@0.475.0_react@19.0.0/node_modules/lucide-react/dist/esm/icons/upload.js +18 -0
  98. package/dist/esm/number-field/index.js +2 -0
  99. package/dist/esm/number-field/number-field.js +134 -0
  100. package/dist/esm/pagination/use-pagination-item.js +5 -6
  101. package/dist/esm/range-calendar/range-calendar.js +1 -1
  102. package/dist/esm/select/select.js +1 -1
  103. package/dist/types/badge/use-badge.d.ts +12 -12
  104. package/dist/types/calendar/calendar-month-day-selector.d.ts.map +1 -1
  105. package/dist/types/date-field/date-field.d.ts +1 -0
  106. package/dist/types/date-field/date-field.d.ts.map +1 -1
  107. package/dist/types/date-picker/date-picker.d.ts +5 -2
  108. package/dist/types/date-picker/date-picker.d.ts.map +1 -1
  109. package/dist/types/field/field.d.ts +4 -1
  110. package/dist/types/field/field.d.ts.map +1 -1
  111. package/dist/types/file-dropzone/contexts.d.ts +4 -0
  112. package/dist/types/file-dropzone/contexts.d.ts.map +1 -0
  113. package/dist/types/file-dropzone/file-dropzone.d.ts +82 -0
  114. package/dist/types/file-dropzone/file-dropzone.d.ts.map +1 -0
  115. package/dist/types/file-dropzone/file-info.d.ts +9 -0
  116. package/dist/types/file-dropzone/file-info.d.ts.map +1 -0
  117. package/dist/types/file-dropzone/index.d.ts +7 -0
  118. package/dist/types/file-dropzone/index.d.ts.map +1 -0
  119. package/dist/types/file-dropzone/types.d.ts +24 -0
  120. package/dist/types/file-dropzone/types.d.ts.map +1 -0
  121. package/dist/types/file-dropzone/utils.d.ts +8 -0
  122. package/dist/types/file-dropzone/utils.d.ts.map +1 -0
  123. package/dist/types/hooks/index.d.ts +4 -1
  124. package/dist/types/hooks/index.d.ts.map +1 -1
  125. package/dist/types/hooks/use-draggable.d.ts +24 -0
  126. package/dist/types/hooks/use-draggable.d.ts.map +1 -0
  127. package/dist/types/index.d.mts +3 -0
  128. package/dist/types/index.d.ts +3 -0
  129. package/dist/types/index.d.ts.map +1 -1
  130. package/dist/types/menu/menu.d.ts.map +1 -1
  131. package/dist/types/modal/i18n.d.ts +3 -0
  132. package/dist/types/modal/i18n.d.ts.map +1 -0
  133. package/dist/types/modal/index.d.ts +12 -0
  134. package/dist/types/modal/index.d.ts.map +1 -0
  135. package/dist/types/modal/modal-body.d.ts +5 -0
  136. package/dist/types/modal/modal-body.d.ts.map +1 -0
  137. package/dist/types/modal/modal-content.d.ts +10 -0
  138. package/dist/types/modal/modal-content.d.ts.map +1 -0
  139. package/dist/types/modal/modal-footer.d.ts +5 -0
  140. package/dist/types/modal/modal-footer.d.ts.map +1 -0
  141. package/dist/types/modal/modal-header.d.ts +5 -0
  142. package/dist/types/modal/modal-header.d.ts.map +1 -0
  143. package/dist/types/modal/modal-variant-context.d.ts +8 -0
  144. package/dist/types/modal/modal-variant-context.d.ts.map +1 -0
  145. package/dist/types/modal/modal.d.ts +8 -0
  146. package/dist/types/modal/modal.d.ts.map +1 -0
  147. package/dist/types/number-field/index.d.ts +3 -0
  148. package/dist/types/number-field/index.d.ts.map +1 -0
  149. package/dist/types/number-field/number-field.d.ts +24 -0
  150. package/dist/types/number-field/number-field.d.ts.map +1 -0
  151. package/dist/types/pagination/use-pagination.d.ts +36 -36
  152. package/package.json +14 -10
  153. package/dist/cjs/node_modules/.pnpm/@react-aria_focus@3.20.5_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/focus/dist/useFocusRing.cjs +0 -45
  154. package/dist/cjs/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/context.cjs +0 -21
  155. package/dist/cjs/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/textSelection.cjs +0 -72
  156. package/dist/cjs/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/useFocus.cjs +0 -60
  157. package/dist/cjs/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/useFocusVisible.cjs +0 -210
  158. package/dist/cjs/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/useFocusWithin.cjs +0 -100
  159. package/dist/cjs/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/useHover.cjs +0 -152
  160. package/dist/cjs/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/usePress.cjs +0 -676
  161. package/dist/cjs/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/utils.cjs +0 -160
  162. package/dist/cjs/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_check_private_redeclaration.cjs +0 -9
  163. package/dist/cjs/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_class_apply_descriptor_get.cjs +0 -9
  164. package/dist/cjs/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_class_apply_descriptor_set.cjs +0 -16
  165. package/dist/cjs/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_class_extract_field_descriptor.cjs +0 -9
  166. package/dist/cjs/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_class_private_field_get.cjs +0 -11
  167. package/dist/cjs/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_class_private_field_init.cjs +0 -10
  168. package/dist/cjs/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_class_private_field_set.cjs +0 -12
  169. package/dist/esm/node_modules/.pnpm/@react-aria_focus@3.20.5_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/focus/dist/useFocusRing.js +0 -43
  170. package/dist/esm/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/context.js +0 -19
  171. package/dist/esm/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/textSelection.js +0 -69
  172. package/dist/esm/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/useFocus.js +0 -58
  173. package/dist/esm/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/useFocusVisible.js +0 -205
  174. package/dist/esm/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/useFocusWithin.js +0 -98
  175. package/dist/esm/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/useHover.js +0 -150
  176. package/dist/esm/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/usePress.js +0 -674
  177. package/dist/esm/node_modules/.pnpm/@react-aria_interactions@3.25.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/@react-aria/interactions/dist/utils.js +0 -155
  178. package/dist/esm/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_check_private_redeclaration.js +0 -7
  179. package/dist/esm/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_class_apply_descriptor_get.js +0 -7
  180. package/dist/esm/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_class_apply_descriptor_set.js +0 -14
  181. package/dist/esm/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_class_extract_field_descriptor.js +0 -7
  182. package/dist/esm/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_class_private_field_get.js +0 -9
  183. package/dist/esm/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_class_private_field_init.js +0 -8
  184. package/dist/esm/node_modules/.pnpm/@swc_helpers@0.5.17/node_modules/@swc/helpers/esm/_class_private_field_set.js +0 -10
@@ -0,0 +1,311 @@
1
+ "use strict";
2
+ "use client";
3
+ 'use strict';
4
+
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var react = require('react');
7
+ var form = require('@react-stately/form');
8
+ var reactAria = require('react-aria');
9
+ var reactAriaComponents = require('react-aria-components');
10
+ var reactDropzone = require('react-dropzone');
11
+ var ouiTheme = require('@opengovsg/oui-theme');
12
+ var field = require('../field/field.cjs');
13
+ var useControllableState = require('../hooks/use-controllable-state.cjs');
14
+ var utils = require('../system/utils.cjs');
15
+ var contexts = require('./contexts.cjs');
16
+ var fileInfo = require('./file-info.cjs');
17
+ var utils$1 = require('./utils.cjs');
18
+ var upload = require('../node_modules/.pnpm/lucide-react@0.475.0_react@19.0.0/node_modules/lucide-react/dist/esm/icons/upload.cjs');
19
+
20
+ const FileDropzone = (originalProps) => {
21
+ const [props, variantProps] = utils.mapPropsVariants(
22
+ originalProps,
23
+ ouiTheme.fileDropzoneStyles.variantKeys
24
+ );
25
+ const {
26
+ name,
27
+ allowedMimeTypes = [],
28
+ maxFileSize = Number.POSITIVE_INFINITY,
29
+ minFileSize = 0,
30
+ showFileSizeText = true,
31
+ maxFiles = 1,
32
+ isDisabled,
33
+ isReadOnly,
34
+ classNames,
35
+ itemClassNames,
36
+ validator,
37
+ showRejectedFiles,
38
+ onError,
39
+ errorMessage,
40
+ label,
41
+ description,
42
+ children,
43
+ hideDropzoneOnValue = maxFiles === 1,
44
+ imagePreview = "small"
45
+ } = props;
46
+ const [value, setValue] = useControllableState.useControllableState({
47
+ value: props.value,
48
+ defaultValue: props.defaultValue || [],
49
+ onChange: props.onChange
50
+ });
51
+ const [rejections, setRejections] = useControllableState.useControllableState({
52
+ value: props.rejections,
53
+ defaultValue: [],
54
+ onChange: props.onRejection
55
+ });
56
+ const validationState = form.useFormValidationState({
57
+ ...props,
58
+ value
59
+ });
60
+ const { isInvalid, validationErrors, validationDetails } = validationState.displayValidation;
61
+ const { labelProps, fieldProps, descriptionProps, errorMessageProps } = reactAria.useField({
62
+ ...props,
63
+ isInvalid,
64
+ errorMessage: props.errorMessage || validationErrors
65
+ });
66
+ const slots = ouiTheme.fileDropzoneStyles(variantProps);
67
+ const fileSizeTextId = reactAria.useId();
68
+ const formatError = react.useCallback(
69
+ (error) => utils$1.formatErrorMessage(error, {
70
+ maxFileSize,
71
+ minFileSize,
72
+ maxFiles
73
+ }),
74
+ [maxFileSize, maxFiles, minFileSize]
75
+ );
76
+ const onDrop = react.useCallback(
77
+ (acceptedFiles, fileRejections) => {
78
+ const files = acceptedFiles;
79
+ if (showRejectedFiles) {
80
+ const invalidFiles = fileRejections.map(({ file, errors }) => {
81
+ file.errors = errors;
82
+ return file;
83
+ });
84
+ setRejections(invalidFiles);
85
+ }
86
+ setValue(files);
87
+ if (onError && fileRejections.length > 0) {
88
+ const firstError = fileRejections[0].errors[0];
89
+ onError(formatError(firstError));
90
+ }
91
+ },
92
+ [formatError, onError, setRejections, setValue, showRejectedFiles]
93
+ );
94
+ const handleRemoveFile = react.useCallback(
95
+ (fileName) => {
96
+ setValue((files) => files.filter((file) => file.name !== fileName));
97
+ },
98
+ [setValue]
99
+ );
100
+ const handleRemoveRejection = react.useCallback(
101
+ (fileName) => {
102
+ setRejections(
103
+ (rejections2) => rejections2.filter((file) => file.name !== fileName)
104
+ );
105
+ },
106
+ [setRejections]
107
+ );
108
+ const { getInputProps, ...dropzoneState } = reactDropzone.useDropzone({
109
+ validator,
110
+ accept: allowedMimeTypes.reduce(
111
+ (acc, type) => ({ ...acc, [type]: [] }),
112
+ {}
113
+ ),
114
+ onError: (e) => onError?.(e.message),
115
+ onDrop,
116
+ disabled: isDisabled,
117
+ noDrag: isReadOnly,
118
+ // Prevent ref hijack when there is a label
119
+ noClick: true,
120
+ noKeyboard: true,
121
+ maxSize: maxFileSize,
122
+ minSize: minFileSize,
123
+ maxFiles,
124
+ multiple: maxFiles !== 1
125
+ });
126
+ const fileSizeText = react.useMemo(() => {
127
+ const notDefaultMaxFileSize = maxFileSize !== Number.POSITIVE_INFINITY;
128
+ const notDefaultMinFileSize = minFileSize !== 0;
129
+ const shouldShow = showFileSizeText && (notDefaultMaxFileSize || notDefaultMinFileSize);
130
+ if (!shouldShow) return null;
131
+ if (notDefaultMaxFileSize && notDefaultMinFileSize) {
132
+ return `File size must be between ${utils$1.formatBytes(minFileSize, 2)} and ${utils$1.formatBytes(
133
+ maxFileSize,
134
+ 2
135
+ )}`;
136
+ }
137
+ if (notDefaultMaxFileSize) {
138
+ return `Maximum file size: ${utils$1.formatBytes(maxFileSize, 2)}`;
139
+ }
140
+ if (notDefaultMinFileSize) {
141
+ return `Minimum file size: ${utils$1.formatBytes(minFileSize, 2)}`;
142
+ }
143
+ return null;
144
+ }, [maxFileSize, minFileSize, showFileSizeText]);
145
+ const triggerFileSelector = react.useCallback(() => {
146
+ if (isDisabled || isReadOnly) return;
147
+ dropzoneState.inputRef.current?.click();
148
+ }, [dropzoneState, isDisabled, isReadOnly]);
149
+ react.useEffect(() => {
150
+ if (value.length <= maxFiles) {
151
+ let changed = false;
152
+ const newFiles = value.map((file) => {
153
+ if (file.errors?.some((e) => e.code === "too-many-files")) {
154
+ file.errors = file.errors.filter((e) => e.code !== "too-many-files");
155
+ changed = true;
156
+ }
157
+ return file;
158
+ });
159
+ if (changed) {
160
+ setValue(newFiles);
161
+ }
162
+ }
163
+ }, [maxFiles, setValue, value]);
164
+ const inputProps = react.useMemo(() => {
165
+ const inputProps2 = { ...fieldProps, name };
166
+ if (fileSizeText) {
167
+ inputProps2["aria-describedby"] = inputProps2["aria-describedby"] ? `${inputProps2["aria-describedby"]} ${fileSizeTextId}` : fileSizeTextId;
168
+ }
169
+ return getInputProps(inputProps2);
170
+ }, [fieldProps, getInputProps, fileSizeTextId, name, fileSizeText]);
171
+ const showDropzone = react.useMemo(() => {
172
+ if (hideDropzoneOnValue) {
173
+ return value.length < maxFiles;
174
+ }
175
+ return true;
176
+ }, [hideDropzoneOnValue, maxFiles, value.length]);
177
+ return /* @__PURE__ */ jsxRuntime.jsx(
178
+ reactAriaComponents.Provider,
179
+ {
180
+ values: [
181
+ [
182
+ contexts.FileDropzoneStyleContext,
183
+ { slots, classNames, itemClassNames, ...variantProps }
184
+ ],
185
+ [
186
+ contexts.FileDropzoneStateContext,
187
+ {
188
+ isDisabled,
189
+ isReadOnly,
190
+ maxFiles,
191
+ maxFileSize,
192
+ showDropzone,
193
+ files: value,
194
+ handleRemoveFile,
195
+ handleRemoveRejection,
196
+ formatError,
197
+ inputProps,
198
+ triggerFileSelector,
199
+ ...dropzoneState
200
+ }
201
+ ],
202
+ [reactAriaComponents.LabelContext, labelProps],
203
+ [
204
+ reactAriaComponents.GroupContext,
205
+ {
206
+ role: "presentation",
207
+ isInvalid,
208
+ isDisabled: props.isDisabled || false
209
+ }
210
+ ],
211
+ [
212
+ reactAriaComponents.TextContext,
213
+ {
214
+ slots: {
215
+ fileSize: {},
216
+ description: descriptionProps,
217
+ errorMessage: errorMessageProps
218
+ }
219
+ }
220
+ ],
221
+ [reactAriaComponents.FieldErrorContext, { isInvalid, validationErrors, validationDetails }]
222
+ ],
223
+ children: /* @__PURE__ */ jsxRuntime.jsxs(reactAriaComponents.Group, { className: slots.base({ className: classNames?.base }), children: [
224
+ label && /* @__PURE__ */ jsxRuntime.jsx(field.Label, { size: variantProps.size, children: label }),
225
+ showDropzone && /* @__PURE__ */ jsxRuntime.jsx(FileDropzoneDropzone, {}),
226
+ value.map((file) => {
227
+ if (typeof children === "function") {
228
+ return children({
229
+ file,
230
+ removeFile: () => handleRemoveFile(file.name)
231
+ });
232
+ }
233
+ return /* @__PURE__ */ jsxRuntime.jsx(fileInfo.FileInfo, { imagePreview, file }, file.name);
234
+ }),
235
+ rejections.length >= 1 && rejections.map((rj) => /* @__PURE__ */ jsxRuntime.jsx(fileInfo.FileInfo, { imagePreview, file: rj }, rj.name)),
236
+ fileSizeText && /* @__PURE__ */ jsxRuntime.jsx(
237
+ field.Description,
238
+ {
239
+ size: variantProps.size,
240
+ id: fileSizeTextId,
241
+ slot: "fileSize",
242
+ children: fileSizeText
243
+ }
244
+ ),
245
+ description && /* @__PURE__ */ jsxRuntime.jsx(field.Description, { size: variantProps.size, children: description }),
246
+ errorMessage && /* @__PURE__ */ jsxRuntime.jsx(field.FieldError, { size: variantProps.size, children: errorMessage })
247
+ ] })
248
+ }
249
+ );
250
+ };
251
+ const FileDropzoneDropzone = () => {
252
+ const {
253
+ maxFiles,
254
+ getRootProps,
255
+ inputProps,
256
+ triggerFileSelector,
257
+ isDisabled,
258
+ isDragActive
259
+ } = contexts.useFileDropzoneStateContext();
260
+ const { slots, classNames } = contexts.useFileDropzoneStyleContext();
261
+ return /* @__PURE__ */ jsxRuntime.jsxs(
262
+ "div",
263
+ {
264
+ ...getRootProps({
265
+ "aria-disabled": isDisabled,
266
+ className: slots.group({
267
+ className: classNames?.group
268
+ })
269
+ }),
270
+ tabIndex: isDisabled ? void 0 : 0,
271
+ onClick: triggerFileSelector,
272
+ onKeyDown: (e) => {
273
+ if (e.key === "Enter" || e.key === " ") {
274
+ e.preventDefault();
275
+ triggerFileSelector();
276
+ }
277
+ },
278
+ children: [
279
+ /* @__PURE__ */ jsxRuntime.jsx("input", { ...inputProps }),
280
+ /* @__PURE__ */ jsxRuntime.jsxs(
281
+ "div",
282
+ {
283
+ "data-dragging": ouiTheme.dataAttr(isDragActive),
284
+ className: slots.dropzone({ className: classNames?.dropzone }),
285
+ children: [
286
+ /* @__PURE__ */ jsxRuntime.jsx(upload.default, { className: slots.icon({ className: classNames?.icon }) }),
287
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: slots.text({ className: classNames?.text }), children: [
288
+ /* @__PURE__ */ jsxRuntime.jsxs(
289
+ "span",
290
+ {
291
+ className: slots.dropzoneHighlight({
292
+ className: classNames?.dropzoneHighlight
293
+ }),
294
+ children: [
295
+ "Choose ",
296
+ maxFiles === 1 ? `file` : `files`
297
+ ]
298
+ }
299
+ ),
300
+ " ",
301
+ "or drag and drop here"
302
+ ] })
303
+ ]
304
+ }
305
+ )
306
+ ]
307
+ }
308
+ );
309
+ };
310
+
311
+ exports.FileDropzone = FileDropzone;
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ "use client";
3
+ 'use strict';
4
+
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var react = require('react');
7
+ var ouiTheme = require('@opengovsg/oui-theme');
8
+ var button = require('../button/button.cjs');
9
+ var contexts = require('./contexts.cjs');
10
+ var utils = require('./utils.cjs');
11
+ var trash2 = require('../node_modules/.pnpm/lucide-react@0.475.0_react@19.0.0/node_modules/lucide-react/dist/esm/icons/trash-2.cjs');
12
+
13
+ const FileInfo = ({ file, imagePreview, classNames }) => {
14
+ const {
15
+ handleRemoveFile,
16
+ handleRemoveRejection,
17
+ formatError,
18
+ isDisabled,
19
+ isReadOnly
20
+ } = contexts.useFileDropzoneStateContext();
21
+ const { size, variant, itemClassNames } = contexts.useFileDropzoneStyleContext();
22
+ const readableFileSize = utils.formatBytes(file.size, 2);
23
+ const styles = ouiTheme.fileInfoDropzoneStyles({
24
+ size,
25
+ variant,
26
+ imagePreview: imagePreview ?? void 0
27
+ });
28
+ const [previewSrc, setPreviewSrc] = react.useState("");
29
+ react.useEffect(() => {
30
+ let objectUrl = "";
31
+ if (file.type.startsWith("image/")) {
32
+ objectUrl = URL.createObjectURL(file);
33
+ setPreviewSrc(objectUrl);
34
+ }
35
+ return () => URL.revokeObjectURL(objectUrl);
36
+ }, [file]);
37
+ return /* @__PURE__ */ jsxRuntime.jsxs(
38
+ "div",
39
+ {
40
+ className: styles.base({
41
+ className: ouiTheme.cn(itemClassNames?.base, classNames?.base)
42
+ }),
43
+ children: [
44
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sr-only", children: [
45
+ "File attached: ",
46
+ file.name,
47
+ " with file size of ",
48
+ readableFileSize
49
+ ] }),
50
+ imagePreview && previewSrc && /* @__PURE__ */ jsxRuntime.jsx(
51
+ "div",
52
+ {
53
+ className: styles.imageContainer({
54
+ className: ouiTheme.cn(
55
+ itemClassNames?.imageContainer,
56
+ classNames?.imageContainer
57
+ )
58
+ }),
59
+ children: /* @__PURE__ */ jsxRuntime.jsx(
60
+ "img",
61
+ {
62
+ src: previewSrc,
63
+ alt: `Image preview of uploaded file: ${file.name}`,
64
+ className: styles.image({
65
+ className: ouiTheme.cn(itemClassNames?.image, classNames?.image)
66
+ })
67
+ }
68
+ )
69
+ }
70
+ ),
71
+ /* @__PURE__ */ jsxRuntime.jsxs(
72
+ "div",
73
+ {
74
+ className: styles.container({
75
+ className: ouiTheme.cn(itemClassNames?.container, classNames?.container)
76
+ }),
77
+ children: [
78
+ /* @__PURE__ */ jsxRuntime.jsxs(
79
+ "div",
80
+ {
81
+ className: styles.textContainer({
82
+ className: ouiTheme.cn(
83
+ itemClassNames?.textContainer,
84
+ classNames?.textContainer
85
+ )
86
+ }),
87
+ children: [
88
+ /* @__PURE__ */ jsxRuntime.jsx(
89
+ "p",
90
+ {
91
+ title: file.name,
92
+ className: styles.name({
93
+ className: ouiTheme.cn(itemClassNames?.name, classNames?.name)
94
+ }),
95
+ children: file.name
96
+ }
97
+ ),
98
+ /* @__PURE__ */ jsxRuntime.jsx(
99
+ "p",
100
+ {
101
+ className: styles.size({
102
+ className: ouiTheme.cn(itemClassNames?.size, classNames?.size)
103
+ }),
104
+ children: readableFileSize
105
+ }
106
+ ),
107
+ file.errors?.length && /* @__PURE__ */ jsxRuntime.jsx(
108
+ "p",
109
+ {
110
+ className: styles.error({
111
+ className: ouiTheme.cn(itemClassNames?.error, classNames?.error)
112
+ }),
113
+ children: file.errors.map(formatError).join(", ")
114
+ }
115
+ )
116
+ ]
117
+ }
118
+ ),
119
+ /* @__PURE__ */ jsxRuntime.jsx(
120
+ button.Button,
121
+ {
122
+ isDisabled: isDisabled || isReadOnly,
123
+ isIconOnly: !file.errors?.length,
124
+ size: size === "md" ? "md" : "xs",
125
+ variant: "clear",
126
+ color: file.errors?.length ? "main" : "critical",
127
+ "aria-label": "Remove file",
128
+ className: styles.actionButton({
129
+ className: ouiTheme.cn(
130
+ itemClassNames?.actionButton,
131
+ classNames?.actionButton
132
+ )
133
+ }),
134
+ onPress: () => file.errors?.length ? handleRemoveRejection(file.name) : handleRemoveFile(file.name),
135
+ children: file.errors?.length ? "Dismiss" : /* @__PURE__ */ jsxRuntime.jsx(trash2.default, {})
136
+ }
137
+ )
138
+ ]
139
+ }
140
+ )
141
+ ]
142
+ }
143
+ );
144
+ };
145
+
146
+ exports.FileInfo = FileInfo;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ 'use strict';
3
+
4
+ var fileDropzone = require('./file-dropzone.cjs');
5
+ var fileInfo = require('./file-info.cjs');
6
+ var utils = require('./utils.cjs');
7
+
8
+
9
+
10
+ exports.FileDropzone = fileDropzone.FileDropzone;
11
+ exports.FileInfo = fileInfo.FileInfo;
12
+ exports.formatBytes = utils.formatBytes;
13
+ exports.formatErrorMessage = utils.formatErrorMessage;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ 'use strict';
3
+
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ 'use strict';
3
+
4
+ var reactDropzone = require('react-dropzone');
5
+
6
+ const formatBytes = (bytes, decimals = 2, size) => {
7
+ const k = 1e3;
8
+ const dm = decimals < 0 ? 0 : decimals;
9
+ const sizes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
10
+ if (bytes === 0 || bytes === void 0)
11
+ return size !== void 0 ? `0 ${size}` : "0 bytes";
12
+ const i = size !== void 0 ? sizes.indexOf(size) : Math.floor(Math.log(bytes) / Math.log(k));
13
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
14
+ };
15
+ const formatErrorMessage = (error, config) => {
16
+ const { maxFileSize, minFileSize, maxFiles } = config;
17
+ switch (error.code) {
18
+ case reactDropzone.ErrorCode.FileTooLarge:
19
+ return `You have exceeded the size limit, please upload a file below ${formatBytes(maxFileSize, 2)}`;
20
+ case reactDropzone.ErrorCode.FileTooSmall:
21
+ return `Please upload a file above ${formatBytes(minFileSize, 2)}`;
22
+ case reactDropzone.ErrorCode.TooManyFiles:
23
+ return `Maximum number of files allowed is ${maxFiles}.`;
24
+ default: {
25
+ return error.message;
26
+ }
27
+ }
28
+ };
29
+
30
+ exports.formatBytes = formatBytes;
31
+ exports.formatErrorMessage = formatErrorMessage;
@@ -3,7 +3,7 @@
3
3
  'use strict';
4
4
 
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
- var $670gB$react = require('react');
6
+ var react = require('react');
7
7
  var reactAria = require('react-aria');
8
8
  var reactStately = require('react-stately');
9
9
  var ouiTheme = require('@opengovsg/oui-theme');
@@ -18,8 +18,8 @@ function GovtBanner({
18
18
  ...props
19
19
  }) {
20
20
  const state = reactStately.useDisclosureState(props);
21
- const panelRef = $670gB$react.useRef(null);
22
- const triggerRef = $670gB$react.useRef(null);
21
+ const panelRef = react.useRef(null);
22
+ const triggerRef = react.useRef(null);
23
23
  const { buttonProps: triggerProps, panelProps } = reactAria.useDisclosure(
24
24
  props,
25
25
  state,
@@ -2,7 +2,9 @@
2
2
  'use strict';
3
3
 
4
4
  var useControllableState = require('./use-controllable-state.cjs');
5
+ var useDraggable = require('./use-draggable.cjs');
5
6
 
6
7
 
7
8
 
8
9
  exports.useControllableState = useControllableState.useControllableState;
10
+ exports.useDraggable = useDraggable.useDraggable;
@@ -2,16 +2,16 @@
2
2
  "use client";
3
3
  'use strict';
4
4
 
5
- var $670gB$react = require('react');
5
+ var react = require('react');
6
6
 
7
7
  function useCallbackRef(callback, deps = []) {
8
- const callbackRef = $670gB$react.useRef(() => {
8
+ const callbackRef = react.useRef(() => {
9
9
  throw new Error("Cannot call an event handler while rendering.");
10
10
  });
11
- $670gB$react.useInsertionEffect(() => {
11
+ react.useInsertionEffect(() => {
12
12
  callbackRef.current = callback;
13
13
  });
14
- return $670gB$react.useCallback((...args) => callbackRef.current?.(...args), deps);
14
+ return react.useCallback((...args) => callbackRef.current?.(...args), deps);
15
15
  }
16
16
 
17
17
  exports.useCallbackRef = useCallbackRef;
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
  'use strict';
4
4
 
5
- var $670gB$react = require('react');
5
+ var react = require('react');
6
6
  var useCallbackRef = require('./use-callback-ref.cjs');
7
7
 
8
8
  function useControllableState(props) {
@@ -14,7 +14,7 @@ function useControllableState(props) {
14
14
  } = props;
15
15
  const onChangeProp = useCallbackRef.useCallbackRef(onChange);
16
16
  const shouldUpdateProp = useCallbackRef.useCallbackRef(shouldUpdate);
17
- const [uncontrolledState, setUncontrolledState] = $670gB$react.useState(defaultValue);
17
+ const [uncontrolledState, setUncontrolledState] = react.useState(defaultValue);
18
18
  const controlled = valueProp !== void 0;
19
19
  const value = controlled ? valueProp : uncontrolledState;
20
20
  const setValue = useCallbackRef.useCallbackRef(
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ "use client";
3
+ 'use strict';
4
+
5
+ var react = require('react');
6
+ var interactions = require('@react-aria/interactions');
7
+
8
+ function useDraggable(props) {
9
+ const { targetRef, isDisabled = false, canOverflow = false } = props;
10
+ const boundary = react.useRef({ minLeft: 0, minTop: 0, maxLeft: 0, maxTop: 0 });
11
+ const isDragging = react.useRef(false);
12
+ let transform = { offsetX: 0, offsetY: 0 };
13
+ const onMoveStart = react.useCallback(() => {
14
+ isDragging.current = true;
15
+ const { offsetX, offsetY } = transform;
16
+ const targetRect = targetRef?.current?.getBoundingClientRect();
17
+ const targetLeft = targetRect?.left ?? 0;
18
+ const targetTop = targetRect?.top ?? 0;
19
+ const targetWidth = targetRect?.width ?? 0;
20
+ const targetHeight = targetRect?.height ?? 0;
21
+ const clientWidth = document.documentElement.clientWidth;
22
+ const clientHeight = document.documentElement.clientHeight;
23
+ const minLeft = -targetLeft + offsetX;
24
+ const minTop = -targetTop + offsetY;
25
+ const maxLeft = clientWidth - targetLeft - targetWidth + offsetX;
26
+ const maxTop = clientHeight - targetTop - targetHeight + offsetY;
27
+ boundary.current = {
28
+ minLeft,
29
+ minTop,
30
+ maxLeft,
31
+ maxTop
32
+ };
33
+ }, [transform, targetRef?.current]);
34
+ const onMove = react.useCallback(
35
+ (e) => {
36
+ if (isDisabled) {
37
+ return;
38
+ }
39
+ const { offsetX, offsetY } = transform;
40
+ const { minLeft, minTop, maxLeft, maxTop } = boundary.current;
41
+ let moveX = offsetX + e.deltaX;
42
+ let moveY = offsetY + e.deltaY;
43
+ if (!canOverflow) {
44
+ moveX = Math.min(Math.max(moveX, minLeft), maxLeft);
45
+ moveY = Math.min(Math.max(moveY, minTop), maxTop);
46
+ }
47
+ transform = {
48
+ offsetX: moveX,
49
+ offsetY: moveY
50
+ };
51
+ if (targetRef?.current) {
52
+ targetRef.current.style.transform = `translate(${moveX}px, ${moveY}px)`;
53
+ }
54
+ },
55
+ [isDisabled, transform, boundary.current, canOverflow, targetRef?.current]
56
+ );
57
+ const onMoveEnd = react.useCallback(() => {
58
+ isDragging.current = false;
59
+ }, []);
60
+ const { moveProps } = interactions.useMove({
61
+ onMoveStart,
62
+ onMove,
63
+ onMoveEnd
64
+ });
65
+ const preventDefault = react.useCallback((e) => {
66
+ if (isDragging.current) {
67
+ e.preventDefault();
68
+ }
69
+ }, []);
70
+ react.useEffect(() => {
71
+ if (!isDisabled) {
72
+ document.body.addEventListener("touchmove", preventDefault, {
73
+ passive: false
74
+ });
75
+ }
76
+ return () => {
77
+ document.body.removeEventListener("touchmove", preventDefault);
78
+ };
79
+ }, [isDisabled, preventDefault]);
80
+ return {
81
+ moveProps: {
82
+ ...moveProps,
83
+ style: { cursor: !isDisabled ? "move" : void 0 }
84
+ }
85
+ };
86
+ }
87
+
88
+ exports.useDraggable = useDraggable;