cozy-ui 135.5.0 → 135.6.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 (34) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/assets/icons/ui/expand.svg +1 -0
  3. package/assets/icons/ui/export.svg +1 -0
  4. package/assets/icons/ui/narrow.svg +1 -0
  5. package/package.json +1 -1
  6. package/react/ActionsMenu/Actions/exportToText.js +54 -0
  7. package/react/ActionsMenu/Actions/helpers.js +20 -0
  8. package/react/ActionsMenu/Actions/index.js +1 -0
  9. package/react/ActionsMenu/Actions/locales/en.json +2 -1
  10. package/react/ActionsMenu/Actions/locales/fr.json +2 -1
  11. package/react/ActionsMenu/Actions/locales/ru.json +3 -2
  12. package/react/ActionsMenu/Actions/locales/vi.json +3 -2
  13. package/react/ActionsMenu/Actions/pdfHelpers.js +187 -0
  14. package/react/Icon/Readme.md +7 -1
  15. package/react/Icons/Expand.jsx +12 -0
  16. package/react/Icons/Export.jsx +13 -0
  17. package/react/Icons/Narrow.jsx +16 -0
  18. package/transpiled/react/ActionsMenu/Actions/exportToText.d.ts +13 -0
  19. package/transpiled/react/ActionsMenu/Actions/exportToText.js +84 -0
  20. package/transpiled/react/ActionsMenu/Actions/helpers.d.ts +1 -0
  21. package/transpiled/react/ActionsMenu/Actions/helpers.js +20 -1
  22. package/transpiled/react/ActionsMenu/Actions/index.d.ts +1 -0
  23. package/transpiled/react/ActionsMenu/Actions/index.js +2 -1
  24. package/transpiled/react/ActionsMenu/Actions/locales/withActionsLocales.js +8 -4
  25. package/transpiled/react/ActionsMenu/Actions/pdfHelpers.d.ts +34 -0
  26. package/transpiled/react/ActionsMenu/Actions/pdfHelpers.js +242 -0
  27. package/transpiled/react/Icon/icons-sprite.d.ts +1 -1
  28. package/transpiled/react/Icon/icons-sprite.js +1 -1
  29. package/transpiled/react/Icons/Expand.d.ts +2 -0
  30. package/transpiled/react/Icons/Expand.js +14 -0
  31. package/transpiled/react/Icons/Export.d.ts +2 -0
  32. package/transpiled/react/Icons/Export.js +16 -0
  33. package/transpiled/react/Icons/Narrow.d.ts +2 -0
  34. package/transpiled/react/Icons/Narrow.js +16 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [135.6.0](https://github.com/cozy/cozy-ui/compare/v135.5.0...v135.6.0) (2026-01-27)
2
+
3
+
4
+ ### Features
5
+
6
+ * Implement new actions ExportToText and new icons :sparkles: ([6714f4a](https://github.com/cozy/cozy-ui/commit/6714f4a))
7
+
1
8
  # [135.5.0](https://github.com/cozy/cozy-ui/compare/v135.4.0...v135.5.0) (2026-01-24)
2
9
 
3
10
 
@@ -0,0 +1 @@
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M14.8 15.2a.8.8 0 0 0-.8-.8H2.8a.8.8 0 0 0 0 1.6H14a.8.8 0 0 0 .8-.8ZM14.8.8A.8.8 0 0 0 14 0H2.8a.8.8 0 1 0 0 1.6H14a.8.8 0 0 0 .8-.8ZM6.893 9.833a.799.799 0 0 0-1.128 1.132l1.928 1.928a1 1 0 0 0 1.414 0l1.928-1.928a.799.799 0 0 0-1.128-1.132l-.707.703V5.464l.707.703a.799.799 0 0 0 1.128-1.132L9.107 3.107a1 1 0 0 0-1.414 0L5.765 5.035a.799.799 0 0 0 1.128 1.132l.707-.703v5.072l-.707-.703Z" fill="#2C3038"/></svg>
@@ -0,0 +1 @@
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M14.8 4v10.4l-.008.159A1.6 1.6 0 0 1 13.2 16H3.6a1.6 1.6 0 0 1-1.014-.362l-.117-.107a1.6 1.6 0 0 1-.461-.972L2 14.4V1.6c0-.372.13-.73.362-1.014L2.47.469A1.6 1.6 0 0 1 3.599 0h7.2l4 4ZM3.6 14.4h9.6V5.6h-1.598a2.4 2.4 0 0 1-2.4-2.4V1.6H3.6v12.8Zm7.202-11.2a.8.8 0 0 0 .8.8h.935l-1.736-1.736V3.2Z" fill="#2C3038"/><path d="M8.401 7.2a.8.8 0 0 1 .8.799v2.867l1.034-1.032a.8.8 0 1 1 1.131 1.13l-2.4 2.401a.794.794 0 0 1-.436.222.817.817 0 0 1-.13.013.793.793 0 0 1-.127-.013.794.794 0 0 1-.438-.222l-2.4-2.4a.8.8 0 1 1 1.131-1.131l1.035 1.034V8a.8.8 0 0 1 .8-.8Z" fill="#2C3038"/></svg>
@@ -0,0 +1 @@
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M15.736.264a.9.9 0 0 1 0 1.274L12.07 5.204h2.027a.9.9 0 1 1 0 1.802H9.896a.9.9 0 0 1-.901-.9V1.901a.9.9 0 1 1 1.802 0V3.93L14.462.264a.9.9 0 0 1 1.274 0ZM1 9.895a.9.9 0 0 1 .902-.901h4.203a.9.9 0 0 1 .901.9v4.204a.9.9 0 0 1-1.802 0V12.07l-3.666 3.667a.9.9 0 0 1-1.274-1.274l3.667-3.666H1.9a.9.9 0 0 1-.9-.901Z" fill="#2C3038"/></svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-ui",
3
- "version": "135.5.0",
3
+ "version": "135.6.0",
4
4
  "description": "Cozy apps UI SDK",
5
5
  "main": "./index.js",
6
6
  "bin": {
@@ -0,0 +1,54 @@
1
+ import React, { forwardRef } from 'react'
2
+
3
+ import logger from 'cozy-logger'
4
+
5
+ import { downloadBlob } from './helpers'
6
+ import { getActionsI18n } from './locales/withActionsLocales'
7
+ import { makePdfBlobFromText } from './pdfHelpers'
8
+ import Icon from '../../Icon'
9
+ import ExportIcon from '../../Icons/Export'
10
+ import ListItemIcon from '../../ListItemIcon'
11
+ import ListItemText from '../../ListItemText'
12
+ import ActionsMenuItem from '../ActionsMenuItem'
13
+
14
+ const makeComponent = (label, icon) => {
15
+ const Component = forwardRef((props, ref) => {
16
+ return (
17
+ <ActionsMenuItem {...props} ref={ref}>
18
+ <ListItemIcon>
19
+ <Icon icon={icon} />
20
+ </ListItemIcon>
21
+ <ListItemText primary={label} />
22
+ </ActionsMenuItem>
23
+ )
24
+ })
25
+
26
+ Component.displayName = 'exportToText'
27
+
28
+ return Component
29
+ }
30
+
31
+ export const exportToText = ({ exportedText, file }) => {
32
+ const { t } = getActionsI18n()
33
+ const icon = ExportIcon
34
+ const label = t('exportToText')
35
+
36
+ return {
37
+ name: 'exportToText',
38
+ icon,
39
+ label,
40
+ displayCondition: () => !!exportedText,
41
+ Component: makeComponent(label, icon),
42
+ action: async () => {
43
+ try {
44
+ const blob = await makePdfBlobFromText(exportedText)
45
+ const baseName = file?.name
46
+ ? `${file.name.replace(/\.[^/.]+$/, '')}_export`
47
+ : 'export'
48
+ downloadBlob(blob, `${baseName}.pdf`)
49
+ } catch (error) {
50
+ logger.error(error)
51
+ }
52
+ }
53
+ }
54
+ }
@@ -238,3 +238,23 @@ export const makePdfBlob = async ({ client, docs, fetchBlobFileById }) => {
238
238
 
239
239
  return blob
240
240
  }
241
+
242
+ /**
243
+ * Trigger a browser download for a given blob.
244
+ *
245
+ * @param {Blob} blob - File content
246
+ * @param {string} filename - Desired filename
247
+ * @returns {void}
248
+ */
249
+ export const downloadBlob = (blob, filename) => {
250
+ if (!blob || !filename) return
251
+
252
+ const url = URL.createObjectURL(blob)
253
+ const a = document.createElement('a')
254
+ a.href = url
255
+ a.download = filename
256
+ a.style.display = 'none'
257
+ document.body.appendChild(a)
258
+ a.click()
259
+ a.remove()
260
+ }
@@ -13,3 +13,4 @@ export { viewInDrive } from './viewInDrive'
13
13
  export { copyToClipboard } from './copyToClipboard'
14
14
  export { editAttribute } from './editAttribute'
15
15
  export { others } from './others'
16
+ export { exportToText } from './exportToText'
@@ -25,5 +25,6 @@
25
25
  "success": "Copied to clipboard",
26
26
  "error": "Cannot copy to clipboard"
27
27
  },
28
- "call": "Call"
28
+ "call": "Call",
29
+ "exportToText": "Export as text"
29
30
  }
@@ -25,5 +25,6 @@
25
25
  "success": "Copié dans le presse-papier",
26
26
  "error": "Impossible de copier dans le presse-papier"
27
27
  },
28
- "call": "Appeler"
28
+ "call": "Appeler",
29
+ "exportToText": "Exporter en texte"
29
30
  }
