@telus-uds/components-base 1.92.0 → 1.94.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -2
- package/lib/A11yText/index.js +7 -2
- package/lib/Button/ButtonGroup.js +17 -1
- package/lib/Card/Card.js +12 -0
- package/lib/Card/CardBase.js +37 -2
- package/lib/Carousel/Carousel.js +55 -13
- package/lib/Carousel/CarouselItem/CarouselItem.js +86 -12
- package/lib/Fieldset/FieldsetContainer.js +7 -2
- package/lib/Fieldset/FieldsetContainer.native.js +4 -1
- package/lib/FileUpload/FileUpload.js +336 -0
- package/lib/FileUpload/NotificationContent.js +60 -0
- package/lib/FileUpload/dictionary.js +47 -0
- package/lib/FileUpload/index.js +10 -0
- package/lib/Link/TextButton.js +7 -17
- package/lib/Modal/ModalContent.js +12 -6
- package/lib/MultiSelectFilter/ModalOverlay.js +49 -30
- package/lib/MultiSelectFilter/MultiSelectFilter.js +170 -131
- package/lib/Notification/Notification.js +11 -2
- package/lib/Status/Status.js +9 -4
- package/lib/index.js +8 -0
- package/lib/utils/convertFromMegaByteToByte.js +16 -0
- package/lib/utils/formatImageSource.js +34 -0
- package/lib/utils/index.js +17 -1
- package/lib-module/A11yText/index.js +7 -2
- package/lib-module/Button/ButtonGroup.js +17 -1
- package/lib-module/Card/Card.js +13 -1
- package/lib-module/Card/CardBase.js +38 -3
- package/lib-module/Carousel/Carousel.js +55 -13
- package/lib-module/Carousel/CarouselItem/CarouselItem.js +86 -12
- package/lib-module/Fieldset/FieldsetContainer.js +7 -2
- package/lib-module/Fieldset/FieldsetContainer.native.js +4 -1
- package/lib-module/FileUpload/FileUpload.js +329 -0
- package/lib-module/FileUpload/NotificationContent.js +55 -0
- package/lib-module/FileUpload/dictionary.js +40 -0
- package/lib-module/FileUpload/index.js +2 -0
- package/lib-module/Link/TextButton.js +7 -17
- package/lib-module/Modal/ModalContent.js +12 -6
- package/lib-module/MultiSelectFilter/ModalOverlay.js +49 -30
- package/lib-module/MultiSelectFilter/MultiSelectFilter.js +171 -132
- package/lib-module/Notification/Notification.js +11 -2
- package/lib-module/Status/Status.js +9 -4
- package/lib-module/index.js +1 -0
- package/lib-module/utils/convertFromMegaByteToByte.js +10 -0
- package/lib-module/utils/formatImageSource.js +27 -0
- package/lib-module/utils/index.js +3 -1
- package/package.json +4 -3
- package/src/A11yText/index.jsx +7 -3
- package/src/Button/ButtonGroup.jsx +9 -1
- package/src/Card/Card.jsx +18 -2
- package/src/Card/CardBase.jsx +47 -12
- package/src/Carousel/Carousel.jsx +59 -13
- package/src/Carousel/CarouselItem/CarouselItem.jsx +94 -9
- package/src/Fieldset/FieldsetContainer.jsx +4 -3
- package/src/Fieldset/FieldsetContainer.native.jsx +9 -6
- package/src/FileUpload/FileUpload.jsx +396 -0
- package/src/FileUpload/NotificationContent.jsx +44 -0
- package/src/FileUpload/dictionary.js +40 -0
- package/src/FileUpload/index.js +3 -0
- package/src/Link/TextButton.jsx +10 -17
- package/src/Modal/ModalContent.jsx +8 -3
- package/src/MultiSelectFilter/ModalOverlay.jsx +44 -18
- package/src/MultiSelectFilter/MultiSelectFilter.jsx +188 -126
- package/src/Notification/Notification.jsx +12 -4
- package/src/Status/Status.jsx +15 -4
- package/src/index.js +1 -0
- package/src/utils/convertFromMegaByteToByte.js +11 -0
- package/src/utils/formatImageSource.js +29 -0
- package/src/utils/index.js +2 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View, Platform } from 'react-native'
|
|
3
|
+
import PropTypes from 'prop-types'
|
|
4
|
+
|
|
5
|
+
import { useThemeTokens } from '../ThemeProvider'
|
|
6
|
+
import {
|
|
7
|
+
a11yProps,
|
|
8
|
+
copyPropTypes,
|
|
9
|
+
getTokensPropType,
|
|
10
|
+
selectSystemProps,
|
|
11
|
+
useCopy,
|
|
12
|
+
variantProp,
|
|
13
|
+
viewProps,
|
|
14
|
+
convertFromMegaByteToByte
|
|
15
|
+
} from '../utils'
|
|
16
|
+
|
|
17
|
+
import InputLabel from '../InputLabel'
|
|
18
|
+
import { Button } from '../Button'
|
|
19
|
+
import Notification from '../Notification'
|
|
20
|
+
import Spacer from '../Spacer'
|
|
21
|
+
import StackView from '../StackView'
|
|
22
|
+
|
|
23
|
+
import NotificationContent from './NotificationContent'
|
|
24
|
+
import dictionary from './dictionary'
|
|
25
|
+
|
|
26
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
27
|
+
|
|
28
|
+
const getHintFromFileTypes = (fileTypes) => {
|
|
29
|
+
const acceptedFileTypes = fileTypes.map((type) => type.toUpperCase())
|
|
30
|
+
|
|
31
|
+
return acceptedFileTypes.reduce((acc, curr, index) => {
|
|
32
|
+
if (index === 0) {
|
|
33
|
+
return `${curr}`
|
|
34
|
+
}
|
|
35
|
+
return `${acc}, ${curr}`
|
|
36
|
+
}, '')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const stringifyFileTypesList = (fileTypes, getCopy) => {
|
|
40
|
+
const acceptedFileTypes = fileTypes.map((type) => `.${type.toUpperCase()}`)
|
|
41
|
+
|
|
42
|
+
return acceptedFileTypes.reduce((acc, curr, index) => {
|
|
43
|
+
if (index === 0) {
|
|
44
|
+
return curr
|
|
45
|
+
}
|
|
46
|
+
if (index === acceptedFileTypes.length - 1) {
|
|
47
|
+
return `${acc} ${getCopy('or')} ${curr}`
|
|
48
|
+
}
|
|
49
|
+
return `${acc}, ${curr}`
|
|
50
|
+
}, '')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const selectComponentTokens = (tokens, componentIdentifier) => {
|
|
54
|
+
return Object.entries(tokens).reduce((acc, [key, value]) => {
|
|
55
|
+
if (key.startsWith(componentIdentifier) && value !== null) {
|
|
56
|
+
const newKey =
|
|
57
|
+
key.replace(componentIdentifier, '').replace('Text', '').charAt(0).toLowerCase() +
|
|
58
|
+
key.replace(componentIdentifier, '').replace('Text', '').slice(1)
|
|
59
|
+
acc[newKey] = value
|
|
60
|
+
}
|
|
61
|
+
return acc
|
|
62
|
+
}, {})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* FileUpload component for uploading files.
|
|
67
|
+
*
|
|
68
|
+
* @component
|
|
69
|
+
* @example
|
|
70
|
+
* // Usage:
|
|
71
|
+
* <FileUpload
|
|
72
|
+
* tokens={tokens}
|
|
73
|
+
* variant={variant}
|
|
74
|
+
* copy={copy}
|
|
75
|
+
* fileTypes={fileTypes}
|
|
76
|
+
* allowMultipleFiles={allowMultipleFiles}
|
|
77
|
+
* maxFileSize={maxFileSize}
|
|
78
|
+
* maxFilesCount={maxFilesCount}
|
|
79
|
+
* onUpload={onUpload}
|
|
80
|
+
* onDelete={onDelete}
|
|
81
|
+
* documentPicker={documentPicker}
|
|
82
|
+
* {...rest}
|
|
83
|
+
* />
|
|
84
|
+
*
|
|
85
|
+
* @param {Object} props - The component props.
|
|
86
|
+
* @param {Object} props.tokens - The tokens for styling the component.
|
|
87
|
+
* @param {string} props.variant - The variant of the component.
|
|
88
|
+
* @param {string} props.copy - The copy for the component.
|
|
89
|
+
* @param {string[]} props.fileTypes - The allowed file types for upload.
|
|
90
|
+
* @param {boolean} props.allowMultipleFiles - Whether multiple files can be uploaded.
|
|
91
|
+
* @param {number} props.maxFileSize - The maximum file size in megabytes.
|
|
92
|
+
* @param {number} props.maxFilesCount - The maximum number of files that can be uploaded.
|
|
93
|
+
* @param {Function} props.onUpload - The callback function for file upload.
|
|
94
|
+
* @param {Function} props.onDelete - The callback function for file deletion.
|
|
95
|
+
* @param {Object} props.documentPicker - The document picker object.
|
|
96
|
+
* @param {any} props.rest - Additional props to be spread to the component.
|
|
97
|
+
* @returns {JSX.Element} The rendered FileUpload component.
|
|
98
|
+
*/
|
|
99
|
+
const FileUpload = React.forwardRef(
|
|
100
|
+
(
|
|
101
|
+
{
|
|
102
|
+
tokens,
|
|
103
|
+
variant,
|
|
104
|
+
copy = 'en',
|
|
105
|
+
fileTypes,
|
|
106
|
+
allowMultipleFiles = false,
|
|
107
|
+
maxFileSize,
|
|
108
|
+
maxFilesCount = 1,
|
|
109
|
+
onUpload,
|
|
110
|
+
onDelete,
|
|
111
|
+
documentPicker,
|
|
112
|
+
...rest
|
|
113
|
+
},
|
|
114
|
+
ref
|
|
115
|
+
) => {
|
|
116
|
+
const themeTokens = useThemeTokens('FileUpload', tokens, variant)
|
|
117
|
+
const getCopy = useCopy({ dictionary, copy })
|
|
118
|
+
|
|
119
|
+
const inputFileRef = React.useRef(null)
|
|
120
|
+
|
|
121
|
+
const [filesStatus, setFilesStatus] = React.useState([])
|
|
122
|
+
const [renderTrigger, setRenderTrigger] = React.useState(0)
|
|
123
|
+
|
|
124
|
+
React.useEffect(() => {
|
|
125
|
+
setRenderTrigger((prev) => prev + 1)
|
|
126
|
+
}, [filesStatus])
|
|
127
|
+
|
|
128
|
+
const areFileTypesExtensionsValid = (files) =>
|
|
129
|
+
files.some((file) => {
|
|
130
|
+
const fileExtension = file.name.split('.').pop().toLowerCase()
|
|
131
|
+
|
|
132
|
+
if (!fileTypes.includes(fileExtension)) {
|
|
133
|
+
setFilesStatus([
|
|
134
|
+
{
|
|
135
|
+
description: getCopy('wrongFileType').replace(
|
|
136
|
+
'%{fileType}',
|
|
137
|
+
stringifyFileTypesList(fileTypes, getCopy)
|
|
138
|
+
),
|
|
139
|
+
style: 'error'
|
|
140
|
+
}
|
|
141
|
+
])
|
|
142
|
+
return false
|
|
143
|
+
}
|
|
144
|
+
return true
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const areFileSizesWithinLimit = (files) =>
|
|
148
|
+
files.some((file) => {
|
|
149
|
+
if (file.size > convertFromMegaByteToByte(maxFileSize)) {
|
|
150
|
+
setFilesStatus([
|
|
151
|
+
{
|
|
152
|
+
description: getCopy('fileTooBig').replace('%{size}', maxFileSize),
|
|
153
|
+
style: 'error'
|
|
154
|
+
}
|
|
155
|
+
])
|
|
156
|
+
return false
|
|
157
|
+
}
|
|
158
|
+
return true
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
const areMoreFilesUploadedThanAllowed = (files) => {
|
|
162
|
+
if (files.length > maxFilesCount) {
|
|
163
|
+
setFilesStatus([
|
|
164
|
+
{
|
|
165
|
+
description: getCopy('tooManyFiles').replace('%{maxFilesCount}', maxFilesCount),
|
|
166
|
+
style: 'error'
|
|
167
|
+
}
|
|
168
|
+
])
|
|
169
|
+
return true
|
|
170
|
+
}
|
|
171
|
+
return false
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const handleFilesUpload = async (files) => {
|
|
175
|
+
try {
|
|
176
|
+
const results = await onUpload(files)
|
|
177
|
+
const currentFilesStatus = files.map((file, index) => {
|
|
178
|
+
if (results[index].error) {
|
|
179
|
+
return {
|
|
180
|
+
description: (
|
|
181
|
+
<NotificationContent
|
|
182
|
+
file={file}
|
|
183
|
+
color={selectComponentTokens(themeTokens, 'notification').color}
|
|
184
|
+
>{` ${getCopy('uploadError')} ${results[index].error}.`}</NotificationContent>
|
|
185
|
+
),
|
|
186
|
+
style: 'error'
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
description: (
|
|
191
|
+
<NotificationContent
|
|
192
|
+
file={file}
|
|
193
|
+
color={selectComponentTokens(themeTokens, 'notification').color}
|
|
194
|
+
>{` ${getCopy('uploadSuccess')}`}</NotificationContent>
|
|
195
|
+
),
|
|
196
|
+
style: 'success',
|
|
197
|
+
file,
|
|
198
|
+
onDismiss: () => handleFileDeletion(file)
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
setFilesStatus(currentFilesStatus)
|
|
202
|
+
} catch (error) {
|
|
203
|
+
setFilesStatus([
|
|
204
|
+
{
|
|
205
|
+
description: allowMultipleFiles
|
|
206
|
+
? getCopy('problemUploadingMultipleFiles')
|
|
207
|
+
: getCopy('problemUploading'),
|
|
208
|
+
style: 'error'
|
|
209
|
+
}
|
|
210
|
+
])
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const handleFileDeletion = async (file) => {
|
|
215
|
+
const copyFile = { ...file }
|
|
216
|
+
try {
|
|
217
|
+
const result = await onDelete(copyFile)
|
|
218
|
+
setFilesStatus((prevStatus) => {
|
|
219
|
+
const filteredFilesStatus = prevStatus.filter((f) => f.file && f.file.name !== file.name)
|
|
220
|
+
return result.error
|
|
221
|
+
? [
|
|
222
|
+
...filteredFilesStatus,
|
|
223
|
+
{
|
|
224
|
+
description: (
|
|
225
|
+
<NotificationContent
|
|
226
|
+
file={file}
|
|
227
|
+
color={selectComponentTokens(themeTokens, 'notification').color}
|
|
228
|
+
>{` ${getCopy('uploadSuccess')}`}</NotificationContent>
|
|
229
|
+
),
|
|
230
|
+
style: 'success',
|
|
231
|
+
file,
|
|
232
|
+
onDismiss: () => handleFileDeletion(file)
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
description: (
|
|
236
|
+
<NotificationContent
|
|
237
|
+
file={file}
|
|
238
|
+
color={selectComponentTokens(themeTokens, 'notification').color}
|
|
239
|
+
>{` ${getCopy('deleteProblem')} ${result.error}.`}</NotificationContent>
|
|
240
|
+
),
|
|
241
|
+
style: 'error',
|
|
242
|
+
file
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
: filteredFilesStatus
|
|
246
|
+
})
|
|
247
|
+
} catch (error) {
|
|
248
|
+
setFilesStatus([
|
|
249
|
+
{
|
|
250
|
+
description: (
|
|
251
|
+
<NotificationContent
|
|
252
|
+
file={file}
|
|
253
|
+
color={selectComponentTokens(themeTokens, 'notification').color}
|
|
254
|
+
>{` ${getCopy('problemDeleting')}`}</NotificationContent>
|
|
255
|
+
),
|
|
256
|
+
style: 'error'
|
|
257
|
+
}
|
|
258
|
+
])
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const handleOnChange = async (event) => {
|
|
263
|
+
const targetFiles = event.target.files
|
|
264
|
+
const files = Array.from(targetFiles)
|
|
265
|
+
|
|
266
|
+
if (files.length === 0) return
|
|
267
|
+
|
|
268
|
+
if (fileTypes && !areFileTypesExtensionsValid(files)) return
|
|
269
|
+
|
|
270
|
+
if (maxFileSize && !areFileSizesWithinLimit(files)) return
|
|
271
|
+
|
|
272
|
+
if (maxFilesCount && areMoreFilesUploadedThanAllowed(files)) return
|
|
273
|
+
|
|
274
|
+
await handleFilesUpload(files)
|
|
275
|
+
|
|
276
|
+
if (inputFileRef.current) inputFileRef.current.value = ''
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const onFileUploadButtonClick = async () => {
|
|
280
|
+
if (Platform.OS === 'web') {
|
|
281
|
+
inputFileRef.current.click()
|
|
282
|
+
} else {
|
|
283
|
+
try {
|
|
284
|
+
const result = await documentPicker.getDocumentAsync({
|
|
285
|
+
multiple: allowMultipleFiles
|
|
286
|
+
})
|
|
287
|
+
if (result) {
|
|
288
|
+
const files = Array.isArray(result) ? result : [result]
|
|
289
|
+
if (files.find((file) => file.type === 'cancel')) return
|
|
290
|
+
const event = { target: { files } }
|
|
291
|
+
await handleOnChange(event)
|
|
292
|
+
}
|
|
293
|
+
} catch (_) {
|
|
294
|
+
setFilesStatus([
|
|
295
|
+
{
|
|
296
|
+
description: allowMultipleFiles
|
|
297
|
+
? getCopy('problemUploadingFiles')
|
|
298
|
+
: getCopy('problemUploading'),
|
|
299
|
+
style: 'error'
|
|
300
|
+
}
|
|
301
|
+
])
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return (
|
|
307
|
+
<View ref={ref} {...selectProps(rest)}>
|
|
308
|
+
<InputLabel
|
|
309
|
+
label={getCopy('label')}
|
|
310
|
+
hint={
|
|
311
|
+
fileTypes &&
|
|
312
|
+
getCopy('allowedFileTypes').replace('%{fileTypes}', getHintFromFileTypes(fileTypes))
|
|
313
|
+
}
|
|
314
|
+
hintPosition="below"
|
|
315
|
+
/>
|
|
316
|
+
<Spacer space={4} />
|
|
317
|
+
{Platform.OS === 'web' && (
|
|
318
|
+
<input
|
|
319
|
+
type="file"
|
|
320
|
+
multiple={allowMultipleFiles}
|
|
321
|
+
ref={inputFileRef}
|
|
322
|
+
hidden={true}
|
|
323
|
+
accept={fileTypes && fileTypes.map((type) => `.${type}`).join(', ')}
|
|
324
|
+
onChange={handleOnChange}
|
|
325
|
+
/>
|
|
326
|
+
)}
|
|
327
|
+
<Button
|
|
328
|
+
onClick={onFileUploadButtonClick}
|
|
329
|
+
tokens={selectComponentTokens(themeTokens, 'button')}
|
|
330
|
+
>
|
|
331
|
+
{getCopy('buttonLabel')}
|
|
332
|
+
</Button>
|
|
333
|
+
{filesStatus.length > 0 && (
|
|
334
|
+
<View>
|
|
335
|
+
<Spacer space={4} />
|
|
336
|
+
<StackView space={4}>
|
|
337
|
+
{filesStatus.map(({ description, style, onDismiss }, index) => (
|
|
338
|
+
<Notification
|
|
339
|
+
key={`${index.toString()}-${renderTrigger}`}
|
|
340
|
+
variant={{ style }}
|
|
341
|
+
dismissible={!!onDismiss}
|
|
342
|
+
onDismiss={onDismiss}
|
|
343
|
+
tokens={selectComponentTokens(themeTokens, 'notification')}
|
|
344
|
+
>
|
|
345
|
+
{description}
|
|
346
|
+
</Notification>
|
|
347
|
+
))}
|
|
348
|
+
</StackView>
|
|
349
|
+
</View>
|
|
350
|
+
)}
|
|
351
|
+
</View>
|
|
352
|
+
)
|
|
353
|
+
}
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
FileUpload.displayName = 'FileUpload'
|
|
357
|
+
|
|
358
|
+
FileUpload.propTypes = {
|
|
359
|
+
...selectedSystemPropTypes,
|
|
360
|
+
tokens: getTokensPropType('FileUpload'),
|
|
361
|
+
variant: variantProp.propType,
|
|
362
|
+
/**
|
|
363
|
+
* To define the locale of the copy
|
|
364
|
+
*/
|
|
365
|
+
copy: copyPropTypes,
|
|
366
|
+
/**
|
|
367
|
+
* The file types allowed for upload. Example: fileTypes={['pdf', 'docx']}.
|
|
368
|
+
*/
|
|
369
|
+
fileTypes: PropTypes.arrayOf(PropTypes.string),
|
|
370
|
+
/**
|
|
371
|
+
* Whether to allow multiple files to be uploaded.
|
|
372
|
+
*/
|
|
373
|
+
allowMultipleFiles: PropTypes.bool,
|
|
374
|
+
/**
|
|
375
|
+
* The maximum file size allowed for upload in MB.
|
|
376
|
+
*/
|
|
377
|
+
maxFileSize: PropTypes.number,
|
|
378
|
+
/**
|
|
379
|
+
* The maximum number of files allowed for upload.
|
|
380
|
+
*/
|
|
381
|
+
maxFilesCount: PropTypes.number,
|
|
382
|
+
/**
|
|
383
|
+
* Callback function called when a file is uploaded.
|
|
384
|
+
*/
|
|
385
|
+
onUpload: PropTypes.func,
|
|
386
|
+
/**
|
|
387
|
+
* Callback function called when a file is deleted
|
|
388
|
+
*/
|
|
389
|
+
onDelete: PropTypes.func,
|
|
390
|
+
/**
|
|
391
|
+
* The document picker to use for mobile
|
|
392
|
+
*/
|
|
393
|
+
documentPicker: PropTypes.object
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export default FileUpload
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import Typography from '../Typography'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Renders the content of a notification for a file upload.
|
|
7
|
+
*
|
|
8
|
+
* @component
|
|
9
|
+
* @param {Object} props - The component props.
|
|
10
|
+
* @param {Object} props.file - The file object.
|
|
11
|
+
* @param {string} props.children - The content to be displayed.
|
|
12
|
+
* @param {string} props.color - The color of the typography.
|
|
13
|
+
* @param {React.Ref} ref - The ref to be forwarded to the Typography component.
|
|
14
|
+
* @returns {JSX.Element} The rendered NotificationContent component.
|
|
15
|
+
*/
|
|
16
|
+
const NotificationContent = React.forwardRef(({ file, color, children }, ref) => {
|
|
17
|
+
return (
|
|
18
|
+
<Typography tokens={{ color }} ref={ref}>
|
|
19
|
+
<Typography variant={{ bold: true }} tokens={{ color }}>
|
|
20
|
+
{file.name}
|
|
21
|
+
</Typography>
|
|
22
|
+
{children}
|
|
23
|
+
</Typography>
|
|
24
|
+
)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
NotificationContent.displayName = 'NotificationContent'
|
|
28
|
+
|
|
29
|
+
NotificationContent.propTypes = {
|
|
30
|
+
/**
|
|
31
|
+
* The file object.
|
|
32
|
+
*/
|
|
33
|
+
file: PropTypes.object,
|
|
34
|
+
/**
|
|
35
|
+
* The content to be displayed.
|
|
36
|
+
*/
|
|
37
|
+
children: PropTypes.string,
|
|
38
|
+
/**
|
|
39
|
+
* The color of the typography.
|
|
40
|
+
*/
|
|
41
|
+
color: PropTypes.string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default NotificationContent
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
en: {
|
|
3
|
+
label: 'Upload files',
|
|
4
|
+
buttonLabel: 'Choose files',
|
|
5
|
+
dismissButtonLabel: 'Remove file',
|
|
6
|
+
wrongFileType: 'The selected file must be a %{fileType}.',
|
|
7
|
+
allowedFileTypes: 'Allowed file types: %{fileTypes}.',
|
|
8
|
+
fileTooBig: 'The selected file must be smaller than %{size}MB.',
|
|
9
|
+
fileIsEmpty: 'The selected file is empty.',
|
|
10
|
+
problemUploading: 'The selected file could not be uploaded. Try again.',
|
|
11
|
+
problemDeleting: 'The selected file could not be deleted. Try again.',
|
|
12
|
+
problemUploadingMultipleFiles: 'The selected files could not be uploaded. Try again.',
|
|
13
|
+
only: 'only',
|
|
14
|
+
and: 'and',
|
|
15
|
+
or: 'or',
|
|
16
|
+
uploadSuccess: 'uploaded successfully.',
|
|
17
|
+
uploadError: 'was not uploaded due to',
|
|
18
|
+
deleteProblem: 'was not deleted due to',
|
|
19
|
+
tooManyFiles: 'You can only select up to %{maxFilesCount} files at the same time.'
|
|
20
|
+
},
|
|
21
|
+
fr: {
|
|
22
|
+
label: 'Téléverser les fichiers"',
|
|
23
|
+
buttonLabel: 'Choisir les fichiers',
|
|
24
|
+
dismissButtonLabel: 'Supprimer le fichier',
|
|
25
|
+
wrongFileType: 'Le fichier sélectionné doit être au format %{fileType}.',
|
|
26
|
+
allowedFileTypes: 'Formats de fichiers autorisés : %{fileTypes}.',
|
|
27
|
+
fileTooBig: 'Le fichier sélectionné doit être inférieur à %{size}Mo.',
|
|
28
|
+
fileIsEmpty: 'Le fichier sélectionné est vide.',
|
|
29
|
+
problemUploading: 'Impossible de téléverser le fichier sélectionné. Veuillez réessayer.',
|
|
30
|
+
problemDeleting: `Le fichier sélectionné n'a pas pu être supprimé. Réessayez.`,
|
|
31
|
+
problemUploadingMultipleFiles: `Les fichiers sélectionnés n'ont pas pu être téléchargés. Veuillez réessayer.`,
|
|
32
|
+
only: 'uniquement',
|
|
33
|
+
and: 'et',
|
|
34
|
+
or: 'ou',
|
|
35
|
+
uploadSuccess: 'téléchargé avec succès.',
|
|
36
|
+
uploadError: `n'a pas été téléchargé en raison de`,
|
|
37
|
+
deleteProblem: `n'a pas été supprimé en raison de`,
|
|
38
|
+
tooManyFiles: `Vous ne pouvez sélectionner que jusqu'à %{maxFilesCount} fichiers en même temps.`
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/Link/TextButton.jsx
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import { View, StyleSheet } from 'react-native'
|
|
4
3
|
import { useThemeTokensCallback } from '../ThemeProvider'
|
|
5
4
|
import LinkBase from './LinkBase'
|
|
6
5
|
import { variantProp } from '../utils'
|
|
@@ -14,25 +13,19 @@ const TextButton = React.forwardRef(
|
|
|
14
13
|
({ onPress, children, variant, tokens, accessibilityRole = 'button', ...linkProps }, ref) => {
|
|
15
14
|
const getTokens = useThemeTokensCallback('Link', tokens, variant)
|
|
16
15
|
return (
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
</LinkBase>
|
|
27
|
-
</View>
|
|
16
|
+
<LinkBase
|
|
17
|
+
onPress={onPress}
|
|
18
|
+
accessibilityRole={accessibilityRole}
|
|
19
|
+
tokens={getTokens}
|
|
20
|
+
ref={ref}
|
|
21
|
+
{...linkProps}
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
</LinkBase>
|
|
28
25
|
)
|
|
29
26
|
}
|
|
30
27
|
)
|
|
31
|
-
|
|
32
|
-
textButton: {
|
|
33
|
-
flex: 1
|
|
34
|
-
}
|
|
35
|
-
})
|
|
28
|
+
|
|
36
29
|
TextButton.displayName = 'TextButton'
|
|
37
30
|
|
|
38
31
|
TextButton.propTypes = {
|
|
@@ -133,9 +133,11 @@ const ModalContent = React.forwardRef(
|
|
|
133
133
|
</Button>
|
|
134
134
|
)}
|
|
135
135
|
{hasCancelButton ? (
|
|
136
|
-
<
|
|
137
|
-
{
|
|
138
|
-
|
|
136
|
+
<View style={styles.cancelButton}>
|
|
137
|
+
<CancelButton tokens={{ color: cancelButtonColor }} onPress={onCancel}>
|
|
138
|
+
{cancelButtonText}
|
|
139
|
+
</CancelButton>
|
|
140
|
+
</View>
|
|
139
141
|
) : null}
|
|
140
142
|
</View>
|
|
141
143
|
)}
|
|
@@ -151,6 +153,9 @@ const styles = StyleSheet.create({
|
|
|
151
153
|
flex: 1,
|
|
152
154
|
flexDirection: 'column',
|
|
153
155
|
minHeight: Platform.OS === 'web' ? '100%' : 'auto'
|
|
156
|
+
},
|
|
157
|
+
cancelButton: {
|
|
158
|
+
flex: 1
|
|
154
159
|
}
|
|
155
160
|
})
|
|
156
161
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import { View, StyleSheet } from 'react-native'
|
|
3
|
+
import { View, StyleSheet, Platform } from 'react-native'
|
|
4
4
|
import { Portal } from '@gorhom/portal'
|
|
5
5
|
import { useCopy, copyPropTypes, getTokensPropType, variantProp } from '../utils'
|
|
6
6
|
import { useViewport } from '../ViewportProvider'
|
|
@@ -13,8 +13,26 @@ import IconButton from '../IconButton'
|
|
|
13
13
|
const staticStyles = StyleSheet.create({
|
|
14
14
|
positioner: {
|
|
15
15
|
flex: 1, // Grow to maxWidth when possible, shrink when not possible
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
zIndex: 10000, // Position on top of all the other overlays, including backdrops and modals,
|
|
17
|
+
elevation: 10000, // Position on top of all the other overlays, including backdrops and modals,
|
|
18
|
+
borderRadius: 4,
|
|
19
|
+
...Platform.select({
|
|
20
|
+
web: {
|
|
21
|
+
boxShadow: 'rgba(0, 0, 0, 0.1) 0px 4px 8px 0px'
|
|
22
|
+
},
|
|
23
|
+
default: {
|
|
24
|
+
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
|
25
|
+
shadowOffset: { width: 0, height: 4 },
|
|
26
|
+
shadowOpacity: 1,
|
|
27
|
+
shadowRadius: 8
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
},
|
|
31
|
+
card: {
|
|
32
|
+
paddingLeft: 0,
|
|
33
|
+
paddingRight: 0,
|
|
34
|
+
paddingTop: 0,
|
|
35
|
+
paddingBottom: 0
|
|
18
36
|
},
|
|
19
37
|
closeButtonContainer: {
|
|
20
38
|
position: 'absolute',
|
|
@@ -29,18 +47,23 @@ const staticStyles = StyleSheet.create({
|
|
|
29
47
|
}
|
|
30
48
|
})
|
|
31
49
|
|
|
50
|
+
const selectContainerStyle = (enableFullscreen, themeTokens) => ({
|
|
51
|
+
borderColor: themeTokens.borderColor,
|
|
52
|
+
...Platform.select({
|
|
53
|
+
web: {
|
|
54
|
+
position: enableFullscreen ? 'fixed' : 'absolute'
|
|
55
|
+
},
|
|
56
|
+
default: {
|
|
57
|
+
position: 'absolute'
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
32
62
|
const selectCloseButtonContainerStyles = ({ paddingRight, paddingTop }) => ({
|
|
33
63
|
paddingRight,
|
|
34
64
|
paddingTop
|
|
35
65
|
})
|
|
36
66
|
|
|
37
|
-
const selectPaddingContainerStyles = ({ paddingTop, paddingLeft, paddingRight }) => ({
|
|
38
|
-
paddingBottom: 35,
|
|
39
|
-
paddingTop,
|
|
40
|
-
paddingLeft,
|
|
41
|
-
paddingRight
|
|
42
|
-
})
|
|
43
|
-
|
|
44
67
|
const ModalOverlay = React.forwardRef(
|
|
45
68
|
(
|
|
46
69
|
{
|
|
@@ -56,17 +79,18 @@ const ModalOverlay = React.forwardRef(
|
|
|
56
79
|
variant,
|
|
57
80
|
tokens,
|
|
58
81
|
copy,
|
|
59
|
-
onClose
|
|
82
|
+
onClose,
|
|
83
|
+
enableFullscreen = false
|
|
60
84
|
},
|
|
61
85
|
ref
|
|
62
86
|
) => {
|
|
63
87
|
const viewport = useViewport()
|
|
64
88
|
const themeTokens = useThemeTokens('Modal', tokens, variant, { viewport, maxWidth: false })
|
|
65
89
|
const containerWidthHeight = {
|
|
66
|
-
minWidth,
|
|
67
|
-
minHeight,
|
|
68
|
-
|
|
69
|
-
|
|
90
|
+
minWidth: tokens.maxWidth ? maxWidthSize : minWidth,
|
|
91
|
+
minHeight: maxHeight ? maxHeightSize : minHeight,
|
|
92
|
+
maxWidth: maxWidthSize,
|
|
93
|
+
maxHeight: maxHeightSize
|
|
70
94
|
}
|
|
71
95
|
|
|
72
96
|
const { closeIcon: CloseIconComponent } = themeTokens
|
|
@@ -83,10 +107,11 @@ const ModalOverlay = React.forwardRef(
|
|
|
83
107
|
overlaidPosition,
|
|
84
108
|
containerWidthHeight,
|
|
85
109
|
staticStyles.positioner,
|
|
86
|
-
!isReady && staticStyles.hidden
|
|
110
|
+
!isReady && staticStyles.hidden,
|
|
111
|
+
selectContainerStyle(enableFullscreen, themeTokens)
|
|
87
112
|
]}
|
|
88
113
|
>
|
|
89
|
-
<Card tokens={
|
|
114
|
+
<Card tokens={staticStyles.card}>
|
|
90
115
|
<View
|
|
91
116
|
style={[
|
|
92
117
|
staticStyles.closeButtonContainer,
|
|
@@ -127,7 +152,8 @@ ModalOverlay.propTypes = {
|
|
|
127
152
|
variant: variantProp.propType,
|
|
128
153
|
tokens: getTokensPropType('Modal'),
|
|
129
154
|
copy: copyPropTypes,
|
|
130
|
-
onClose: PropTypes.func
|
|
155
|
+
onClose: PropTypes.func,
|
|
156
|
+
enableFullscreen: PropTypes.bool
|
|
131
157
|
}
|
|
132
158
|
|
|
133
159
|
export default ModalOverlay
|