From caff7594716655dff84c437753e175dd29a19092 Mon Sep 17 00:00:00 2001 From: Beatrice Guerra Date: Wed, 23 Nov 2022 11:08:12 +0100 Subject: [PATCH] feat: add setting to change base font size for scaling * build(deps): update @zextrs/carbonio-design-system dep refs: CDS-78 (#156) --- babel.config.js | 1 + package-lock.json | 169 +++++----- package.json | 2 +- src/boot/bootstrapper-router.tsx | 2 +- src/boot/theme-provider.tsx | 200 +++++------- src/constants/index.ts | 16 + src/network/edit-settings.ts | 54 +++- src/reporting/feedback.tsx | 4 +- src/search/search-bar.tsx | 4 +- ...picker-style.jsx => date-picker-style.tsx} | 193 ++++++++++-- .../components/date-time-select-view.tsx | 13 +- .../general-settings/appearance-settings.tsx | 79 ++--- .../dark-theme-settings-section.tsx | 75 +++++ .../general-settings/out-of-office-view.tsx | 13 +- .../scaling-setting-section.tsx | 298 ++++++++++++++++++ src/settings/components/utils.ts | 15 + src/settings/general-settings-sub-sections.ts | 8 +- src/settings/general-settings.tsx | 146 ++++++--- .../language-and-timezone-settings.tsx | 11 +- src/settings/search-settings-view.tsx | 9 +- src/shell/hooks.ts | 27 +- translations/de.json | 5 + translations/en.json | 18 ++ translations/es.json | 5 + translations/fi.json | 11 +- translations/fr.json | 5 + translations/hi.json | 5 + translations/id.json | 5 + translations/it.json | 5 + translations/ja.json | 5 + translations/nl.json | 5 + translations/pl.json | 5 + translations/pt.json | 5 + translations/pt_BR.json | 5 + translations/ro.json | 10 +- translations/ru.json | 5 + translations/th.json | 5 + translations/tr.json | 5 + translations/vi.json | 5 + translations/zh_Hans.json | 5 + types/account/index.d.ts | 6 +- types/apps/index.d.ts | 5 +- types/network/entities.d.ts | 73 +++++ types/network/index.d.ts | 54 +++- types/settings/index.d.ts | 10 + types/styled-components.d.ts | 15 + types/theme/index.d.ts | 5 +- 47 files changed, 1232 insertions(+), 394 deletions(-) rename src/settings/components/{date-picker-style.jsx => date-picker-style.tsx} (93%) create mode 100644 src/settings/components/general-settings/dark-theme-settings-section.tsx create mode 100644 src/settings/components/general-settings/scaling-setting-section.tsx create mode 100644 types/network/entities.d.ts create mode 100644 types/settings/index.d.ts create mode 100644 types/styled-components.d.ts diff --git a/babel.config.js b/babel.config.js index 9a60e717..a6bb3f5d 100644 --- a/babel.config.js +++ b/babel.config.js @@ -24,6 +24,7 @@ module.exports = { 'i18next-extract', { outputPath: 'translations/{{ns}}.json', + defaultContexts: [], defaultNS: 'en', jsonSpace: 4, compatibilityJSON: 'v3' diff --git a/package-lock.json b/package-lock.json index aecdff24..0eb7e662 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,11 @@ "version": "0.4.43", "hasInstallScript": true, "license": "AGPL-3.0-only", - "dependences": { + "dependencies": { "@fontsource/roboto": "^4.5.7", "@sentry/browser": "^6.17.7", "@tinymce/tinymce-react": "^3.13.0", - "@zextras/carbonio-design-system": "^0.5.1", + "@zextras/carbonio-design-system": "^0.5.2", "@zextras/carbonio-ui-preview": "^0.2.4", "darkreader": "4.9.46", "history": "^5.2.0", @@ -1735,10 +1735,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.16.7", - "license": "MIT", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", + "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.10" }, "engines": { "node": ">=6.9.0" @@ -2652,8 +2653,9 @@ "license": "MIT" }, "node_modules/@popperjs/core": { - "version": "2.11.0", - "license": "MIT", + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", + "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -3911,15 +3913,14 @@ "license": "Apache-2.0" }, "node_modules/@zextras/carbonio-design-system": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@zextras/carbonio-design-system/-/carbonio-design-system-0.5.1.tgz", - "integrity": "sha512-RRC/GWhmQaRNFZiDGiw+xYp9knP2MCCIk3wL7VNaQcUza2pPQSYxSs2uzVCeO22+DVxjgKL6PZLYwvgdIS1lrA==", - "dependencies": { - "@popperjs/core": "2.11.0", - "darkreader": "4.9.44", - "polished": "4.1.3", - "prop-types": "15.7.2", - "react-datepicker": "^4.3.0", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@zextras/carbonio-design-system/-/carbonio-design-system-0.5.2.tgz", + "integrity": "sha512-A6yeXS4MBglT0z44coJ9WzWbnRWRco8BTd96MxYIdMRB1DJAJaBYnryoz+DQaVqfYVh9BmUsiJnGAoy0HTQSJA==", + "dependencies": { + "@popperjs/core": "2.11.6", + "darkreader": "4.9.58", + "polished": "4.2.2", + "react-datepicker": "^4.8.0", "react-docgen-typescript": "^2.2.2" }, "peerDependencies": { @@ -3930,26 +3931,14 @@ } }, "node_modules/@zextras/carbonio-design-system/node_modules/darkreader": { - "version": "4.9.44", - "license": "MIT", + "version": "4.9.58", + "resolved": "https://registry.npmjs.org/darkreader/-/darkreader-4.9.58.tgz", + "integrity": "sha512-D/JGoJqW3m2AWBLhO+Pev+eThfs+CwRT4bcLb/1zKjql2yVwG0lx8C2XRDdSVGHw4y11n26W7syWoBpUfuhMqQ==", "funding": { "type": "opencollective", "url": "https://opencollective.com/darkreader/donate" } }, - "node_modules/@zextras/carbonio-design-system/node_modules/prop-types": { - "version": "15.7.2", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "node_modules/@zextras/carbonio-design-system/node_modules/react-is": { - "version": "16.13.1", - "license": "MIT" - }, "node_modules/@zextras/carbonio-ui-configs": { "version": "0.1.11", "dev": true, @@ -5925,8 +5914,9 @@ "license": "MIT" }, "node_modules/date-fns": { - "version": "2.28.0", - "license": "MIT", + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", "engines": { "node": ">=0.11" }, @@ -11630,10 +11620,11 @@ } }, "node_modules/polished": { - "version": "4.1.3", - "license": "MIT", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.2.2.tgz", + "integrity": "sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==", "dependencies": { - "@babel/runtime": "^7.14.0" + "@babel/runtime": "^7.17.8" }, "engines": { "node": ">=10" @@ -12175,8 +12166,9 @@ } }, "node_modules/react-datepicker": { - "version": "4.6.0", - "license": "MIT", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.8.0.tgz", + "integrity": "sha512-u69zXGHMpxAa4LeYR83vucQoUCJQ6m/WBsSxmUMu/M8ahTSVMMyiyQzauHgZA2NUr9y0FUgOAix71hGYUb6tvg==", "dependencies": { "@popperjs/core": "^2.9.2", "classnames": "^2.2.6", @@ -12186,8 +12178,8 @@ "react-popper": "^2.2.5" }, "peerDependencies": { - "react": "^16.9.0 || ^17", - "react-dom": "^16.9.0 || ^17" + "react": "^16.9.0 || ^17 || ^18", + "react-dom": "^16.9.0 || ^17 || ^18" } }, "node_modules/react-docgen-typescript": { @@ -12211,7 +12203,8 @@ }, "node_modules/react-fast-compare": { "version": "3.2.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, "node_modules/react-i18next": { "version": "11.15.3", @@ -12271,15 +12264,16 @@ } }, "node_modules/react-onclickoutside": { - "version": "6.12.1", - "license": "MIT", + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.2.tgz", + "integrity": "sha512-NMXGa223OnsrGVp5dJHkuKxQ4czdLmXSp5jSV9OqiCky9LOpPATn3vLldc+q5fK3gKbEHvr7J1u0yhBh/xYkpA==", "funding": { "type": "individual", "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md" }, "peerDependencies": { - "react": "^15.5.x || ^16.x || ^17.x", - "react-dom": "^15.5.x || ^16.x || ^17.x" + "react": "^15.5.x || ^16.x || ^17.x || ^18.x", + "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x" } }, "node_modules/react-pdf": { @@ -12318,15 +12312,17 @@ } }, "node_modules/react-popper": { - "version": "2.2.5", - "license": "MIT", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", + "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", "dependencies": { "react-fast-compare": "^3.0.1", "warning": "^4.0.2" }, "peerDependencies": { "@popperjs/core": "^2.0.0", - "react": "^16.8.0 || ^17" + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" } }, "node_modules/react-redux": { @@ -12693,8 +12689,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.9", - "license": "MIT" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/regenerator-transform": { "version": "0.14.5", @@ -16211,9 +16208,11 @@ } }, "@babel/runtime": { - "version": "7.16.7", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", + "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.10" } }, "@babel/runtime-corejs3": { @@ -16816,7 +16815,9 @@ "dev": true }, "@popperjs/core": { - "version": "2.11.0" + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", + "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" }, "@reduxjs/toolkit": { "version": "1.7.1", @@ -17660,31 +17661,21 @@ "version": "4.2.2" }, "@zextras/carbonio-design-system": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@zextras/carbonio-design-system/-/carbonio-design-system-0.5.1.tgz", - "integrity": "sha512-RRC/GWhmQaRNFZiDGiw+xYp9knP2MCCIk3wL7VNaQcUza2pPQSYxSs2uzVCeO22+DVxjgKL6PZLYwvgdIS1lrA==", - "requires": { - "@popperjs/core": "2.11.0", - "darkreader": "4.9.44", - "polished": "4.1.3", - "prop-types": "15.7.2", - "react-datepicker": "^4.3.0", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@zextras/carbonio-design-system/-/carbonio-design-system-0.5.2.tgz", + "integrity": "sha512-A6yeXS4MBglT0z44coJ9WzWbnRWRco8BTd96MxYIdMRB1DJAJaBYnryoz+DQaVqfYVh9BmUsiJnGAoy0HTQSJA==", + "requires": { + "@popperjs/core": "2.11.6", + "darkreader": "4.9.58", + "polished": "4.2.2", + "react-datepicker": "^4.8.0", "react-docgen-typescript": "^2.2.2" }, "dependencies": { "darkreader": { - "version": "4.9.44" - }, - "prop-types": { - "version": "15.7.2", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "react-is": { - "version": "16.13.1" + "version": "4.9.58", + "resolved": "https://registry.npmjs.org/darkreader/-/darkreader-4.9.58.tgz", + "integrity": "sha512-D/JGoJqW3m2AWBLhO+Pev+eThfs+CwRT4bcLb/1zKjql2yVwG0lx8C2XRDdSVGHw4y11n26W7syWoBpUfuhMqQ==" } } }, @@ -18940,7 +18931,9 @@ "version": "3.1.0" }, "date-fns": { - "version": "2.28.0" + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==" }, "debug": { "version": "4.3.3", @@ -22375,9 +22368,11 @@ } }, "polished": { - "version": "4.1.3", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.2.2.tgz", + "integrity": "sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==", "requires": { - "@babel/runtime": "^7.14.0" + "@babel/runtime": "^7.17.8" } }, "portfinder": { @@ -22688,7 +22683,9 @@ } }, "react-datepicker": { - "version": "4.6.0", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.8.0.tgz", + "integrity": "sha512-u69zXGHMpxAa4LeYR83vucQoUCJQ6m/WBsSxmUMu/M8ahTSVMMyiyQzauHgZA2NUr9y0FUgOAix71hGYUb6tvg==", "requires": { "@popperjs/core": "^2.9.2", "classnames": "^2.2.6", @@ -22711,7 +22708,9 @@ } }, "react-fast-compare": { - "version": "3.2.0" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, "react-i18next": { "version": "11.15.3", @@ -22747,7 +22746,9 @@ } }, "react-onclickoutside": { - "version": "6.12.1", + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.2.tgz", + "integrity": "sha512-NMXGa223OnsrGVp5dJHkuKxQ4czdLmXSp5jSV9OqiCky9LOpPATn3vLldc+q5fK3gKbEHvr7J1u0yhBh/xYkpA==", "requires": {} }, "react-pdf": { @@ -22772,7 +22773,9 @@ } }, "react-popper": { - "version": "2.2.5", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", + "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", "requires": { "react-fast-compare": "^3.0.1", "warning": "^4.0.2" @@ -23036,7 +23039,9 @@ } }, "regenerator-runtime": { - "version": "0.13.9" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "regenerator-transform": { "version": "0.14.5", diff --git a/package.json b/package.json index a4b6b20a..fed5773b 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "@fontsource/roboto": "^4.5.7", "@sentry/browser": "^6.17.7", "@tinymce/tinymce-react": "^3.13.0", - "@zextras/carbonio-design-system": "^0.5.1", + "@zextras/carbonio-design-system": "^0.5.2", "@zextras/carbonio-ui-preview": "^0.2.4", "darkreader": "4.9.46", "history": "^5.2.0", diff --git a/src/boot/bootstrapper-router.tsx b/src/boot/bootstrapper-router.tsx index 16b74f63..2227e5eb 100644 --- a/src/boot/bootstrapper-router.tsx +++ b/src/boot/bootstrapper-router.tsx @@ -53,7 +53,7 @@ const DefaultViewsRegister: FC<{ t: TFunction }> = ({ t }) => { }; const BootstrapperRouter: FC = () => { - const { t } = useTranslation(); + const [t] = useTranslation(); return ( diff --git a/src/boot/theme-provider.tsx b/src/boot/theme-provider.tsx index 021c4018..5cb46afc 100644 --- a/src/boot/theme-provider.tsx +++ b/src/boot/theme-provider.tsx @@ -4,138 +4,91 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import React, { createContext, FC, useCallback, useEffect, useState } from 'react'; -import { ThemeProvider as UIThemeProvider } from '@zextras/carbonio-design-system'; -import { enable, disable, auto, setFetchMethod } from 'darkreader'; +import React, { createContext, FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { + ThemeProvider as UIThemeProvider, + ThemeProviderProps +} from '@zextras/carbonio-design-system'; +import { auto, disable, enable, setFetchMethod } from 'darkreader'; import { reduce } from 'lodash'; -import { useAccountStore } from '../store/account'; -import { DarkReaderPropValues, ThemeExtension, ThemeExtensionMap } from '../../types'; -import { darkReaderDynamicThemeFixes } from '../constants'; +import { createGlobalStyle, DefaultTheme } from 'styled-components'; +import { DarkReaderPropValues, ThemeExtension } from '../../types'; +import { darkReaderDynamicThemeFixes, LOCAL_STORAGE_SETTINGS_KEY } from '../constants'; +import { useLocalStorage } from '../shell/hooks'; +import { ScalingSettings } from '../../types/settings'; +import { getAutoScalingFontSize } from '../settings/components/utils'; setFetchMethod(window.fetch); -export const ThemeCallbacksContext = createContext<{ +interface ThemeCallbacks { addExtension: (newExtension: ThemeExtension, id: string) => void; setDarkReaderState: (newState: DarkReaderPropValues) => void; -}>({ - // eslint-disable-next-line @typescript-eslint/no-empty-function - addExtension: (newExtension: ThemeExtension, id: string) => {}, - // eslint-disable-next-line @typescript-eslint/no-empty-function - setDarkReaderState: (newState: DarkReaderPropValues) => {} -}); +} -const themeSizes = ( - size: 'small' | 'normal' | 'large' | 'larger' | 'default' | string -): ThemeExtension => { - switch (size) { - case 'small': { - return (theme: any): any => { - // eslint-disable-next-line no-param-reassign - theme.sizes.font = { - extrasmall: '0.625rem', - small: '0.75rem', - medium: '0.875rem', - large: '1rem' - }; - return theme; - }; - } - case 'large': { - return (theme: any): any => { - // eslint-disable-next-line no-param-reassign - theme.sizes.font = { - extrasmall: '0.875rem', - small: '1rem', - medium: '1.125rem', - large: '1.25rem' - }; - return theme; - }; - } - case 'larger': { - return (theme: any): any => { - // eslint-disable-next-line no-param-reassign - theme.sizes.font = { - extrasmall: '1rem', - small: '1.125rem', - medium: '1.25rem', - large: '1.375rem' - }; - return theme; - }; - } - case 'default': - case 'normal': - default: { - return (theme: any): any => { - // eslint-disable-next-line no-param-reassign - theme.sizes.font = { - extrasmall: '0.75rem', - small: '0.875rem', - medium: '1rem', - large: '1.125rem' - }; - return theme; - }; - } +export const ThemeCallbacksContext = createContext({ + addExtension: () => { + throw Error('Not implemented'); + }, + setDarkReaderState: () => { + throw Error('not implemented'); } -}; +}); -const paletteExtension = - (): ThemeExtension => - (theme: any): any => { - // eslint-disable-next-line no-param-reassign - theme.palette.shared = { +const paletteExtension = (theme: DefaultTheme): DefaultTheme => ({ + ...theme, + palette: { + ...theme.palette, + shared: { regular: '#FFB74D', hover: '#FFA21A', active: '#FFA21A', focus: '#FF9800', disabled: '#FFD699' - }; - // eslint-disable-next-line no-param-reassign - theme.palette.linked = { + }, + linked: { regular: '#AB47BC', hover: '#8B3899', active: '#8B3899', focus: '#7A3187', disabled: '#DDB4E4' - }; - return theme; - }; - -const iconExtension = - (): ThemeExtension => - (theme: any): any => { - // eslint-disable-next-line no-param-reassign - theme.icons.Shared = theme.icons.ArrowCircleRight; - // eslint-disable-next-line no-param-reassign - theme.icons.Linked = theme.icons.ArrowCircleLeft; - return theme; - }; + } + } +}); + +const iconExtension: ThemeExtension = (theme) => ({ + ...theme, + icons: { + ...theme.icons, + Shared: theme.icons.ArrowCircleRight, + Linked: theme.icons.ArrowCircleLeft + } +}); + +interface GlobalStyledProps { + baseFontSize: number; +} + +const GlobalStyle = createGlobalStyle` + html { + font-size: ${({ baseFontSize }): string => `${baseFontSize}%`}; + } +`; export const ThemeProvider: FC = ({ children }) => { - const zimbraPrefFontSize = useAccountStore((s) => s.settings.prefs?.zimbraPrefFontSize as string); - // TODO: update when the DS is fully typed :D - const [extensions, setExtensions] = useState({ - fonts: (theme) => { - // eslint-disable-next-line no-param-reassign - theme.sizes.font = { - extrasmall: '0.75rem', - small: '0.875rem', - medium: '1rem', - large: '1.125rem' - }; - return theme; - } - }); + const [localStorageSettings] = useLocalStorage(LOCAL_STORAGE_SETTINGS_KEY, {}); + + const [extensions, setExtensions] = useState>>( + {} + ); + useEffect(() => { - setExtensions((e) => ({ - ...e, - fonts: themeSizes(zimbraPrefFontSize), - palette: paletteExtension(), - icons: iconExtension() + setExtensions((extension) => ({ + ...extension, + palette: paletteExtension, + icons: iconExtension })); - }, [zimbraPrefFontSize]); + }, []); + const [darkReaderState, setDarkReaderState] = useState('disabled'); useEffect(() => { switch (darkReaderState) { @@ -163,17 +116,38 @@ export const ThemeProvider: FC = ({ children }) => { break; } }, [darkReaderState]); - const aggregatedExtensions = useCallback( - (theme: any) => reduce(extensions, (acc, val) => val(acc), theme), + + const aggregatedExtensions = useCallback>( + (theme) => + reduce( + extensions, + (themeAccumulator, themeExtensionFn) => { + if (themeExtensionFn) { + return themeExtensionFn(themeAccumulator); + } + return themeAccumulator; + }, + theme + ), [extensions] ); - const addExtension = useCallback((newExtension: ThemeExtension, id: string) => { + + const addExtension = useCallback((newExtension, id) => { setExtensions((ext) => ({ ...ext, [id]: newExtension })); }, []); + const baseFontSize = useMemo(() => { + const savedScalingValueSetting = localStorageSettings['settings.appearance_setting.scaling']; + if (savedScalingValueSetting !== undefined) { + return savedScalingValueSetting; + } + return getAutoScalingFontSize(); + }, [localStorageSettings]); + return ( - + + {children} diff --git a/src/constants/index.ts b/src/constants/index.ts index dd9e1663..08811b93 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -103,5 +103,21 @@ export const FOLDER_VIEW = { task: 'task', chat: 'chat' }; +export const LOCAL_STORAGE_SETTINGS_KEY = 'settings'; +export const LOCAL_STORAGE_SEARCH_KEY = 'search_suggestions'; +export const SCALING_OPTIONS = [ + { value: 75, label: 'xs' }, + { value: 87.5, label: 's' }, + { value: 100, label: 'm' }, + { value: 112.5, label: 'l' }, + { value: 125, label: 'xl' } +] as const; +export const BASE_FONT_SIZE = 100; +export const SCALING_LIMIT = { + WIDTH: 1400, + HEIGHT: 900, + DPR: 2 // device pixel ratio +} as const; export const LOGIN_V3_CONFIG_PATH = '/zx/login/v3/config'; +export const DARK_READER_PROP_KEY = 'zappDarkreaderMode'; diff --git a/src/network/edit-settings.ts b/src/network/edit-settings.ts index 9855fd87..dac64f0a 100644 --- a/src/network/edit-settings.ts +++ b/src/network/edit-settings.ts @@ -6,12 +6,36 @@ import { filter, find, findIndex, forEach, map, reduce, isArray } from 'lodash'; import { SHELL_APP_ID } from '../constants'; -import { useAccountStore } from '../store/account/store'; -import { AccountState, Mods, Account } from '../../types'; +import { useAccountStore } from '../store/account'; +import { + AccountState, + Mods, + Account, + CreateIdentityResponse, + ModifyPropertiesResponse, + ModifyPrefsResponse, + ModifyIdentityResponse, + RevokeRightsResponse, + DeleteIdentityResponse, + GrantRightsResponse +} from '../../types'; import { getXmlSoapFetch } from './fetch'; -export const editSettings = (mods: Mods, appId: string = SHELL_APP_ID): Promise => - getXmlSoapFetch(SHELL_APP_ID)( +type EditSettingsBatchResponse = { + ModifyPropertiesResponse?: ModifyPropertiesResponse[]; + ModifyPrefsResponse?: ModifyPrefsResponse[]; + ModifyIdentityResponse?: ModifyIdentityResponse[]; + DeleteIdentityResponse?: DeleteIdentityResponse[]; + CreateIdentityResponse?: CreateIdentityResponse[]; + RevokeRightsResponse?: RevokeRightsResponse[]; + GrantRightsResponse?: GrantRightsResponse[]; +}; + +export const editSettings = ( + mods: Partial, + appId: string = SHELL_APP_ID +): Promise => + getXmlSoapFetch(SHELL_APP_ID)( 'Batch', `${ mods.props @@ -93,11 +117,12 @@ export const editSettings = (mods: Mods, appId: string = SHELL_APP_ID): Promise< return ``; } if (mods.permissions.freeBusy.new.gt === 'usr') { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore return map( mods.permissions.freeBusy.new.d, (u) => + // FIXME: usage differs from the declaration of the AccountACE + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore `` ).join(''); } @@ -114,11 +139,12 @@ export const editSettings = (mods: Mods, appId: string = SHELL_APP_ID): Promise< return ``; } if (mods.permissions.inviteRight.new.gt === 'usr') { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore return map( mods.permissions.inviteRight.new.d, (u) => + // FIXME: usage differs from the declaration of the AccountACE + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore `` ).join(''); } @@ -129,7 +155,7 @@ export const editSettings = (mods: Mods, appId: string = SHELL_APP_ID): Promise< ` : '' }` - ).then((r: any) => { + ).then((r) => { useAccountStore.setState((s: AccountState) => ({ settings: { ...s.settings, @@ -220,8 +246,8 @@ export const editSettings = (mods: Mods, appId: string = SHELL_APP_ID): Promise< export const getEditSettingsForApp = (app: string) => - (mods: Mods): Promise => - editSettings(mods, app).then((r) => { - r.type = 'fulfilled'; - return r; - }); + (mods: Mods): Promise => + editSettings(mods, app).then((r) => ({ + ...r, + type: 'fulfilled' + })); diff --git a/src/reporting/feedback.tsx b/src/reporting/feedback.tsx index 2fcfcd24..92ffb38b 100644 --- a/src/reporting/feedback.tsx +++ b/src/reporting/feedback.tsx @@ -26,14 +26,14 @@ import React, { useState } from 'react'; import { TFunction } from 'react-i18next'; -import styled from 'styled-components'; +import styled, { DefaultTheme } from 'styled-components'; import { useUserAccount } from '../store/account'; import { useAppList } from '../store/app'; import { closeBoard } from '../store/boards'; import { getT } from '../store/i18n'; import { feedback } from './functions'; -const TextArea = styled.textarea<{ size?: string }>` +const TextArea = styled.textarea<{ size?: keyof DefaultTheme['sizes']['font'] }>` width: 100%; min-height: 8rem; box-sizing: border-box; diff --git a/src/search/search-bar.tsx b/src/search/search-bar.tsx index f08888fa..9397389d 100644 --- a/src/search/search-bar.tsx +++ b/src/search/search-bar.tsx @@ -18,7 +18,7 @@ import { filter, find, map, reduce } from 'lodash'; import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useHistory } from 'react-router-dom'; import styled from 'styled-components'; -import { SEARCH_APP_ID } from '../constants'; +import { LOCAL_STORAGE_SEARCH_KEY, SEARCH_APP_ID } from '../constants'; import { useLocalStorage } from '../shell/hooks'; import { QueryChip } from '../../types'; @@ -65,7 +65,7 @@ export const SearchBar: FC = () => { const inputRef = useRef(null); const t = getT(); const [storedValue, setStoredValue] = useLocalStorage( - 'search_suggestions', + LOCAL_STORAGE_SEARCH_KEY, [] ); const [inputTyped, setInputTyped] = useState(''); diff --git a/src/settings/components/date-picker-style.jsx b/src/settings/components/date-picker-style.tsx similarity index 93% rename from src/settings/components/date-picker-style.jsx rename to src/settings/components/date-picker-style.tsx index c689378e..beb3c81b 100644 --- a/src/settings/components/date-picker-style.jsx +++ b/src/settings/components/date-picker-style.tsx @@ -7,7 +7,7 @@ import { Container } from '@zextras/carbonio-design-system'; import styled from 'styled-components'; -const Styler = styled(Container)` +const Styler = styled(Container)<{ allDay?: boolean }>` .rw-btn, .rw-input-reset, .rw-input, @@ -26,31 +26,38 @@ const Styler = styled(Container)` -ms-touch-action: manipulation; touch-action: manipulation; } + .rw-btn::-moz-focus-inner { padding: 0; border: 0; } + select.rw-input { text-transform: none; } + html input[type='button'].rw-input { -webkit-appearance: button; cursor: pointer; } + textarea.rw-input { overflow: auto; resize: vertical; } + button[disabled].rw-input, fieldset[disabled] .rw-input, html input[disabled].rw-input { cursor: not-allowed; } + button.rw-input::-moz-focus-inner, input.rw-input::-moz-focus-inner { border: 0; padding: 0; } + input[type='checkbox'], input[type='radio'] { box-sizing: border-box; @@ -58,15 +65,16 @@ const Styler = styled(Container)` } ${ /* @font-face { - font-family: "RwWidgets"; - font-weight: normal; - font-style: normal; - ${/* src: url("../fonts/rw-widgets.eot?v=4.1.0"); - src: url("../fonts/rw-widgets.eot?#iefix&v=4.1.0") format("embedded-opentype"), - url("../fonts/rw-widgets.woff?v=4.1.0") format("woff"), - url("../fonts/rw-widgets.ttf?v=4.1.0") format("truetype"), - url("../fonts/rw-widgets.svg?v=4.1.0#fontawesomeregular") format("svg"); */ '' +font-family: "RwWidgets"; +font-weight: normal; +font-style: normal; +${/* src: url("../fonts/rw-widgets.eot?v=4.1.0"); +src: url("../fonts/rw-widgets.eot?#iefix&v=4.1.0") format("embedded-opentype"), +url("../fonts/rw-widgets.woff?v=4.1.0") format("woff"), +url("../fonts/rw-widgets.ttf?v=4.1.0") format("truetype"), + url("../fonts/rw-widgets.svg?v=4.1.0#fontawesomeregular") format("svg"); */ '' }; + .rw-i { display: inline-block; color: inherit; @@ -78,27 +86,35 @@ const Styler = styled(Container)` -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; } + .rw-i-caret-down:before { content: '\e803'; } + .rw-i-caret-up:before { content: '\e800'; } + .rw-i-chevron-left:before { content: '\f104'; } + .rw-i-chevron-right:before { content: '\f105'; } + .rw-i-clock-o:before { content: '\e805'; } + .rw-i-calendar:before { content: '\e804'; } + .rw-i-search:before { content: '\e801'; } + .rw-btn { position: relative; color: #333; @@ -109,27 +125,33 @@ const Styler = styled(Container)` cursor: pointer; outline: none; } + .rw-state-readonly .rw-btn, .rw-state-disabled .rw-btn { cursor: not-allowed; } + .rw-btn-select { opacity: 0.75; transition: opacity 150ms ease-in; } + .rw-btn-select:hover, .rw-state-focus .rw-btn-select, :hover > .rw-btn-select { opacity: 1; } + .rw-btn-primary { width: 100%; white-space: normal; line-height: 2rem; } + .rw-btn-primary:hover { background-color: #e6e6e6; } + .rw-btn-select[disabled], .rw-btn-primary[disabled], fieldset[disabled] .rw-btn-select, @@ -139,6 +161,7 @@ const Styler = styled(Container)` opacity: 0.65; pointer-events: none; } + .rw-sr { position: absolute; width: 0.0625rem; @@ -149,6 +172,7 @@ const Styler = styled(Container)` clip: rect(0, 0, 0, 0); border: 0; } + .rw-widget { background-clip: border-box; border: none; @@ -157,35 +181,42 @@ const Styler = styled(Container)` border-radius: 0.125rem 0.125rem 0 0; width: 100%; } + .rw-widget, .rw-widget * { box-sizing: border-box; } + .rw-widget:before, .rw-widget *:before, .rw-widget:after, .rw-widget *:after { box-sizing: border-box; } + .rw-widget > .rw-widget-container { width: 100%; margin: 0; } + .rw-widget-container { - background-color: ${({ theme }) => theme.palette.gray5.regular}; - border-bottom: ${({ theme }) => theme.palette.gray2.regular} 0.0625rem solid; + background-color: ${({ theme }): string => theme.palette.gray5.regular}; + border-bottom: ${({ theme }): string => theme.palette.gray2.regular} 0.0625rem solid; } + .rw-widget-container.rw-state-focus, .rw-state-focus > .rw-widget-container, .rw-widget-container.rw-state-focus:hover, .rw-state-focus > .rw-widget-container:hover { - background-color: ${({ theme }) => theme.palette.gray5.focus}; - border-bottom: ${({ theme }) => theme.palette.primary.regular} 0.0625rem solid; + background-color: ${({ theme }): string => theme.palette.gray5.focus}; + border-bottom: ${({ theme }): string => theme.palette.primary.regular} 0.0625rem solid; } + .rw-widget-container.rw-state-readonly, .rw-state-readonly > .rw-widget-container { cursor: not-allowed; } + .rw-widget-container.rw-state-disabled, .rw-state-disabled > .rw-widget-container, fieldset[disabled] .rw-widget-container, @@ -202,6 +233,7 @@ const Styler = styled(Container)` .rw-datetime-picker { height: 2.625rem; } + .rw-widget-picker { position: relative; overflow: hidden; @@ -209,6 +241,7 @@ const Styler = styled(Container)` display: inline-table; height: 100%; } + .rw-widget-picker > * { position: relative; border: none; @@ -217,48 +250,60 @@ const Styler = styled(Container)` height: 100%; display: table-cell; } + .rw-widget-picker > .rw-select { - width: ${({ allDay }) => (allDay ? '2.125rem' : '4.25rem')}; + width: ${({ allDay }): string => (allDay ? '2.125rem' : '4.25rem')}; white-space: nowrap; } + .rw-open > .rw-widget-picker { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } + .rw-open-up > .rw-widget-picker { border-top-right-radius: 0; border-top-left-radius: 0; } + fieldset[disabled] .rw-widget-picker, .rw-state-disabled > .rw-widget-picker { background-color: #eee; } + .rw-multiselect > .rw-widget-picker { height: auto; } + .rw-select { cursor: pointer; } + .rw-select > * { width: 2rem; height: 100%; } + .rw-state-readonly .rw-select, .rw-state-disabled .rw-select { cursor: not-allowed; } + .rw-select-bordered { cursor: pointer; border: none; border-left: #ccc 0.0625rem solid; } + .rw-select-bordered:hover, .rw-select-bordered:active { background-color: #e6e6e6; } + .rw-select-bordered:active { box-shadow: inset 0 0.1875rem 0.3125rem rgba(0, 0, 0, 0.125); } + .rw-state-disabled .rw-select-bordered, .rw-state-readonly .rw-select-bordered, fieldset[disabled] .rw-select-bordered, @@ -273,37 +318,46 @@ const Styler = styled(Container)` background-image: none; box-shadow: none; } + .rw-rtl .rw-select-bordered { border-right: #ccc 0.0625rem solid; border-left: none; } + .rw-rtl { direction: rtl; } + .rw-input-reset, .rw-input, .rw-dropdown-list-autofill, .rw-filter-input { outline: 0; } + .rw-input-reset::-moz-placeholder { color: #999; opacity: 1; } + .rw-input-reset:-ms-input-placeholder { color: #999; } + .rw-input-reset::-webkit-input-placeholder { color: #999; } + .rw-input, .rw-dropdown-list-autofill, .rw-filter-input { padding: 0 0.857rem; } + .rw-input[type='text']::-ms-clear { display: none; } + .rw-input[disabled], fieldset[disabled] .rw-input { box-shadow: none; @@ -312,9 +366,11 @@ const Styler = styled(Container)` background-color: #eee; border-color: #ccc; } + .rw-input[readonly] { cursor: not-allowed; } + .rw-i.rw-loading { display: block; ${/* background: url("../img/loading.gif") no-repeat center; */ ''}; @@ -322,32 +378,40 @@ const Styler = styled(Container)` width: 1.9rem; height: 1rem; } + .rw-i.rw-loading:before { content: ''; } + .rw-placeholder { color: #999; } + .rw-detect-autofill:-webkit-autofill { animation-name: react-widgets-autofill-start; transition: background-color 50000s ease-in-out 0s; } + .rw-detect-autofill:not(:-webkit-autofill) { animation-name: react-widgets-autofill-cancel; } + .rw-webkit-autofill .rw-widget-container, .rw-input:-webkit-autofill { background-color: #faffbd !important; background-image: none !important; color: #000 !important; } + .rw-widget-input, .rw-filter-input { } + .rw-widget-input.rw-state-focus { box-shadow: 0 0 0.5rem rgba(102, 175, 233, 0.6), inset 0 0.0625rem 0.0625rem rgba(0, 0, 0, 0.075); } + .rw-list { margin: 0; padding: 0; @@ -357,6 +421,7 @@ const Styler = styled(Container)` overflow: auto; max-height: 12.5rem; } + .rw-list-option { -ms-user-select: none; user-select: none; @@ -364,24 +429,28 @@ const Styler = styled(Container)` cursor: pointer; border: 0.0625rem solid transparent; } + .rw-list-option.rw-state-focus, .rw-list-option.rw-state-focus:hover { background-color: transparent; border-color: #66afe9; color: #333; } + .rw-list-option:hover, .rw-list-option:hover.rw-state-focus { background-color: #e6e6e6; border-color: #e6e6e6; color: #333; } + .rw-list-option.rw-state-selected, .rw-list-option.rw-state-selected:hover { background-color: #337ab7; border-color: #337ab7; color: white; } + fieldset[disabled] .rw-list-option, .rw-list-option.rw-state-disabled, .rw-list-option.rw-state-readonly { @@ -390,28 +459,34 @@ const Styler = styled(Container)` color: #999; opacity: 0.7; } + fieldset[disabled] .rw-list-option:hover, .rw-list-option.rw-state-disabled:hover, .rw-list-option.rw-state-readonly:hover { background: none; border-color: transparent; } + .rw-list-empty, .rw-list-option, .rw-list-optgroup { padding: 0.143rem 0.75rem; outline: 0; } + .rw-list-optgroup { font-weight: bold; padding-top: 0.4375rem; } + .rw-list-option-create { border-top: 0.0625rem #ccc solid; } + .rw-dropdown-list-autofill { padding: 0; } + .rw-dropdown-list-input { background-color: transparent; vertical-align: middle; @@ -421,24 +496,29 @@ const Styler = styled(Container)` white-space: nowrap; overflow: hidden; } + .rw-rtl .rw-dropdown-list-input { padding-right: 0.857rem; padding-left: 0; } + .rw-filter-input { position: relative; margin: 0.25rem; padding-right: 0; } + .rw-filter-input .rw-rtl { padding-right: 0.857rem; padding-left: 0; } + .rw-filter-input .rw-select, .rw-filter-input .rw-btn { opacity: 0.75; cursor: text; } + .rw-filter-input > .rw-select, .rw-filter-input > .rw-select:active, .rw-filter-input > .rw-select:hover { @@ -452,8 +532,8 @@ const Styler = styled(Container)` height: calc(1.2145rem - 0.0625rem); ${ /* - margin-top: -0.0625rem\9; - height: 1.2145rem\9; + margin-top: -0.0625rem\9; +height: 1.2145rem\9; */ 'border: 0.125rem solid red;' }; line-height: 1.2145rem; @@ -466,9 +546,11 @@ const Styler = styled(Container)` .rw-number-picker .rw-btn:active { background-color: #e6e6e6; } + .rw-number-picker .rw-btn:active { box-shadow: inset 0 0.1875rem 0.3125rem rgba(0, 0, 0, 0.125); } + .rw-state-disabled .rw-number-picker .rw-btn, .rw-state-readonly .rw-number-picker .rw-btn, fieldset[disabled] .rw-number-picker .rw-btn, @@ -483,41 +565,51 @@ const Styler = styled(Container)` background-image: none; box-shadow: none; } + .rw-number-picker .rw-select { vertical-align: middle; } + .rw-number-picker .rw-select, .rw-number-picker .rw-select:hover, .rw-number-picker .rw-select:active { box-shadow: none; } + .rw-calendar-popup { right: auto; min-width: 0; width: 18rem; } + .rw-calendar { border-radius: 0.25rem; background-color: #fff; border: #ccc 0.0625rem solid; overflow: hidden; } + .rw-calendar.rw-popup { border-color: #ccc; } + .rw-calendar-now { font-weight: bold; } + .rw-calendar-btn-left, .rw-calendar-btn-right { width: 12.5%; } + .rw-calendar-btn-view { width: 75%; } + .rw-calendar-footer { border-top: 0.0625rem solid #ccc; } + .rw-calendar-grid { outline: none; height: 14.28571429rem; @@ -527,11 +619,13 @@ const Styler = styled(Container)` width: 100%; background-color: #fff; } + .rw-head-cell { text-align: center; border-bottom: 0.0625rem solid #ccc; padding: 0.25rem; } + .rw-cell { color: #333; border-radius: 0.25rem; @@ -541,60 +635,74 @@ const Styler = styled(Container)` border: 0.0625rem solid transparent; padding: 0.25rem; } + .rw-cell:hover { background-color: #e6e6e6; border-color: #e6e6e6; color: #333; } + .rw-cell.rw-state-focus, .rw-cell.rw-state-focus:hover { background-color: transparent; border-color: #66afe9; color: #333; } + .rw-cell.rw-state-selected, .rw-cell.rw-state-selected:hover { background-color: #337ab7; border-color: #337ab7; color: white; } + .rw-cell.rw-state-disabled { color: #999; opacity: 0.7; } + .rw-cell.rw-state-disabled:hover { background: none; border-color: transparent; } + .rw-calendar-month .rw-cell { text-align: center; } + .rw-cell-off-range { color: #999; } + .rw-calendar-transition-group { position: relative; } + .rw-calendar-transition { transition: transform 300ms; overflow: hidden; } + .rw-calendar-transition-top { -ms-transform: translateY(-100%); transform: translateY(-100%); } + .rw-calendar-transition-bottom { -ms-transform: translateY(100%); transform: translateY(100%); } + .rw-calendar-transition-right { -ms-transform: translateX(-100%); transform: translateX(-100%); } + .rw-calendar-transition-left { -ms-transform: translateX(100%); transform: translateX(100%); } + .rw-calendar-transition-entering.rw-calendar-transition-top, .rw-calendar-transition-entered.rw-calendar-transition-top, .rw-calendar-transition-entering.rw-calendar-transition-bottom, @@ -602,6 +710,7 @@ const Styler = styled(Container)` -ms-transform: translateY(0); transform: translateY(0); } + .rw-calendar-transition-entering.rw-calendar-transition-right, .rw-calendar-transition-entered.rw-calendar-transition-right, .rw-calendar-transition-entering.rw-calendar-transition-left, @@ -609,30 +718,37 @@ const Styler = styled(Container)` -ms-transform: translateX(0); transform: translateX(0); } + .rw-calendar-transition-exiting.rw-calendar-transition-top { -ms-transform: translateY(100%); transform: translateY(100%); } + .rw-calendar-transition-exiting.rw-calendar-transition-bottom { -ms-transform: translateY(-100%); transform: translateY(-100%); } + .rw-calendar-transition-exiting.rw-calendar-transition-right { -ms-transform: translateX(100%); transform: translateX(100%); } + .rw-calendar-transition-exiting.rw-calendar-transition-left { -ms-transform: translateX(-100%); transform: translateX(-100%); } + .rw-select-list { overflow: auto; position: relative; } + .rw-select-list .rw-list { max-height: none; font-size: 1rem; } + .rw-select-list-label { display: block; position: relative; @@ -641,10 +757,12 @@ const Styler = styled(Container)` padding-left: 1.25rem; margin: 0; } + .rw-rtl .rw-select-list-label { padding-left: 0; padding-right: 1.25rem; } + input.rw-select-list-input { position: absolute; left: 0; @@ -656,10 +774,12 @@ const Styler = styled(Container)` line-height: normal; cursor: inherit; } + .rw-rtl input.rw-select-list-input { left: auto; right: 0; } + .rw-loading-mask { content: ''; ${/* background: url("../img/loader-big.gif") no-repeat center; */ ''}; @@ -672,29 +792,34 @@ const Styler = styled(Container)` height: 100%; width: 100%; } + .rw-multiselect { cursor: text; } + .rw-multiselect .rw-input-reset { height: calc(2.429rem - 0.125rem); ${ /* margin-top: -0.125rem\9; - height: 2.429rem\9; */ '' +height: 2.429rem\9; */ '' }; border-width: 0; width: auto; max-width: 100%; padding: 0 0.857rem; } + .rw-multiselect .rw-select { vertical-align: middle; } + .rw-multiselect .rw-select, .rw-multiselect .rw-select:hover, .rw-multiselect .rw-select:active { box-shadow: none; background: none; } + .rw-multiselect-taglist { margin: 0; padding: 0; @@ -703,6 +828,7 @@ const Styler = styled(Container)` vertical-align: 0; outline: none; } + .rw-multiselect-tag { display: inline-table; color: inherit; @@ -720,22 +846,26 @@ const Styler = styled(Container)` overflow: hidden; max-width: 100%; } + .rw-multiselect-tag > * { display: table-cell; vertical-align: middle; height: 100%; } + .rw-rtl .rw-multiselect-tag { margin-left: 0; margin-right: calc(0.279335rem - 0.0625rem); padding: 0 0.35rem 0 0.35rem; } + .rw-multiselect-tag.rw-state-focus, .rw-multiselect-tag.rw-state-focus:hover { background-color: transparent; border-color: #66afe9; color: #333; } + .rw-multiselect-tag.rw-state-readonly, .rw-multiselect-tag.rw-state-disabled, .rw-state-readonly .rw-multiselect-tag, @@ -743,23 +873,28 @@ const Styler = styled(Container)` fieldset[disabled] .rw-multiselect-tag { cursor: not-allowed; } + .rw-multiselect-tag.rw-state-disabled, .rw-state-disabled .rw-multiselect-tag, fieldset[disabled] .rw-multiselect-tag { opacity: 0.65; } + fieldset[disabled] .rw-multiselect-tag { box-shadow: none; cursor: not-allowed; } + .rw-multiselect-tag-btn { color: inherit; margin-left: 0.25rem; } + .rw-rtl .rw-multiselect-tag-btn { margin-left: 0; margin-right: 0.25rem; } + .rw-autocomplete .rw-select { position: absolute; display: block; @@ -768,6 +903,7 @@ const Styler = styled(Container)` bottom: 0; right: 0; } + .rw-popup-container { position: absolute; z-index: 1005; @@ -775,22 +911,27 @@ const Styler = styled(Container)` left: -0.375rem; right: -0.375rem; } + .rw-popup-container.rw-dropup { top: auto; bottom: 100%; } + .rw-state-focus .rw-popup-container { z-index: 1006; } + .rw-popup-transition { width: 100%; margin-bottom: 0.375rem; padding: 0 0.375rem; } + .rw-dropup > .rw-popup-transition { margin-bottom: 0; margin-top: 0.375rem; } + .rw-popup { border-top-right-radius: 0; border-top-left-radius: 0; @@ -800,6 +941,7 @@ const Styler = styled(Container)` border: #ccc 0.0625rem solid; background: #fff; } + .rw-dropup .rw-popup { border-bottom-right-radius: 0; border-bottom-left-radius: 0; @@ -807,46 +949,55 @@ const Styler = styled(Container)` border-top-left-radius: 0.1875rem; box-shadow: 0 -0.125rem 0.375rem rgba(0, 0, 0, 0.2); } + .rw-popup-transition { transition: transform 200ms; } + .rw-popup-transition-entering { overflow: hidden; } + .rw-popup-transition-entering .rw-popup-transition { -ms-transform: translateY(0); transform: translateY(0); transition-timing-function: ease-out; } + .rw-popup-transition-exiting .rw-popup-transition { transition-timing-function: ease-in; } + .rw-popup-transition-exiting, .rw-popup-transition-exited { overflow: hidden; } + .rw-popup-transition-exiting .rw-popup-transition, .rw-popup-transition-exited .rw-popup-transition { -ms-transform: translateY(-100%); transform: translateY(-100%); } + .rw-popup-transition-exiting.rw-dropup .rw-popup-transition, .rw-popup-transition-exited.rw-dropup .rw-popup-transition { -ms-transform: translateY(100%); transform: translateY(100%); } + .rw-popup-transition-exited { display: none; } + .rw-state-disabled { box-shadow: none; cursor: not-allowed; } - color: ${({ theme }) => theme.palette.text.regular}; - font-family: ${({ theme }) => theme.fonts.default}; - font-size: ${({ theme }) => theme.sizes.font.medium}; - font-weight: ${({ theme }) => theme.fonts.weight.regular}; + color: ${({ theme }): string => theme.palette.text.regular}; + font-family: ${({ theme }): string => theme.fonts.default}; + font-size: ${({ theme }): string => theme.sizes.font.medium}; + font-weight: ${({ theme }): number => theme.fonts.weight.regular}; `; export default Styler; diff --git a/src/settings/components/date-time-select-view.tsx b/src/settings/components/date-time-select-view.tsx index 40cd78b1..b47bcc9f 100644 --- a/src/settings/components/date-time-select-view.tsx +++ b/src/settings/components/date-time-select-view.tsx @@ -9,15 +9,10 @@ import React, { FC, useCallback, useEffect, useState } from 'react'; import moment from 'moment'; import DateTimePicker from 'react-widgets/lib/DateTimePicker'; -// eslint-disable-next-line import/no-extraneous-dependencies import momentLocalizer from 'react-widgets-moment'; -import { AccountSettings } from '../../../types'; +import { AccountSettings, AddMod, PrefsMods } from '../../../types'; import Heading from './settings-heading'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore import Styler from './date-picker-style'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore import { getT } from '../../store/i18n'; import { changeDateEvent, endOfDate, getDateEvent, startOfDate } from './utils'; @@ -25,7 +20,7 @@ momentLocalizer(); const DateTimeSelect: FC<{ settings: AccountSettings; - addMod: (type: 'prefs' | 'props', key: string, value: { value: any; app: string }) => void; + addMod: AddMod; sendAutoReply: boolean; }> = ({ settings, addMod, sendAutoReply }) => { const t = getT(); @@ -47,8 +42,8 @@ const DateTimeSelect: FC<{ const [allDayDisabled, setAllDayDisabled] = useState(false); const [timeDisabled, setTimeDisabled] = useState(false); const updatePrefs = useCallback( - (v, p) => { - addMod('prefs', p, v); + (prefValue: PrefsMods[keyof PrefsMods], prefKey: keyof PrefsMods) => { + addMod('prefs', prefKey, prefValue); }, [addMod] ); diff --git a/src/settings/components/general-settings/appearance-settings.tsx b/src/settings/components/general-settings/appearance-settings.tsx index ba962bbc..8e0b2eae 100644 --- a/src/settings/components/general-settings/appearance-settings.tsx +++ b/src/settings/components/general-settings/appearance-settings.tsx @@ -4,74 +4,35 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { FormSubSection, Select, SelectProps } from '@zextras/carbonio-design-system'; -import { find } from 'lodash'; -import React, { FC, useCallback, useContext, useMemo } from 'react'; -import { ThemeCallbacksContext } from '../../../boot/theme-provider'; -import { SHELL_APP_ID } from '../../../constants'; +import { Container, Text } from '@zextras/carbonio-design-system'; +import React, { useMemo } from 'react'; import { getT } from '../../../store/i18n'; -import { themeSubSection } from '../../general-settings-sub-sections'; -import { useDarkReaderResultValue } from '../../../custom-hooks/useDarkReaderResultValue'; -import { DarkReaderPropValues, isDarkReaderPropValues } from '../../../../types'; +import { appearanceSubSection } from '../../general-settings-sub-sections'; -const AppearanceSettings: FC<{ - addMod: (type: 'prefs' | 'props', key: string, value: { value: any; app: string }) => void; - removeMod: (type: 'prefs' | 'props', key: string) => void; -}> = ({ addMod, removeMod }) => { - const { setDarkReaderState } = useContext(ThemeCallbacksContext); - - const darkReaderResultValue = useDarkReaderResultValue(); +interface AppearanceSettingsProps { + children: React.ReactElement | React.ReactElement[]; +} +const AppearanceSettings = ({ children }: AppearanceSettingsProps): JSX.Element => { const t = getT(); - const items = useMemo>( - () => [ - { - label: t('settings.general.theme_auto', 'Auto'), - value: 'auto' - }, - { - label: t('settings.general.theme_enabled', 'Enabled'), - value: 'enabled' - }, - { - label: t('settings.general.theme_disabled', 'Disabled'), - value: 'disabled' - } - ], - [t] - ); - const defaultSelection = useMemo( - () => find(items, { value: darkReaderResultValue }), - [darkReaderResultValue, items] - ); - const onSelectionChange = useCallback>( - (value) => { - if (isDarkReaderPropValues(value)) { - setDarkReaderState(value); - if (value !== darkReaderResultValue) { - addMod('props', 'zappDarkreaderMode', { app: SHELL_APP_ID, value }); - } else { - removeMod('props', 'zappDarkreaderMode'); - } - } - }, - [addMod, darkReaderResultValue, removeMod, setDarkReaderState] - ); - const subSection = useMemo(() => themeSubSection(t), [t]); + const subSection = useMemo(() => appearanceSubSection(t), [t]); return ( - - + + ); +}; + +export default DarkThemeSettingSection; diff --git a/src/settings/components/general-settings/out-of-office-view.tsx b/src/settings/components/general-settings/out-of-office-view.tsx index 31cfe76b..5b564307 100644 --- a/src/settings/components/general-settings/out-of-office-view.tsx +++ b/src/settings/components/general-settings/out-of-office-view.tsx @@ -13,16 +13,11 @@ import { } from '@zextras/carbonio-design-system'; import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; -// eslint-disable-next-line import/no-extraneous-dependencies import { find } from 'lodash'; import momentLocalizer from 'react-widgets-moment'; -import { AccountSettings } from '../../../../types'; +import { AccountSettings, AddMod, PrefsMods } from '../../../../types'; import Heading from '../settings-heading'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore import DateTimeSelect from '../date-time-select-view'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore import { getT } from '../../../store/i18n'; import { outOfOfficeSubSection } from '../../general-settings-sub-sections'; import { @@ -54,7 +49,7 @@ momentLocalizer(); const OutOfOfficeView: FC<{ settings: AccountSettings; - addMod: (type: 'prefs' | 'props', key: string, value: { value: any; app: string }) => void; + addMod: AddMod; }> = ({ settings, addMod }) => { const t = getT(); const [sendAutoReply, setSendAutoReply] = useState( @@ -75,8 +70,8 @@ const OutOfOfficeView: FC<{ const [createAppointment, setCreateAppointment] = useState(true); const updatePrefs = useCallback( - (v, p) => { - addMod('prefs', p, v); + (prefValue: PrefsMods[keyof PrefsMods], prefKey: keyof PrefsMods) => { + addMod('prefs', prefKey, prefValue); }, [addMod] ); diff --git a/src/settings/components/general-settings/scaling-setting-section.tsx b/src/settings/components/general-settings/scaling-setting-section.tsx new file mode 100644 index 00000000..bd69ff08 --- /dev/null +++ b/src/settings/components/general-settings/scaling-setting-section.tsx @@ -0,0 +1,298 @@ +/* + * SPDX-FileCopyrightText: 2022 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { useTranslation } from 'react-i18next'; +import React, { useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react'; +import { + Button, + Checkbox, + Container, + Slider, + SliderProps, + Text, + Tooltip +} from '@zextras/carbonio-design-system'; +import styled, { SimpleInterpolation } from 'styled-components'; +import { ScalingSettings } from '../../../../types/settings'; +import { BASE_FONT_SIZE, SCALING_OPTIONS } from '../../../constants'; +import { getAutoScalingFontSize } from '../utils'; + +const ScalingSliderContainer = styled(Container)` + box-shadow: 0px 0px 4px rgba(166, 166, 166, 0.5); + border-radius: 0.5rem; +`; + +const ExampleContainer = styled(Container)<{ $fontSize: number | undefined }>` + font-size: ${({ $fontSize }): SimpleInterpolation => $fontSize && `${$fontSize}%`}; +`; + +const ExampleText = styled(Text)` + font-size: 1em; /* font-size needs to be relative to ExampleContainer */ +`; + +interface ScalingSettingSectionProps { + scalingSettings: ScalingSettings; + addLocalStoreChange: ( + key: keyof ScalingSettings, + value: ScalingSettings[keyof ScalingSettings] + ) => void; + cleanLocalStoreChange: (key: keyof ScalingSettings) => void; +} + +export type ScalingSettingSectionRef = { reset: () => void }; + +const BASE_FONT_OPTION_INDEX = SCALING_OPTIONS.findIndex( + (option) => option.value === BASE_FONT_SIZE +); + +const AUTOSCALING_FONT_SIZE = getAutoScalingFontSize(); +const AUTOSCALING_FONT_OPTION_INDEX = SCALING_OPTIONS.findIndex( + (option) => option.value === AUTOSCALING_FONT_SIZE +); + +export const ScalingSettingSection = React.forwardRef< + ScalingSettingSectionRef, + ScalingSettingSectionProps +>(function ScalingSettingSectionFn( + { scalingSettings, addLocalStoreChange, cleanLocalStoreChange }: ScalingSettingSectionProps, + ref +) { + const [t] = useTranslation(); + + const [scalingOptionValues, scalingOptionLabels] = useMemo<[number[], string[]]>( + () => + SCALING_OPTIONS.reduce<[number[], string[]]>( + ([values, labels], option) => { + values.push(option.value); + labels.push(t('settings.appearance.option', { context: option.label })); + return [values, labels]; + }, + [[], []] + ), + [t] + ); + + const savedOptionIndex = useMemo(() => { + const savedScalingValueSetting = scalingSettings['settings.appearance_setting.scaling']; + if (savedScalingValueSetting !== undefined) { + const optionIndex = scalingOptionValues.findIndex( + (option) => option === savedScalingValueSetting + ); + if (optionIndex >= 0) { + return optionIndex; + } + } + return AUTOSCALING_FONT_OPTION_INDEX; + }, [scalingOptionValues, scalingSettings]); + + const [autoScaling, setAutoScaling] = useState( + scalingSettings['settings.appearance_setting.auto'] ?? true + ); + const [scalingValue, setScalingValue] = useState(savedOptionIndex); + + useEffect(() => { + // update input value when setting is updated + setScalingValue(savedOptionIndex); + }, [savedOptionIndex]); + + useEffect(() => { + if (scalingOptionValues[scalingValue]) { + if (scalingValue !== savedOptionIndex) { + addLocalStoreChange( + 'settings.appearance_setting.scaling', + scalingOptionValues[scalingValue] + ); + } else { + cleanLocalStoreChange('settings.appearance_setting.scaling'); + } + } + }, [ + addLocalStoreChange, + cleanLocalStoreChange, + savedOptionIndex, + scalingOptionValues, + scalingSettings, + scalingValue + ]); + + useEffect(() => { + if ( + (scalingSettings['settings.appearance_setting.auto'] !== undefined && + autoScaling !== scalingSettings['settings.appearance_setting.auto']) || + (scalingSettings['settings.appearance_setting.auto'] === undefined && !autoScaling) + ) { + addLocalStoreChange('settings.appearance_setting.auto', autoScaling); + if (autoScaling) { + addLocalStoreChange('settings.appearance_setting.scaling', undefined); + } + } else { + cleanLocalStoreChange('settings.appearance_setting.auto'); + } + }, [addLocalStoreChange, autoScaling, cleanLocalStoreChange, scalingSettings]); + + const increaseScalingByStep = useCallback(() => { + setScalingValue((prevState) => + prevState < SCALING_OPTIONS.length - 1 ? prevState + 1 : prevState + ); + }, []); + + const decreaseScalingByStep = useCallback(() => { + setScalingValue((prevState) => (prevState > 0 ? prevState - 1 : prevState)); + }, []); + + const toggleAutoScaling = useCallback(() => { + setAutoScaling((prevState) => !prevState); + setScalingValue(savedOptionIndex); + }, [savedOptionIndex]); + + const onChangeScalingValue = useCallback>( + (ev, newValueIndex) => { + setScalingValue(newValueIndex); + }, + [] + ); + + useImperativeHandle(ref, () => ({ + reset: (): void => { + setScalingValue(savedOptionIndex); + setAutoScaling(scalingSettings['settings.appearance_setting.auto'] ?? true); + } + })); + + const exampleText = useMemo( + () => + t( + 'settings.appearance.labels.choose_size_pangram', + 'The quick brown fox jumps over the lazy dog.' + ), + [t] + ); + + const exampleFontSize = useMemo( + () => + // Revert the percentage and then apply the new value to use the browser base font-size and not the html font-size rule + // Explanation: + // - reverting factor (factor to apply to current font to obtain the browser font size) = unsaved input value / local storage value + // - new font size percentage = reverting factor * 100 + // The resulting value is the percentage to apply to current html font-size to obtain the same calculated value + // as if the scalingValue was set in the html. + // E.g.: html: 75%, scalingValue: 100 -> 1 rem is 12px (because of the 75%). In the browser the base font is set to 16px. + // To make scalingValue 100 equals to 16px, we need to get the reverting factor (100 / 75 = 1.3333). + // This is the factor required to have 16 starting from 12 (12*1.3333 ~= 16) + // The percentage to apply to the example container is then 1.3333 * 100 = 133.33 (133.33%(75) ~= 100 ~= 16px) + ((scalingOptionValues[Math.round(scalingValue)] || 100) / + scalingOptionValues[savedOptionIndex]) * + 100, + [savedOptionIndex, scalingOptionValues, scalingValue] + ); + + return ( + + + + {t( + 'settings.appearance.labels.choose_size_description', + 'Choose Type Size and Styles for Carbonio Environment' + )} + + + + +