@@ -25,5 +25,6 @@
25
25
  "success": "Скопировано в буфер обмена",
26
26
  "error": "Не удается скопировать в буфер обмена"
27
27
  },
28
- "call": "Звонить"
29
- }
28
+ "call": "Звонить",
29
+ "exportToText": "Экспортировать как текст"
30
+ }
@@ -25,5 +25,6 @@
25
25
  "success": "Đã sao chép vào clipboard",
26
26
  "error": "Không thể sao chép vào clipboard"
27
27
  },
28
- "call": "Gọi"
29
- }
28
+ "call": "Gọi",
29
+ "exportToText": "Xuất dưới dạng văn bản"
30
+ }
@@ -0,0 +1,187 @@
1
+ import { PDFDocument, StandardFonts } from 'pdf-lib'
2
+
3
+ /**
4
+ * Wrap a string into multiple lines based on available width.
5
+ *
6
+ * @param {object} opts
7
+ * @param {import('pdf-lib').PDFFont} opts.font - Embedded pdf-lib font
8
+ * @param {number} opts.fontSize - Font size
9
+ * @param {number} opts.maxLineWidth - Max line width in PDF units
10
+ * @param {string} opts.line - Text to wrap
11
+ * @returns {string[]} Wrapped lines
12
+ */
13
+ export const wrapTextToLines = ({ font, fontSize, maxLineWidth, line }) => {
14
+ if (!line) return []
15
+ if (fontSize <= 0 || maxLineWidth <= 0) return [line]
16
+
17
+ const words = line.split(' ')
18
+
19
+ return words.reduce((lines, word) => {
20
+ const currentLine = lines[lines.length - 1]
21
+
22
+ if (!currentLine) {
23
+ return [word]
24
+ }
25
+
26
+ const testLine = `${currentLine} ${word}`
27
+ const testWidth = font.widthOfTextAtSize(testLine, fontSize)
28
+
29
+ if (testWidth <= maxLineWidth) {
30
+ lines[lines.length - 1] = testLine
31
+ } else {
32
+ lines.push(word)
33
+ }
34
+
35
+ return lines
36
+ }, [])
37
+ }
38
+
39
+ /**
40
+ * Add a new page if there is not enough space to draw a new line.
41
+ *
42
+ * @param {object} opts
43
+ * @param {PDFDocument} opts.pdfDoc - pdf-lib document
44
+ * @param {import('pdf-lib').PDFPage} opts.page - Current page
45
+ * @param {number} opts.y - Current y cursor position
46
+ * @param {number} opts.requiredHeight - Height required before adding a new page
47
+ * @param {number} opts.marginTop - Top margin
48
+ * @param {number} opts.marginBottom - Bottom margin
49
+ * @returns {{ page: import('pdf-lib').PDFPage, y: number }} Updated page and y
50
+ */
51
+ export const pushNewPageIfNeeded = ({
52
+ pdfDoc,
53
+ page,
54
+ y,
55
+ requiredHeight,
56
+ marginTop,
57
+ marginBottom
58
+ }) => {
59
+ if (!pdfDoc || !page) {
60
+ throw new Error('pdfDoc and page are required')
61
+ }
62
+
63
+ const { width, height } = page.getSize()
64
+
65
+ if (y - requiredHeight < marginBottom) {
66
+ const newPage = pdfDoc.addPage([width, height])
67
+ return {
68
+ page: newPage,
69
+ y: height - marginTop
70
+ }
71
+ }
72
+
73
+ return { page, y }
74
+ }
75
+
76
+ /**
77
+ * Draws a plain text string onto a PDF, wrapping lines to max width and handling page breaks.
78
+ *
79
+ * @param {object} opts
80
+ * @param {PDFDocument} opts.pdfDoc - pdf-lib document
81
+ * @param {import('pdf-lib').PDFPage} opts.page - Current page
82
+ * @param {number} opts.y - Current y cursor position
83
+ * @param {string} opts.text - Text to draw
84
+ * @param {import('pdf-lib').PDFFont} opts.font - Embedded pdf-lib font
85
+ * @param {number} opts.fontSize - Font size
86
+ * @param {number} opts.lineHeight - Line height
87
+ * @param {number} opts.marginX - Left margin
88
+ * @param {number} opts.marginY - Top/bottom margin
89
+ * @param {number} opts.maxLineWidth - Max line width in PDF units
90
+ * @returns {{ page: import('pdf-lib').PDFPage, y: number }} Updated page and y
91
+ */
92
+ export const drawWrappedText = ({
93
+ pdfDoc,
94
+ page,
95
+ y,
96
+ text,
97
+ font,
98
+ fontSize,
99
+ lineHeight,
100
+ marginX,
101
+ marginY,
102
+ maxLineWidth
103
+ }) => {
104
+ let currentPage = page
105
+ let currentY = y
106
+
107
+ const paragraphs = String(text ?? '').split(/\r?\n/)
108
+
109
+ const renderItems = paragraphs.flatMap(paragraph => {
110
+ const wrappedLines = wrapTextToLines({
111
+ font,
112
+ fontSize,
113
+ maxLineWidth,
114
+ line: paragraph
115
+ })
116
+
117
+ return [...wrappedLines, { type: 'paragraphGap' }]
118
+ })
119
+
120
+ for (const item of renderItems) {
121
+ if (typeof item === 'object' && item.type === 'paragraphGap') {
122
+ currentY -= lineHeight / 2
123
+ continue
124
+ }
125
+
126
+ const nextState = pushNewPageIfNeeded({
127
+ pdfDoc,
128
+ page: currentPage,
129
+ y: currentY,
130
+ requiredHeight: lineHeight,
131
+ marginTop: marginY,
132
+ marginBottom: marginY
133
+ })
134
+
135
+ currentPage = nextState.page
136
+ currentY = nextState.y
137
+
138
+ currentPage.drawText(item, {
139
+ x: marginX,
140
+ y: currentY - fontSize,
141
+ size: fontSize,
142
+ font
143
+ })
144
+
145
+ currentY -= lineHeight
146
+ }
147
+
148
+ return { page: currentPage, y: currentY }
149
+ }
150
+
151
+ /**
152
+ * Creates a PDF Blob from a plain text string.
153
+ *
154
+ * @param {string} text - Text content to put into the PDF
155
+ * @returns {Promise<Blob>} PDF file as a Blob
156
+ */
157
+ export const makePdfBlobFromText = async text => {
158
+ const pdfDoc = await PDFDocument.create()
159
+ const font = await pdfDoc.embedFont(StandardFonts.Helvetica)
160
+
161
+ const CONFIG = {
162
+ fontSize: 12,
163
+ lineHeight: 16,
164
+ marginX: 40,
165
+ marginY: 40,
166
+ pageSize: [595.28, 841.89] // A4
167
+ }
168
+
169
+ const maxLineWidth = CONFIG.pageSize[0] - CONFIG.marginX * 2
170
+ const initialPage = pdfDoc.addPage(CONFIG.pageSize)
171
+
172
+ drawWrappedText({
173
+ pdfDoc,
174
+ page: initialPage,
175
+ y: CONFIG.pageSize[1] - CONFIG.marginY,
176
+ text,
177
+ font,
178
+ fontSize: CONFIG.fontSize,
179
+ lineHeight: CONFIG.lineHeight,
180
+ marginX: CONFIG.marginX,
181
+ marginY: CONFIG.marginY,
182
+ maxLineWidth
183
+ })
184
+
185
+ const pdfBytes = await pdfDoc.save()
186
+ return new Blob([pdfBytes], { type: 'application/pdf' })
187
+ }
@@ -170,7 +170,9 @@ import EmailOpen from 'cozy-ui/transpiled/react/Icons/EmailOpen'
170
170
  import Eu from 'cozy-ui/transpiled/react/Icons/Eu'
