cozy-ui 102.0.0 → 102.1.1

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 CHANGED
@@ -1,3 +1,18 @@
1
+ ## [102.1.1](https://github.com/cozy/cozy-ui/compare/v102.1.0...v102.1.1) (2024-01-29)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **BottomSheet:** Default offset value wasn't good if flagship immersive [secure] ([b1551d2](https://github.com/cozy/cozy-ui/commit/b1551d2))
7
+
8
+ # [102.1.0](https://github.com/cozy/cozy-ui/compare/v102.0.0...v102.1.0) (2024-01-25)
9
+
10
+
11
+ ### Features
12
+
13
+ * **Action:** Print can now deal with multiple docs ([ee5695d](https://github.com/cozy/cozy-ui/commit/ee5695d))
14
+ * Add pdf-lib package ([9b363e1](https://github.com/cozy/cozy-ui/commit/9b363e1))
15
+
1
16
  # [102.0.0](https://github.com/cozy/cozy-ui/compare/v101.2.0...v102.0.0) (2024-01-23)
2
17
 
3
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-ui",
3
- "version": "102.0.0",
3
+ "version": "102.1.1",
4
4
  "description": "Cozy apps UI SDK",
5
5
  "main": "./index.js",
6
6
  "bin": {
@@ -171,6 +171,7 @@
171
171
  "mui-bottom-sheet": "https://github.com/cozy/mui-bottom-sheet.git#v1.0.9",
172
172
  "node-polyglot": "^2.2.2",
173
173
  "normalize.css": "^8.0.0",
174
+ "pdf-lib": "1.17.1",
174
175
  "piwik-react-router": "0.12.1",
175
176
  "react-chartjs-2": "4.1.0",
176
177
  "react-markdown": "^4.0.8",
@@ -1,3 +1,9 @@
1
+ import { PDFDocument } from 'pdf-lib'
2
+ import { fetchBlobFileById } from 'cozy-client/dist/models/file'
3
+
4
+ // Should guarantee good resolution for different uses (printing, downloading, etc.)
5
+ const MAX_RESIZE_IMAGE_SIZE = 3840
6
+
1
7
  /**
2
8
  * Make array of actions for ActionsItems component
3
9
  *
@@ -82,3 +88,159 @@ export const makeBase64FromFile = async file => {
82
88
  }
83
89
  })
84
90
  }
91
+
92
+ /**
93
+ * @param {HTMLImageElement} image
94
+ * @param {number} [maxSizeInPixel] - Maximum size before being resized
95
+ * @returns {number}
96
+ */
97
+ const getImageScaleRatio = (image, maxSize) => {
98
+ const longerSideSizeInPixel = Math.max(image.height, image.width)
99
+ let scaleRatio = 1
100
+ if (maxSize < longerSideSizeInPixel) {
101
+ scaleRatio = maxSize / longerSideSizeInPixel
102
+ }
103
+
104
+ return scaleRatio
105
+ }
106
+
107
+ /**
108
+ * @param {object} opts
109
+ * @param {string} opts.base64 - Base64 of image
110
+ * @param {string} opts.type - Type of image
111
+ * @param {number} opts.maxSize - Maximum size before being resized
112
+ * @returns {Promise<string>}
113
+ */
114
+ const resizeImage = async ({
115
+ base64: fileDataUri,
116
+ type: fileType,
117
+ maxSize
118
+ }) => {
119
+ return new Promise((resolve, reject) => {
120
+ const newImage = new Image()
121
+ newImage.src = fileDataUri
122
+ newImage.onerror = reject
123
+ newImage.onload = () => {
124
+ const canvas = document.createElement('canvas')
125
+ const scaleRatio = getImageScaleRatio(newImage, maxSize)
126
+ const scaledWidth = scaleRatio * newImage.width
127
+ const scaledHeight = scaleRatio * newImage.height
128
+
129
+ canvas.width = scaledWidth
130
+ canvas.height = scaledHeight
131
+ canvas
132
+ .getContext('2d')
133
+ .drawImage(newImage, 0, 0, scaledWidth, scaledHeight)
134
+
135
+ resolve(canvas.toDataURL(fileType))
136
+ }
137
+ })
138
+ }
139
+
140
+ /**
141
+ * @param {File} file
142
+ * @returns {Promise<string>}
143
+ */
144
+ const fileToDataUri = async file => {
145
+ return new Promise((resolve, reject) => {
146
+ let reader = new FileReader()
147
+ reader.onerror = reject
148
+ reader.onload = e => resolve(e.target.result)
149
+ reader.readAsDataURL(file)
150
+ })
151
+ }
152
+
153
+ /**
154
+ * @param {PDFDocument} pdfDoc
155
+ * @param {File} file
156
+ * @returns {Promise<void>}
157
+ */
158
+ const addImageToPdf = async (pdfDoc, file) => {
159
+ const fileDataUri = await fileToDataUri(file)
160
+ const resizedImage = await resizeImage({
161
+ base64: fileDataUri,
162
+ type: file.type,
163
+ maxSize: MAX_RESIZE_IMAGE_SIZE
164
+ })
165
+
166
+ let img
167
+ if (file.type === 'image/png') img = await pdfDoc.embedPng(resizedImage)
168
+ if (file.type === 'image/jpeg') img = await pdfDoc.embedJpg(resizedImage)
169
+
170
+ const page = pdfDoc.addPage([img.width, img.height])
171
+ const { width: pageWidth, height: pageHeight } = page.getSize()
172
+ page.drawImage(img, {
173
+ x: pageWidth / 2 - img.width / 2,
174
+ y: pageHeight / 2 - img.height / 2,
175
+ width: img.width,
176
+ height: img.height
177
+ })
178
+ }
179
+
180
+ /**
181
+ * @param {File} file
182
+ * @returns {Promise<ArrayBuffer>}
183
+ */
184
+ const fileToArrayBuffer = async file => {
185
+ if ('arrayBuffer' in file) return await file.arrayBuffer()
186
+
187
+ return new Promise((resolve, reject) => {
188
+ let reader = new FileReader()
189
+ reader.onerror = reject
190
+ reader.onload = e => resolve(new Uint8Array(e.target.result))
191
+ reader.readAsArrayBuffer(file)
192
+ })
193
+ }
194
+
195
+ /**
196
+ * @param {PDFDocument} pdfDoc
197
+ * @param {File} file
198
+ * @returns {Promise<void>}
199
+ */
200
+ const addPdfToPdf = async (pdfDoc, file) => {
201
+ const pdfToAdd = await fileToArrayBuffer(file)
202
+ const document = await PDFDocument.load(pdfToAdd)
203
+ const copiedPages = await pdfDoc.copyPages(
204
+ document,
205
+ document.getPageIndices()
206
+ )
207
+ copiedPages.forEach(page => pdfDoc.addPage(page))
208
+ }
209
+
210
+ /**
211
+ * @param {PDFDocument} pdfDoc - Instance of PDFDocument
212
+ * @param {File} file - File to add in pdf
213
+ * @returns {Promise<ArrayBuffer>} - Data of pdf generated
214
+ */
215
+ export const addFileToPdf = async (pdfDoc, file) => {
216
+ if (file.type === 'application/pdf') {
217
+ await addPdfToPdf(pdfDoc, file)
218
+ } else {
219
+ await addImageToPdf(pdfDoc, file)
220
+ }
221
+ const pdfDocBytes = await pdfDoc.save()
222
+
223
+ return pdfDocBytes
224
+ }
225
+
226
+ /**
227
+ * Fetches file from docs list and return a blob of pdf
228
+ * @param {import('cozy-client/types/CozyClient').default} client - Instance of CozyClient
229
+ * @param {array} docs - Docs from an io.cozy.xxx doctypes
230
+ * @returns {Promise<object>} Blob of generated Pdf
231
+ */
232
+ export const makePdfBlob = async (client, docs) => {
233
+ const pdfDoc = await PDFDocument.create()
234
+
235
+ for (const doc of docs) {
236
+ const blob = await fetchBlobFileById(client, doc._id)
237
+ await addFileToPdf(pdfDoc, blob)
238
+ }
239
+
240
+ const pdfBytes = await pdfDoc.save()
241
+
242
+ const bytes = new Uint8Array(pdfBytes)
243
+ const blob = new Blob([bytes], { type: 'application/pdf' })
244
+
245
+ return blob
246
+ }
@@ -3,7 +3,7 @@ import React, { forwardRef } from 'react'
3
3
  import logger from 'cozy-logger'
4
4
  import { fetchBlobFileById, isFile } from 'cozy-client/dist/models/file'
5
5
 
6
- import { makeBase64FromFile } from './helpers'
6
+ import { makeBase64FromFile, makePdfBlob } from './helpers'
7
7
  import PrinterIcon from '../../Icons/Printer'
8
8
  import { getActionsI18n } from './locales/withActionsLocales'
9
9
  import ActionsMenuItem from '../ActionsMenuItem'
@@ -21,33 +21,40 @@ export const print = () => {
21
21
  icon,
22
22
  label,
23
23
  disabled: docs => docs.length === 0,
24
- displayCondition: docs => isFile(docs[0]), // feature not yet supported for multi-files
24
+ displayCondition: docs => docs.every(doc => isFile(doc)),
25
25
  action: async (docs, { client, webviewIntent }) => {
26
- const doc = docs[0] // feature not yet supported for multi-files
26
+ const isSingleDoc = docs.length === 1
27
+ const firstDoc = docs[0]
27
28
 
28
- if (webviewIntent) {
29
- try {
30
- const blob = await fetchBlobFileById(client, doc._id)
29
+ try {
30
+ // in flagship app
31
+ if (webviewIntent) {
32
+ const blob = isSingleDoc
33
+ ? await fetchBlobFileById(client, firstDoc._id)
34
+ : await makePdfBlob(client, docs)
31
35
  const base64 = await makeBase64FromFile(blob)
32
36
 
33
37
  return webviewIntent.call('print', base64)
34
- } catch (error) {
35
- logger.error(
36
- `Error trying to print document with Flagship App: ${JSON.stringify(
37
- error
38
- )}`
39
- )
40
38
  }
41
- }
42
39
 
43
- try {
44
- const downloadURL = await client
45
- .collection('io.cozy.files')
46
- .getDownloadLinkById(doc._id, doc.name)
40
+ // not in flagship app
41
+ let docUrl = ''
42
+ if (isSingleDoc) {
43
+ docUrl = await client
44
+ .collection('io.cozy.files')
45
+ .getDownloadLinkById(firstDoc._id, firstDoc.name)
46
+ } else {
47
+ const blob = await makePdfBlob(client, docs)
48
+ docUrl = URL.createObjectURL(blob)
49
+ }
47
50
 
48
- window.open(downloadURL, '_blank')
51
+ window.open(docUrl, '_blank')
49
52
  } catch (error) {
50
- logger.error(`Error trying to print document: ${JSON.stringify(error)}`)
53
+ logger.error(
54
+ `Error trying to print document ${
55
+ webviewIntent ? 'inside flagship appp' : 'outside flagship app'
56
+ }: ${JSON.stringify(error)}`
57
+ )
51
58
  }
52
59
  },
53
60
  Component: forwardRef((props, ref) => {
@@ -13,7 +13,7 @@ import { useMutationObserver, useTimeoutWhen } from 'rooks'
13
13
  import Fade from '@material-ui/core/Fade'
14
14
  import Portal from '@material-ui/core/Portal'
15
15
 
16
- import { getFlagshipMetadata } from 'cozy-device-helper'
16
+ import { getFlagshipMetadata } from '../hooks/useSetFlagshipUi/helpers'
17
17
 
18
18
  import { useSetFlagshipUI } from '../hooks/useSetFlagshipUi/useSetFlagshipUI'
19
19
  import CozyTheme, { useCozyTheme } from '../providers/CozyTheme'
@@ -392,7 +392,7 @@ BottomSheet.defaultProps = {
392
392
  toolbarProps: {},
393
393
  backdrop: false,
394
394
  offset:
395
- (getFlagshipMetadata().immersive && getFlagshipMetadata().navbarHeight) ?? 0
395
+ (getFlagshipMetadata().immersive && getFlagshipMetadata().navbarHeight) || 0
396
396
  }
397
397
 
398
398
  BottomSheet.propTypes = {
@@ -94,7 +94,7 @@ const initialState = {
94
94
  isSecondBottomSheetDisplayed: false,
95
95
  mediumHeight: isTesting() ? 450 : undefined,
96
96
  mediumHeightRatio: undefined,
97
- offset: 0
97
+ offset: undefined
98
98
  }
99
99
  const showBottomSheet = () => setState({ isBottomSheetDisplayed: true })
100
100
  const hideBottomSheet = () => setState({ isBottomSheetDisplayed: false })
@@ -1,8 +1,8 @@
1
1
  import React from 'react'
2
- import { getFlagshipMetadata } from 'cozy-device-helper'
3
2
 
4
3
  import { ANIMATION_DURATION } from './constants'
5
4
  import { getSafeAreaValue } from '../helpers/getSafeArea'
5
+ import { getFlagshipMetadata } from '../hooks/useSetFlagshipUi/helpers'
6
6
 
7
7
  export const computeToolbarHeight = (toolbarProps = {}) => {
8
8
  const { ref, height } = toolbarProps
@@ -12,7 +12,7 @@ import {
12
12
  jest.mock('../helpers/getSafeArea', () => ({
13
13
  getSafeAreaValue: jest.fn().mockReturnValue(15)
14
14
  }))
15
- jest.mock('cozy-device-helper', () => ({
15
+ jest.mock('../hooks/useSetFlagshipUi/helpers', () => ({
16
16
  getFlagshipMetadata: jest.fn(() => ({ navbarHeight: 10 }))
17
17
  }))
18
18
  const windowSpy = jest.spyOn(window, 'window', 'get')
@@ -208,50 +208,9 @@ const initialVariants = [{
208
208
  withBackground: false
209
209
  }]
210
210
 
211
- // The goal of this method is to simulate the
212
- // immersive mode of the flagship app. So we
213
- // add the flagship-app class to the body and
214
- // we set 2 css variables:
215
- // flagship-top-height
216
- // flagship-bottom-height
217
- const setFlagshipVars = () => {
218
- const root = document.getElementsByTagName('body')[0]
219
- root.style.setProperty('--flagship-top-height', "40px")
220
- root.style.setProperty('--flagship-bottom-height', "40px")
221
- root.classList.add('flagship-app')
222
-
223
- const statusBarDiv = document.createElement("div")
224
- statusBarDiv.style.cssText = "position:fixed;top:0;height:40px;z-index:10000000;background-color:red;width:100%"
225
- // and give it some content
226
- const statusBarDivContent = document.createTextNode("Top Status Bar with clock")
227
-
228
- // add the text node to the newly created div
229
- statusBarDiv.appendChild(statusBarDivContent)
230
-
231
-
232
- const bottomBarDiv = document.createElement("div")
233
- bottomBarDiv.style.cssText = "position:fixed;bottom:0;height:40px;z-index:10000000;background-color:red;width:100%"
234
- // and give it some content
235
- const bottomBarDivContent = document.createTextNode("BottomBar")
236
-
237
- // add the text node to the newly created div
238
- bottomBarDiv.appendChild(bottomBarDivContent)
239
-
240
- // add the newly created element and its content into the DOM
241
- const currentDiv = document.getElementById("rsg-root")
242
- document.body.insertBefore(statusBarDiv, currentDiv)
243
- document.body.insertBefore(bottomBarDiv, currentDiv)
244
- }
245
-
246
211
  ;
247
212
 
248
213
  <DemoProvider>
249
- <Button
250
- className="u-mb-1"
251
- variant="secondary"
252
- label={'Simulate Immersive Flagship Mode'}
253
- onClick={() => setFlagshipVars()}
254
- />
255
214
  <Variants initialVariants={initialVariants}>
256
215
  {variant => (
257
216
  <>
@@ -238,8 +238,11 @@ it('should provide the inversed UI when Cozybar is not black on white, but white
238
238
  })
239
239
 
240
240
  jest.mock('cozy-device-helper', () => ({
241
- isFlagshipApp: (): boolean => true,
242
- getFlagshipMetadata: (): Record<string, never> => ({})
241
+ isFlagshipApp: (): boolean => true
242
+ }))
243
+ jest.mock('../hooks/useSetFlagshipUi/helpers', () => ({
244
+ getFlagshipMetadata: (): Record<string, never> => ({}),
245
+ setRsgFlagshipElements: (): null => null
243
246
  }))
244
247
 
245
248
  const onOpenMountExpected = {
@@ -1,7 +1,7 @@
1
1
  import { getLuminance, Theme, useTheme } from '@material-ui/core'
2
2
  import { useEffect } from 'react'
3
3
 
4
- import { getFlagshipMetadata, isFlagshipApp } from 'cozy-device-helper'
4
+ import { isFlagshipApp } from 'cozy-device-helper'
5
5
  import { useWebviewIntent } from 'cozy-intent'
6
6
 
7
7
  import {
@@ -9,6 +9,8 @@ import {
9
9
  ThemeColor,
10
10
  parseArg
11
11
  } from '../hooks/useSetFlagshipUi/useSetFlagshipUI'
12
+ import { getFlagshipMetadata } from '../hooks/useSetFlagshipUi/helpers'
13
+ import { isRsg } from '../hooks/useSetFlagshipUi/helpers'
12
14
 
13
15
  interface DialogEffectsOptions {
14
16
  cozybar?: Element | null
@@ -192,7 +194,8 @@ export const useDialogSetFlagshipUI = (
192
194
  }, [open, webviewIntent]) // eslint-disable-line react-hooks/exhaustive-deps
193
195
  }
194
196
 
195
- export const useDialogEffects = isFlagshipApp()
196
- ? useHook
197
- : // eslint-disable-next-line @typescript-eslint/no-empty-function
198
- (): void => {}
197
+ export const useDialogEffects =
198
+ isFlagshipApp() || isRsg
199
+ ? useHook
200
+ : // eslint-disable-next-line @typescript-eslint/no-empty-function
201
+ (): void => {}
@@ -7,6 +7,7 @@ import { Theme, useTheme } from '@material-ui/core'
7
7
  import { isFlagshipApp } from 'cozy-device-helper'
8
8
 
9
9
  import { useSetFlagshipUI } from '../../hooks/useSetFlagshipUi/useSetFlagshipUI'
10
+ import { isRsg } from '../../hooks/useSetFlagshipUi/helpers'
10
11
 
11
12
  const getBottomBackground = (theme: Theme): string => {
12
13
  const sidebar = document.getElementById('sidebar')
@@ -40,7 +41,8 @@ const useHook = (): void => {
40
41
  )
41
42
  }
42
43
 
43
- export const useActionMenuEffects = isFlagshipApp()
44
- ? useHook
45
- : // eslint-disable-next-line @typescript-eslint/no-empty-function
46
- (): void => {}
44
+ export const useActionMenuEffects =
45
+ isFlagshipApp() || isRsg
46
+ ? useHook
47
+ : // eslint-disable-next-line @typescript-eslint/no-empty-function
48
+ (): void => {}
@@ -4,9 +4,10 @@
4
4
 
5
5
  import { Theme, useTheme } from '@material-ui/core'
6
6
 
7
- import { getFlagshipMetadata, isFlagshipApp } from 'cozy-device-helper'
7
+ import { isFlagshipApp } from 'cozy-device-helper'
8
8
 
9
9
  import { useSetFlagshipUI } from '../../hooks/useSetFlagshipUi/useSetFlagshipUI'
10
+ import { getFlagshipMetadata } from '../../hooks/useSetFlagshipUi/helpers'
10
11
 
11
12
  const getTopBackground = (theme: Theme, cozyBar: Element | null): string =>
12
13
  (cozyBar && getComputedStyle(cozyBar).getPropertyValue('background-color')) ||
@@ -0,0 +1,149 @@
1
+ // eslint-disable-next-line no-unused-vars
2
+ import { FlagshipUI } from './useSetFlagshipUI'
3
+ import { getFlagshipMetadata as getFlagshipMetadataFromDeviceHelper } from 'cozy-device-helper'
4
+
5
+ /**
6
+ * The goal of this method is to simulate the immersive mode of the flagship app.
7
+ * So we add the flagship-app class to the body and we set css variables:
8
+ * If the "contained" mode is activated, it simulates an classic app behavior
9
+ * If the "immersive" mode is activated, it simulates a fullscreen app like Home
10
+ */
11
+ export const addFlagshipElements = () => {
12
+ const root = document.getElementsByTagName('body')[0]
13
+ root.style.setProperty('--flagship-top-height', '40px')
14
+ root.style.setProperty('--flagship-top-bgcolor', 'white') // used only for cozy-ui docs
15
+ root.style.setProperty('--flagship-top-overlay', 'transparent') // used only for cozy-ui docs
16
+ root.style.setProperty('--flagship-top-color', 'black') // used only for cozy-ui docs
17
+ root.style.setProperty('--flagship-bottom-height', '40px')
18
+ root.style.setProperty('--flagship-bottom-bgcolor', 'white') // used only for cozy-ui docs
19
+ root.style.setProperty('--flagship-bottom-overlay', 'transparent') // used only for cozy-ui docs
20
+ root.style.setProperty('--flagship-bottom-color', 'black') // used only for cozy-ui docs
21
+ root.classList.add('flagship-app')
22
+
23
+ // create status bar
24
+ const statusBarDiv = document.createElement('div')
25
+ statusBarDiv.setAttribute('id', 'flagshipStatusBar')
26
+ statusBarDiv.style.cssText =
27
+ 'position:fixed;top:0;height:var(--flagship-top-height);z-index:10000000;color:var(--flagship-top-color);background-color:var(--flagship-top-bgcolor);width:100%;display:flex;align-items:center;justify-content:center'
28
+ // create status bar overlay
29
+ const statusBarOverlay = document.createElement('div')
30
+ statusBarOverlay.setAttribute('id', 'flagshipStatusBarOverlay')
31
+ statusBarOverlay.style.cssText =
32
+ 'position:absolute;top:0;left:0;width:100%;height:100%;z-index:-1;background-color:var(--flagship-top-overlay)'
33
+ statusBarDiv.appendChild(statusBarOverlay)
34
+ // create status bar text
35
+ const statusBarText = document.createElement('div')
36
+ statusBarText.setAttribute('id', 'flagshipStatusBarText')
37
+ statusBarText.innerText = 'Flagship Top Status Bar with clock'
38
+ statusBarDiv.appendChild(statusBarText)
39
+
40
+ // create nav bar
41
+ const navBarDiv = document.createElement('div')
42
+ navBarDiv.setAttribute('id', 'flagshipNavBar')
43
+ navBarDiv.style.cssText =
44
+ 'position:fixed;bottom:0;height:var(--flagship-bottom-height);z-index:10000000;color:var(--flagship-bottom-color);background-color:var(--flagship-bottom-bgcolor);width:100%;display:flex;align-items:center;justify-content:center'
45
+ // create nav bar overlay
46
+ const navBarOverlay = document.createElement('div')
47
+ navBarOverlay.setAttribute('id', 'flagshipNavBarOverlay')
48
+ navBarOverlay.style.cssText =
49
+ 'position:absolute;top:0;left:0;width:100%;height:100%;z-index:-1;background-color:var(--flagship-bottom-overlay)'
50
+ navBarDiv.appendChild(navBarOverlay)
51
+ // create nav bar text
52
+ const navBarText = document.createElement('div')
53
+ navBarText.setAttribute('id', 'flagshipNavBarText')
54
+ navBarText.innerText = 'Flagship Nav Bar'
55
+ navBarDiv.appendChild(navBarText)
56
+
57
+ // add status and nav bar and its content into the DOM
58
+ const currentDiv = document.getElementById('rsg-root')
59
+ document.body.insertBefore(statusBarDiv, currentDiv)
60
+ document.body.insertBefore(navBarDiv, currentDiv)
61
+ }
62
+
63
+ /**
64
+ * Remove fake DOM element that simulates native Status and Nav bar
65
+ */
66
+ export const removeFlagshipElements = () => {
67
+ const root = document.getElementsByTagName('body')[0]
68
+ root.style.removeProperty('--flagship-top-height')
69
+ root.style.removeProperty('--flagship-top-bgcolor')
70
+ root.style.removeProperty('--flagship-top-overlay')
71
+ root.style.removeProperty('--flagship-top-color')
72
+ root.style.removeProperty('--flagship-bottom-height')
73
+ root.style.removeProperty('--flagship-bottom-bgcolor')
74
+ root.style.removeProperty('--flagship-bottom-overlay')
75
+ root.style.removeProperty('--flagship-bottom-color')
76
+ root.classList.remove('flagship-app')
77
+
78
+ document.getElementById('flagshipStatusBar')?.remove()
79
+ document.getElementById('flagshipNavBar')?.remove()
80
+ }
81
+
82
+ /**
83
+ * Whether we are in cozy-ui documentation (Rsg is for ReactStyleGuide)
84
+ * @returns {boolean}
85
+ */
86
+ export const isRsg = !!document.getElementById('rsg-root')
87
+
88
+ /**
89
+ * Overrides flagship metadata
90
+ * See https://github.com/cozy/cozy-libs/blob/master/packages/cozy-device-helper/src/flagship.ts#L13
91
+ */
92
+ export const getFlagshipMetadata = isRsg
93
+ ? () => ({
94
+ immersive:
95
+ JSON.parse(localStorage.getItem('flagship-app'))?.contained === 'off' ||
96
+ false,
97
+ statusBarHeight: 40,
98
+ navbarHeight: 40
99
+ })
100
+ : getFlagshipMetadataFromDeviceHelper
101
+
102
+ /**
103
+ * Set the css values for Status and Nav bar inside cozy-ui documentation
104
+ * see https://github.com/cozy/cozy-libs/blob/master/packages/cozy-intent/src/api/models/applications.ts#L33
105
+ * @param {undefined | Partial<FlagshipUI>} param0
106
+ * @returns
107
+ */
108
+ export const setRsgFlagshipElements = ({
109
+ topBackground,
110
+ topOverlay,
111
+ topTheme,
112
+ bottomBackground,
113
+ bottomOverlay,
114
+ bottomTheme
115
+ } = {}) => {
116
+ if (!isRsg) return
117
+
118
+ const root = document.getElementsByTagName('body')[0]
119
+
120
+ if (topBackground) {
121
+ root.style.setProperty('--flagship-top-bgcolor', topBackground)
122
+ }
123
+
124
+ if (topOverlay) {
125
+ root.style.setProperty('--flagship-top-overlay', topOverlay)
126
+ }
127
+
128
+ if (topTheme) {
129
+ root.style.setProperty(
130
+ '--flagship-top-color',
131
+ topTheme === 'dark' ? 'black' : 'white'
132
+ )
133
+ }
134
+
135
+ if (bottomBackground) {
136
+ root.style.setProperty('--flagship-bottom-bgcolor', bottomBackground)
137
+ }
138
+
139
+ if (bottomOverlay) {
140
+ root.style.setProperty('--flagship-bottom-overlay', bottomOverlay)
141
+ }
142
+
143
+ if (bottomTheme) {
144
+ root.style.setProperty(
145
+ '--flagship-bottom-color',
146
+ bottomTheme === 'dark' ? 'black' : 'white'
147
+ )
148
+ }
149
+ }
@@ -8,6 +8,8 @@ import { useWebviewIntent } from 'cozy-intent'
8
8
  import { FlagshipUI as IntentInterface } from 'cozy-intent/dist/api/models/applications'
9
9
  import { WebviewService } from 'cozy-intent/dist/api/services/WebviewService'
10
10
 
11
+ import { setRsgFlagshipElements } from './helpers'
12
+
11
13
  export enum ThemeColor {
12
14
  Dark = 'dark',
13
15
  Light = 'light'
@@ -23,11 +25,13 @@ export const parseArg = (
23
25
  arg?: FlagshipUI,
24
26
  caller?: string
25
27
  ): void => {
26
- if (!webviewIntent) return
27
-
28
- const sanitized = isObject(arg) && pickBy(arg, identity)
28
+ const sanitized = isObject(arg) ? pickBy(arg, identity) : undefined
29
29
  const validUI = !isEmpty(sanitized) && sanitized
30
30
 
31
+ setRsgFlagshipElements(sanitized)
32
+
33
+ if (!webviewIntent) return
34
+
31
35
  validUI && webviewIntent.call('setFlagshipUI', validUI, caller)
32
36
  }
33
37