171
171
  import Euro from 'cozy-ui/transpiled/react/Icons/Euro'
172
172
  import Exchange from 'cozy-ui/transpiled/react/Icons/Exchange'
173
+ import Expand from 'cozy-ui/transpiled/react/Icons/Expand'
173
174
  import Expert from 'cozy-ui/transpiled/react/Icons/Expert'
175
+ import Export from 'cozy-ui/transpiled/react/Icons/Export'
174
176
  import Eye from 'cozy-ui/transpiled/react/Icons/Eye'
175
177
  import EyeClosed from 'cozy-ui/transpiled/react/Icons/EyeClosed'
176
178
  import FaceId from 'cozy-ui/transpiled/react/Icons/FaceId'
@@ -245,6 +247,7 @@ import MovementOut from 'cozy-ui/transpiled/react/Icons/MovementOut'
245
247
  import Moveto from 'cozy-ui/transpiled/react/Icons/Moveto'
246
248
  import MultiFiles from 'cozy-ui/transpiled/react/Icons/MultiFiles'
247
249
  import Music from 'cozy-ui/transpiled/react/Icons/Music'
250
+ import Narrow from 'cozy-ui/transpiled/react/Icons/Narrow'
248
251
  import New from 'cozy-ui/transpiled/react/Icons/New'
249
252
  import Next from 'cozy-ui/transpiled/react/Icons/Next'
250
253
  import Note from 'cozy-ui/transpiled/react/Icons/Note'
@@ -461,7 +464,9 @@ const icons = [
461
464
  Eu,
462
465
  Euro,
463
466
  Exchange,
467
+ Expand,
464
468
  Expert,
469
+ Export,
465
470
  Eye,
466
471
  EyeClosed,
467
472
  FaceId,
@@ -536,6 +541,7 @@ const icons = [
536
541
  Moveto,
537
542
  MultiFiles,
538
543
  Music,
544
+ Narrow,
539
545
  New,
540
546
  Next,
541
547
  Note,
@@ -1014,7 +1020,7 @@ import Typography from 'cozy-ui/transpiled/react/Typography'
1014
1020
 
1015
1021
  const colors = ['#297EF2', '#08b442', '#B449E7', '#F52D2D', '#FF962F']
1016
1022
  let i = 0
1017
- const availableIcons = ['album-add','album-remove','album','answer','apple','archive','arrowUp', 'article', 'assign-admin','assign-moderator','attachment','attention','bank-check','bank','banking-add','banking','bell','benefit','bike','bill','bottom','browser-brave','browser-chrome','browser-duckduckgo','browser-edge','browser-edge-chromium','browser-firefox','browser-ie','browser-opera','browser-safari','burger','bus','calendar','camera','car','carbonCopy','carpooling','categories','certified','check-circle','check-list','check-square','check','checkbox','chess','child','circle-filled','clock','clock-outline','cloud-happy','cloud-rainbow','cloud-plus-outlined','cloud','cloud2','collect','cocktail','comment','company','compare','compass','connector','contract','contrast','copy','cozy-circle','cozy-laugh', 'cozy-lock', 'cozy-text', 'cozy-release', 'credit-card-add','credit-card','credit','crop','cross-circle-outline','cross-circle','cross-medium','cross-small','cross','cube','dash','dashboard','data-control','database','debit','desktop-download','devices','discuss','dots','down','download','drawing-arrow-up','dropdown-close','dropdown-open','dropdown','dropup','electric-bike','electric-car','electric-scooter','email-notification','email-open','email','eu','euro','exchange','expert','eye-closed','eye','face-id','file-add','file-duotone','file-new','file-none','file-outline','file','filter','fingerprint','fitness','flag-outlined','flag','flash-auto','flashlight','folder-add','folder-moveto','folder-open','folder','forbidden','from-user','gear','globe','gouv','graph-circle','grid','group-list','groups','growth','hand','heart','help','help-outlined','history','home','hourglass','image','info-outlined','info','justice','key','key2','label-outlined','laudry','laptop','left','library','lightbulb','lightning','link-out','link','list','list-min','location','lock', 'lock-screen', 'logout','magic-trick','magnet','magnifier','matrix','merge','moped','mosaic','mosaic-min','motorcycle','mountain','movement-in','movement-out','mouvement','moveto','multi-files','music','new','next','note','notification-email','number','offline','online', 'openapp', 'openwith','palette','paper','paperplane','password','pen','people','peoples','percent-circle','percent','person-add','personal-data','phone-download','phone-upload','phone','pie-chart','pin','plane','planet','plus-small','plus', 'pop-inside', 'previous','printer','qualify','radio-checked','radio-unchecked','refresh','relationship','remboursement','rename','repare','reply','restaurant','restore-straight','restore','right','rise','rotate-left','rotate-right','sad-cozy','safe','school','scooter','secutiry','select-all','send','server','setting','share-circle','share','shield','shield-infected','shield-clean','shop','sound','spinner','sport-bag','stack','star','star-outline','stats','stop', 'subway', 'support', 'swap', 'sync-cozy','sync','tab','tag','target','task','team','telecom','telephone','text','text-info','to-the-cloud','top','train','tram','trash','trophy', 'uncloud', 'unknow','unlink','unlock','up','upload','videos','walk','wallet-add','wallet-new','wallet','warn','warning-circle','warning','water','wrench-circle','work']
1023
+ const availableIcons = ['album-add','album-remove','album','answer','apple','archive','arrowUp', 'article', 'assign-admin','assign-moderator','attachment','attention','bank-check','bank','banking-add','banking','bell','benefit','bike','bill','bottom','browser-brave','browser-chrome','browser-duckduckgo','browser-edge','browser-edge-chromium','browser-firefox','browser-ie','browser-opera','browser-safari','burger','bus','calendar','camera','car','carbonCopy','carpooling','categories','certified','check-circle','check-list','check-square','check','checkbox','chess','child','circle-filled','clock','clock-outline','cloud-happy','cloud-rainbow','cloud-plus-outlined','cloud','cloud2','collect','cocktail','comment','company','compare','compass','connector','contract','contrast','copy','cozy-circle','cozy-laugh', 'cozy-lock', 'cozy-text', 'cozy-release', 'credit-card-add','credit-card','credit','crop','cross-circle-outline','cross-circle','cross-medium','cross-small','cross','cube','dash','dashboard','data-control','database','debit','desktop-download','devices','discuss','dots','down','download','drawing-arrow-up','dropdown-close','dropdown-open','dropdown','dropup','electric-bike','electric-car','electric-scooter','email-notification','email-open','email','eu','euro','exchange','expand','expert','export','eye-closed','eye','face-id','file-add','file-duotone','file-new','file-none','file-outline','file','filter','fingerprint','fitness','flag-outlined','flag','flash-auto','flashlight','folder-add','folder-moveto','folder-open','folder','forbidden','from-user','gear','globe','gouv','graph-circle','grid','group-list','groups','growth','hand','heart','help','help-outlined','history','home','hourglass','image','info-outlined','info','justice','key','key2','label-outlined','laudry','laptop','left','library','lightbulb','lightning','link-out','link','list','list-min','location','lock', 'lock-screen', 'logout','magic-trick','magnet','magnifier','matrix','merge','moped','mosaic','mosaic-min','motorcycle','mountain','movement-in','movement-out','mouvement','moveto','multi-files','music','narrow','new','next','note','notification-email','number','offline','online', 'openapp', 'openwith','palette','paper','paperplane','password','pen','people','peoples','percent-circle','percent','person-add','personal-data','phone-download','phone-upload','phone','pie-chart','pin','plane','planet','plus-small','plus', 'pop-inside', 'previous','printer','qualify','radio-checked','radio-unchecked','refresh','relationship','remboursement','rename','repare','reply','restaurant','restore-straight','restore','right','rise','rotate-left','rotate-right','sad-cozy','safe','school','scooter','secutiry','select-all','send','server','setting','share-circle','share','shield','shield-infected','shield-clean','shop','sound','spinner','sport-bag','stack','star','star-outline','stats','stop', 'subway', 'support', 'swap', 'sync-cozy','sync','tab','tag','target','task','team','telecom','telephone','text','text-info','to-the-cloud','top','train','tram','trash','trophy', 'uncloud', 'unknow','unlink','unlock','up','upload','videos','walk','wallet-add','wallet-new','wallet','warn','warning-circle','warning','water','wrench-circle','work']
1018
1024
  ;
1019
1025
 
1020
1026
  <Grid container spacing={2}>
@@ -0,0 +1,12 @@
1
+ // Automatically created, please run `scripts/generate-svgr-icon.sh assets/icons/ui/expand.svg` to regenerate;
2
+ import React from 'react'
3
+
4
+ function SvgExpand(props) {
5
+ return (
6
+ <svg viewBox="0 0 16 16" fill="none" {...props}>
7
+ <path d="M14.8 15.2a.8.8 0 00-.8-.8H2.8a.8.8 0 000 1.6H14a.8.8 0 00.8-.8zm0-14.4A.8.8 0 0014 0H2.8a.8.8 0 100 1.6H14a.8.8 0 00.8-.8zM6.893 9.833a.799.799 0 00-1.128 1.132l1.928 1.928a1 1 0 001.414 0l1.928-1.928a.799.799 0 00-1.128-1.132l-.707.703V5.464l.707.703a.799.799 0 001.128-1.132L9.107 3.107a1 1 0 00-1.414 0L5.765 5.035a.799.799 0 001.128 1.132l.707-.703v5.072l-.707-.703z" />
8
+ </svg>
9
+ )
10
+ }
11
+
12
+ export default SvgExpand
@@ -0,0 +1,13 @@
1
+ // Automatically created, please run `scripts/generate-svgr-icon.sh assets/icons/ui/export.svg` to regenerate;
2
+ import React from 'react'
3
+
4
+ function SvgExport(props) {
5
+ return (
6
+ <svg viewBox="0 0 16 16" fill="none" {...props}>
7
+ <path d="M14.8 4v10.4l-.008.159A1.6 1.6 0 0113.2 16H3.6a1.6 1.6 0 01-1.014-.362l-.117-.107a1.6 1.6 0 01-.461-.972L2 14.4V1.6c0-.372.13-.73.362-1.014L2.47.469A1.6 1.6 0 013.599 0h7.2l4 4zM3.6 14.4h9.6V5.6h-1.598a2.4 2.4 0 01-2.4-2.4V1.6H3.6v12.8zm7.202-11.2a.8.8 0 00.8.8h.935l-1.736-1.736V3.2z" />
8
+ <path d="M8.401 7.2a.8.8 0 01.8.799v2.867l1.034-1.032a.8.8 0 111.131 1.13l-2.4 2.401a.794.794 0 01-.436.222.817.817 0 01-.13.013.793.793 0 01-.127-.013.794.794 0 01-.438-.222l-2.4-2.4a.8.8 0 111.131-1.131l1.035 1.034V8a.8.8 0 01.8-.8z" />
9
+ </svg>
10
+ )
11
+ }
12
+
13
+ export default SvgExport
@@ -0,0 +1,16 @@
1
+ // Automatically created, please run `scripts/generate-svgr-icon.sh assets/icons/ui/narrow.svg` to regenerate;
2
+ import React from 'react'
3
+
4
+ function SvgNarrow(props) {
5
+ return (
6
+ <svg viewBox="0 0 16 16" fill="none" {...props}>
7
+ <path
8
+ fillRule="evenodd"
9
+ clipRule="evenodd"
10
+ d="M15.736.264a.9.9 0 010 1.274L12.07 5.204h2.027a.9.9 0 110 1.802H9.896a.9.9 0 01-.901-.9V1.901a.9.9 0 111.802 0V3.93L14.462.264a.9.9 0 011.274 0zM1 9.895a.9.9 0 01.902-.901h4.203a.9.9 0 01.901.9v4.204a.9.9 0 01-1.802 0V12.07l-3.666 3.667a.9.9 0 01-1.274-1.274l3.667-3.666H1.9a.9.9 0 01-.9-.901z"
11
+ />
12
+ </svg>
13
+ )
14
+ }
15
+
16
+ export default SvgNarrow
@@ -0,0 +1,13 @@
1
+ export function exportToText({ exportedText, file }: {
2
+ exportedText: any;
3
+ file: any;
4
+ }): {
5
+ name: string;
6
+ icon: typeof ExportIcon;
7
+ label: any;
8
+ displayCondition: () => boolean;
9
+ Component: React.ForwardRefExoticComponent<React.RefAttributes<any>>;
10
+ action: () => Promise<void>;
11
+ };
12
+ import ExportIcon from "../../Icons/Export";
13
+ import React from "react";
@@ -0,0 +1,84 @@
1
+ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
+ import _extends from "@babel/runtime/helpers/extends";
3
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
4
+ import React, { forwardRef } from 'react';
5
+ import logger from 'cozy-logger';
6
+ import { downloadBlob } from "cozy-ui/transpiled/react/ActionsMenu/Actions/helpers";
7
+ import { getActionsI18n } from "cozy-ui/transpiled/react/ActionsMenu/Actions/locales/withActionsLocales";
8
+ import { makePdfBlobFromText } from "cozy-ui/transpiled/react/ActionsMenu/Actions/pdfHelpers";
9
+ import Icon from "cozy-ui/transpiled/react/Icon";
10
+ import ExportIcon from "cozy-ui/transpiled/react/Icons/Export";
11
+ import ListItemIcon from "cozy-ui/transpiled/react/ListItemIcon";
12
+ import ListItemText from "cozy-ui/transpiled/react/ListItemText";
13
+ import ActionsMenuItem from "cozy-ui/transpiled/react/ActionsMenu/ActionsMenuItem";
14
+
15
+ var makeComponent = function makeComponent(label, icon) {
16
+ var Component = /*#__PURE__*/forwardRef(function (props, ref) {
17
+ return /*#__PURE__*/React.createElement(ActionsMenuItem, _extends({}, props, {
18
+ ref: ref
19
+ }), /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(Icon, {
20
+ icon: icon
21
+ })), /*#__PURE__*/React.createElement(ListItemText, {
22
+ primary: label
23
+ }));
24
+ });
25
+ Component.displayName = 'exportToText';
26
+ return Component;
27
+ };
28
+
29
+ export var exportToText = function exportToText(_ref) {
30
+ var exportedText = _ref.exportedText,
31
+ file = _ref.file;
32
+
33
+ var _getActionsI18n = getActionsI18n(),
34
+ t = _getActionsI18n.t;
35
+
36
+ var icon = ExportIcon;
37
+ var label = t('exportToText');
38
+ return {
39
+ name: 'exportToText',
40
+ icon: icon,
41
+ label: label,
42
+ displayCondition: function displayCondition() {
43
+ return !!exportedText;
44
+ },
45
+ Component: makeComponent(label, icon),
46
+ action: function () {
47
+ var _action = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
48
+ var blob, baseName;
49
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
50
+ while (1) {
51
+ switch (_context.prev = _context.next) {
52
+ case 0:
53
+ _context.prev = 0;
54
+ _context.next = 3;
55
+ return makePdfBlobFromText(exportedText);
56
+
57
+ case 3:
58
+ blob = _context.sent;
59
+ baseName = file !== null && file !== void 0 && file.name ? "".concat(file.name.replace(/\.[^/.]+$/, ''), "_export") : 'export';
60
+ downloadBlob(blob, "".concat(baseName, ".pdf"));
61
+ _context.next = 11;
62
+ break;
63
+
64
+ case 8:
65
+ _context.prev = 8;
66
+ _context.t0 = _context["catch"](0);
67
+ logger.error(_context.t0);
68
+
69
+ case 11:
70
+ case "end":
71
+ return _context.stop();
72
+ }
73
+ }
74
+ }, _callee, null, [[0, 8]]);
75
+ }));
76
+
77
+ function action() {
78
+ return _action.apply(this, arguments);
79
+ }
80
+
81
+ return action;
82
+ }()
83
+ };
84
+ };
@@ -5,4 +5,5 @@ export function makeBase64FromFile(file: File): Promise<string | null>;
5
5
  export function fileToArrayBuffer(file: File): Promise<ArrayBuffer>;
6
6
  export function addFileToPdf(pdfDoc: PDFDocument, file: File): Promise<ArrayBuffer>;
7
7
  export function makePdfBlob({ client, docs, fetchBlobFileById }: object): Promise<object>;
8
+ export function downloadBlob(blob: Blob, filename: string): void;
8
9
  import { PDFDocument } from "pdf-lib/cjs/api";
@@ -475,4 +475,23 @@ export var makePdfBlob = /*#__PURE__*/function () {
475
475
  return function makePdfBlob(_x11) {
476
476
  return _ref11.apply(this, arguments);
477
477
  };
478
- }();
478
+ }();
479
+ /**
480
+ * Trigger a browser download for a given blob.
481
+ *
482
+ * @param {Blob} blob - File content
483
+ * @param {string} filename - Desired filename
484
+ * @returns {void}
485
+ */
486
+
487
+ export var downloadBlob = function downloadBlob(blob, filename) {
488
+ if (!blob || !filename) return;
489
+ var url = URL.createObjectURL(blob);
490
+ var a = document.createElement('a');
491
+ a.href = url;
492
+ a.download = filename;
493
+ a.style.display = 'none';
494
+ document.body.appendChild(a);
495
+ a.click();
496
+ a.remove();
497
+ };
@@ -13,3 +13,4 @@ export { viewInDrive } from "./viewInDrive";
13
13
  export { copyToClipboard } from "./copyToClipboard";
14
14
  export { editAttribute } from "./editAttribute";
15
15
  export { others } from "./others";
16
+ export { exportToText } from "./exportToText";
@@ -12,4 +12,5 @@ export { viewInContacts } from './viewInContacts';
12
12
  export { viewInDrive } from './viewInDrive';
13
13
  export { copyToClipboard } from './copyToClipboard';
14
14
  export { editAttribute } from './editAttribute';
15
- export { others } from './others';
15
+ export { others } from './others';
16
+ export { exportToText } from './exportToText';
@@ -26,7 +26,8 @@ var en = {
26
26
  success: "Copied to clipboard",
27
27
  error: "Cannot copy to clipboard"
28
28
  },
29
- call: "Call"
29
+ call: "Call",
30
+ exportToText: "Export as text"
30
31
  };
31
32
  var fr = {
32
33
  menu: "Menu",
@@ -55,7 +56,8 @@ var fr = {
55
56
  success: "Copi\xE9 dans le presse-papier",
56
57
  error: "Impossible de copier dans le presse-papier"
57
58
  },
58
- call: "Appeler"
59
+ call: "Appeler",
60
+ exportToText: "Exporter en texte"
59
61
  };
60
62
  var ru = {
61
63
  menu: "\u041C\u0435\u043D\u044E",
@@ -84,7 +86,8 @@ var ru = {
84
86
  success: "\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u043D\u043E \u0432 \u0431\u0443\u0444\u0435\u0440 \u043E\u0431\u043C\u0435\u043D\u0430",
85
87
  error: "\u041D\u0435 \u0443\u0434\u0430\u0435\u0442\u0441\u044F \u0441\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0432 \u0431\u0443\u0444\u0435\u0440 \u043E\u0431\u043C\u0435\u043D\u0430"
86
88
  },
87
- call: "\u0417\u0432\u043E\u043D\u0438\u0442\u044C"
89
+ call: "\u0417\u0432\u043E\u043D\u0438\u0442\u044C",
90
+ exportToText: "\u042D\u043A\u0441\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043A\u0430\u043A \u0442\u0435\u043A\u0441\u0442"
88
91
  };
89
92
  var vi = {
90
93
  menu: "Menu",
@@ -113,7 +116,8 @@ var vi = {
113
116
  success: "\u0110\xE3 sao ch\xE9p v\xE0o clipboard",
114
117
  error: "Kh\xF4ng th\u1EC3 sao ch\xE9p v\xE0o clipboard"
115
118
  },
116
- call: "G\u1ECDi"
119
+ call: "G\u1ECDi",
120
+ exportToText: "Xu\u1EA5t d\u01B0\u1EDBi d\u1EA1ng v\u0103n b\u1EA3n"
117
121
  };
118
122
  export var locales = {
119
123
  en: en,