fix: move encrypted icon to excalidraw-app add separate animation for renderFooter prop (#3577)

* fix: move encrypted icon to excalidraw-app

* use grid & separate animation for custom footer

* update docs

* fix
This commit is contained in:
Aakansha Doshi 2021-05-15 14:49:58 +05:30 committed by GitHub
parent 78da4c075e
commit 6271a031a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 133 additions and 103 deletions

View File

@ -40,39 +40,17 @@
.layer-ui__wrapper { .layer-ui__wrapper {
z-index: var(--zIndex-layerUI); z-index: var(--zIndex-layerUI);
.encrypted-icon {
position: relative;
margin-inline-start: 15px;
display: flex;
justify-content: center;
align-items: center;
border-radius: var(--space-factor);
color: $oc-green-9;
svg {
width: 1.2rem;
height: 1.2rem;
}
}
&__top-right { &__top-right {
display: flex; display: flex;
} }
&__footer { &__footer {
position: absolute; width: 100%;
&-right {
z-index: 100; z-index: 100;
bottom: 0; display: flex;
:root[dir="ltr"] & {
right: 0;
} }
:root[dir="rtl"] & {
left: 0;
}
width: 190px;
} }
.zen-mode-transition { .zen-mode-transition {
@ -94,12 +72,16 @@
transform: translate(-999px, 0); transform: translate(-999px, 0);
} }
:root[dir="ltr"] &.App-menu_bottom--transition-left { :root[dir="ltr"] &.layer-ui__wrapper__footer-left--transition-left {
transform: translate(-92px, 0); transform: translate(-92px, 0);
} }
:root[dir="rtl"] &.App-menu_bottom--transition-left { :root[dir="rtl"] &.layer-ui__wrapper__footer-left--transition-left {
transform: translate(92px, 0); transform: translate(92px, 0);
} }
&.layer-ui__wrapper__footer-left--transition-bottom {
transform: translate(0, 92px);
}
} }
.disable-zen-mode { .disable-zen-mode {

View File

@ -31,7 +31,7 @@ import { ErrorDialog } from "./ErrorDialog";
import { ExportCB, ExportDialog } from "./ExportDialog"; import { ExportCB, ExportDialog } from "./ExportDialog";
import { FixedSideContainer } from "./FixedSideContainer"; import { FixedSideContainer } from "./FixedSideContainer";
import { HintViewer } from "./HintViewer"; import { HintViewer } from "./HintViewer";
import { exportFile, load, shield, trash } from "./icons"; import { exportFile, load, trash } from "./icons";
import { Island } from "./Island"; import { Island } from "./Island";
import "./LayerUI.scss"; import "./LayerUI.scss";
import { LibraryUnit } from "./LibraryUnit"; import { LibraryUnit } from "./LibraryUnit";
@ -68,7 +68,7 @@ interface LayerUIProps {
canvas: HTMLCanvasElement | null, canvas: HTMLCanvasElement | null,
) => void; ) => void;
renderTopRightUI?: (isMobile: boolean, appState: AppState) => JSX.Element; renderTopRightUI?: (isMobile: boolean, appState: AppState) => JSX.Element;
renderCustomFooter?: (isMobile: boolean) => JSX.Element; renderCustomFooter?: (isMobile: boolean, appState: AppState) => JSX.Element;
viewModeEnabled: boolean; viewModeEnabled: boolean;
libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"]; libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
UIOptions: AppProps["UIOptions"]; UIOptions: AppProps["UIOptions"];
@ -382,22 +382,6 @@ const LayerUI = ({
}: LayerUIProps) => { }: LayerUIProps) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const renderEncryptedIcon = () => (
<a
className={clsx("encrypted-icon tooltip zen-mode-visibility", {
"zen-mode-visibility--hidden": zenModeEnabled,
})}
href="https://blog.excalidraw.com/end-to-end-encryption/"
target="_blank"
rel="noopener noreferrer"
aria-label={t("encrypted.link")}
>
<Tooltip label={t("encrypted.tooltip")} position="above" long={true}>
{shield}
</Tooltip>
</a>
);
const renderExportDialog = () => { const renderExportDialog = () => {
if (!UIOptions.canvasActions.export) { if (!UIOptions.canvasActions.export) {
return null; return null;
@ -634,10 +618,17 @@ const LayerUI = ({
const renderBottomAppMenu = () => { const renderBottomAppMenu = () => {
return ( return (
<footer
role="contentinfo"
className="layer-ui__wrapper__footer App-menu App-menu_bottom"
>
<div <div
className={clsx("App-menu App-menu_bottom zen-mode-transition", { className={clsx(
"App-menu_bottom--transition-left": zenModeEnabled, "layer-ui__wrapper__footer-left zen-mode-transition",
})} {
"layer-ui__wrapper__footer-left--transition-left": zenModeEnabled,
},
)}
> >
<Stack.Col gap={2}> <Stack.Col gap={2}>
<Section heading="canvasActions"> <Section heading="canvasActions">
@ -647,21 +638,27 @@ const LayerUI = ({
zoom={appState.zoom} zoom={appState.zoom}
/> />
</Island> </Island>
{renderEncryptedIcon()}
</Section> </Section>
</Stack.Col> </Stack.Col>
</div> </div>
);
};
const renderFooter = () => (
<footer role="contentinfo" className="layer-ui__wrapper__footer">
<div <div
className={clsx("zen-mode-transition", { className={clsx(
"transition-right disable-pointerEvents": zenModeEnabled, "layer-ui__wrapper__footer-center zen-mode-transition",
})} {
"layer-ui__wrapper__footer-left--transition-bottom": zenModeEnabled,
},
)}
>
{renderCustomFooter?.(false, appState)}
</div>
<div
className={clsx(
"layer-ui__wrapper__footer-right zen-mode-transition",
{
"transition-right disable-pointerEvents": zenModeEnabled,
},
)}
> >
{renderCustomFooter?.(false)}
{actionManager.renderAction("toggleShortcuts")} {actionManager.renderAction("toggleShortcuts")}
</div> </div>
<button <button
@ -674,6 +671,7 @@ const LayerUI = ({
</button> </button>
</footer> </footer>
); );
};
const dialogs = ( const dialogs = (
<> <>
@ -737,7 +735,6 @@ const LayerUI = ({
{dialogs} {dialogs}
{renderFixedSideContainer()} {renderFixedSideContainer()}
{renderBottomAppMenu()} {renderBottomAppMenu()}
{renderFooter()}
{appState.scrolledOutside && ( {appState.scrolledOutside && (
<button <button
className="scroll-back-to-content" className="scroll-back-to-content"

View File

@ -28,7 +28,7 @@ type MobileMenuProps = {
onLockToggle: () => void; onLockToggle: () => void;
canvas: HTMLCanvasElement | null; canvas: HTMLCanvasElement | null;
isCollaborating: boolean; isCollaborating: boolean;
renderCustomFooter?: (isMobile: boolean) => JSX.Element; renderCustomFooter?: (isMobile: boolean, appState: AppState) => JSX.Element;
viewModeEnabled: boolean; viewModeEnabled: boolean;
showThemeBtn: boolean; showThemeBtn: boolean;
}; };
@ -155,7 +155,7 @@ export const MobileMenu = ({
<div className="panelColumn"> <div className="panelColumn">
<Stack.Col gap={4}> <Stack.Col gap={4}>
{renderCanvasActions()} {renderCanvasActions()}
{renderCustomFooter?.(true)} {renderCustomFooter?.(true, appState)}
{appState.collaborators.size > 0 && ( {appState.collaborators.size > 0 && (
<fieldset> <fieldset>
<legend>{t("labels.collaborators")}</legend> <legend>{t("labels.collaborators")}</legend>

View File

@ -332,8 +332,8 @@
.App-menu_bottom { .App-menu_bottom {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
grid-template-columns: 1fr auto 1fr; grid-template-columns: min-content auto min-content;
grid-gap: 4px; grid-gap: 15px;
align-items: flex-start; align-items: flex-start;
cursor: default; cursor: default;
pointer-events: none !important; pointer-events: none !important;
@ -419,13 +419,11 @@
} }
&.dropdown-select--floating { &.dropdown-select--floating {
position: absolute;
margin: 0.5em; margin: 0.5em;
} }
} }
.dropdown-select__language.dropdown-select--floating { .dropdown-select__language.dropdown-select--floating {
position: absolute;
bottom: 10px; bottom: 10px;
:root[dir="ltr"] & { :root[dir="ltr"] & {
@ -461,13 +459,12 @@
} }
.help-icon { .help-icon {
position: absolute;
cursor: pointer; cursor: pointer;
fill: $oc-gray-6; fill: $oc-gray-6;
bottom: 14px;
width: 1.5rem; width: 1.5rem;
padding: 0; padding: 0;
margin: 0; margin: 0;
margin-top: 5px;
background: none; background: none;
color: var(--icon-fill-color); color: var(--icon-fill-color);
@ -476,11 +473,11 @@
} }
:root[dir="ltr"] & { :root[dir="ltr"] & {
right: 14px; margin-right: 14px;
} }
:root[dir="rtl"] & { :root[dir="rtl"] & {
left: 14px; margin-left: 14px;
} }
} }

View File

@ -0,0 +1,17 @@
.excalidraw {
.layer-ui__wrapper__footer-center {
display: flex;
justify-content: space-between;
}
.encrypted-icon {
border-radius: var(--space-factor);
color: var(--icon-green-fill-color);
margin-top: 13px;
svg {
width: 1.2rem;
height: 1.2rem;
}
}
}

View File

@ -52,6 +52,10 @@ import {
} from "./data/localStorage"; } from "./data/localStorage";
import CustomStats from "./CustomStats"; import CustomStats from "./CustomStats";
import { restoreAppState, RestoredDataState } from "../data/restore"; import { restoreAppState, RestoredDataState } from "../data/restore";
import { Tooltip } from "../components/Tooltip";
import { shield } from "../components/icons";
import "./index.scss";
const languageDetector = new LanguageDetector(); const languageDetector = new LanguageDetector();
languageDetector.init({ languageDetector.init({
@ -328,6 +332,20 @@ const ExcalidrawWrapper = () => {
const renderFooter = useCallback( const renderFooter = useCallback(
(isMobile: boolean) => { (isMobile: boolean) => {
const renderEncryptedIcon = () => (
<a
className="encrypted-icon tooltip"
href="https://blog.excalidraw.com/end-to-end-encryption/"
target="_blank"
rel="noopener noreferrer"
aria-label={t("encrypted.link")}
>
<Tooltip label={t("encrypted.tooltip")} position="above" long={true}>
{shield}
</Tooltip>
</a>
);
const renderLanguageList = () => ( const renderLanguageList = () => (
<LanguageList <LanguageList
onChange={(langCode) => { onChange={(langCode) => {
@ -370,7 +388,12 @@ const ExcalidrawWrapper = () => {
</div> </div>
); );
} }
return renderLanguageList(); return (
<>
{renderEncryptedIcon()}
{renderLanguageList()}
</>
);
}, },
[langCode], [langCode],
); );

View File

@ -23,6 +23,12 @@ Please add the latest change on the top under the correct section.
This also removes the GitHub icon, keeping it local to the https://excalidraw.com app. This also removes the GitHub icon, keeping it local to the https://excalidraw.com app.
### Fixes
- The encryption shield icon is now removed from excalidraw package as it was specific to excalidraw app and is now rendered via [`renderFooter`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#renderFooter) prop. In case you were hiding this icon earlier, you need not do that anymore [#3577](https://github.com/excalidraw/excalidraw/pull/3577).
Now `appState` is also passed to [`renderFooter`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#renderFooter) prop.
## 0.7.0 (2021-04-26) ## 0.7.0 (2021-04-26)
## Excalidraw API ## Excalidraw API

View File

@ -348,7 +348,7 @@ To view the full example visit :point_down:
| Name | Type | Default | Description | | Name | Type | Default | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| [`onChange`](#onChange) | Function | | This callback is triggered whenever the component updates due to any change. This callback will receive the excalidraw elements and the current app state. | | [`onChange`](#onChange) | Function | | This callback is triggered whenever the component updates due to any change. This callback will receive the excalidraw elements and the current app state. |
| [`initialData`](#initialData) | <pre>{elements?: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement[]</a>, appState?: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37">AppState<a> } </pre> | null | The initial data with which app loads. | | [`initialData`](#initialData) | <pre>{elements?: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement[]</a>, appState?: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L42">AppState<a> } </pre> | null | The initial data with which app loads. |
| [`ref`](#ref) | [`createRef`](https://reactjs.org/docs/refs-and-the-dom.html#creating-refs) or [`callbackRef`](https://reactjs.org/docs/refs-and-the-dom.html#callback-refs) or <pre>{ current: { readyPromise: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/utils.ts#L317">resolvablePromise</a> } }</pre> | | Ref to be passed to Excalidraw | | [`ref`](#ref) | [`createRef`](https://reactjs.org/docs/refs-and-the-dom.html#creating-refs) or [`callbackRef`](https://reactjs.org/docs/refs-and-the-dom.html#callback-refs) or <pre>{ current: { readyPromise: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/utils.ts#L317">resolvablePromise</a> } }</pre> | | Ref to be passed to Excalidraw |
| [`onCollabButtonClick`](#onCollabButtonClick) | Function | | Callback to be triggered when the collab button is clicked | | [`onCollabButtonClick`](#onCollabButtonClick) | Function | | Callback to be triggered when the collab button is clicked |
| [`isCollaborating`](#isCollaborating) | `boolean` | | This implies if the app is in collaboration mode | | [`isCollaborating`](#isCollaborating) | `boolean` | | This implies if the app is in collaboration mode |
@ -384,7 +384,7 @@ Every time component updates, this callback if passed will get triggered and has
1.`excalidrawElements`: Array of [excalidrawElements](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78) in the scene. 1.`excalidrawElements`: Array of [excalidrawElements](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78) in the scene.
2.`appState`: [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37) of the scene 2.`appState`: [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L42) of the scene
Here you can try saving the data to your backend or local storage for example. Here you can try saving the data to your backend or local storage for example.
@ -395,7 +395,7 @@ This helps to load Excalidraw with `initialData`. It must be an object or a [pro
| Name | Type | Descrption | | Name | Type | Descrption |
| --- | --- | --- | | --- | --- | --- |
| `elements` | [ExcalidrawElement[]](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78) | The elements with which Excalidraw should be mounted. | | `elements` | [ExcalidrawElement[]](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78) | The elements with which Excalidraw should be mounted. |
| `appState` | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37) | The App state with which Excalidraw should be mounted. | | `appState` | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L42) | The App state with which Excalidraw should be mounted. |
| `scrollToContent` | boolean | This attribute implies whether to scroll to the nearest element to center once Excalidraw is mounted. By default, it will not scroll the nearest element to the center. Make sure you pass `initialData.appState.scrollX` and `initialData.appState.scrollY` when `scrollToContent` is false so that scroll positions are retained | | `scrollToContent` | boolean | This attribute implies whether to scroll to the nearest element to center once Excalidraw is mounted. By default, it will not scroll the nearest element to the center. Make sure you pass `initialData.appState.scrollX` and `initialData.appState.scrollY` when `scrollToContent` is false so that scroll positions are retained |
| `libraryItems` | [LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L151) | This library items with which Excalidraw should be mounted. | | `libraryItems` | [LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L151) | This library items with which Excalidraw should be mounted. |
@ -442,7 +442,7 @@ You can pass a `ref` when you want to access some excalidraw APIs. We expose the
| resetScene | `({ resetLoadingState: boolean }) => void` | Resets the scene. If `resetLoadingState` is passed as true then it will also force set the loading state to false. | | resetScene | `({ resetLoadingState: boolean }) => void` | Resets the scene. If `resetLoadingState` is passed as true then it will also force set the loading state to false. |
| getSceneElementsIncludingDeleted | <pre> () => <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement[]</a></pre> | Returns all the elements including the deleted in the scene | | getSceneElementsIncludingDeleted | <pre> () => <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement[]</a></pre> | Returns all the elements including the deleted in the scene |
| getSceneElements | <pre> () => <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement[]</a></pre> | Returns all the elements excluding the deleted in the scene | | getSceneElements | <pre> () => <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement[]</a></pre> | Returns all the elements excluding the deleted in the scene |
| getAppState | <pre> () => <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37">AppState</a></pre> | Returns current appState | | getAppState | <pre> () => <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L42">AppState</a></pre> | Returns current appState |
| history | `{ clear: () => void }` | This is the history API. `history.clear()` will clear the history | | history | `{ clear: () => void }` | This is the history API. `history.clear()` will clear the history |
| scrollToContent | <pre> (target?: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement</a> &#124; <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement</a>[]) => void </pre> | Scroll the nearest element out of the elements supplied to the center. Defaults to the elements on the scene. | | scrollToContent | <pre> (target?: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement</a> &#124; <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement</a>[]) => void </pre> | Scroll the nearest element out of the elements supplied to the center. Defaults to the elements on the scene. |
| refresh | `() => void` | Updates the offsets for the Excalidraw component so that the coordinates are computed correctly (for example the cursor position). You don't have to call this when the position is changed on page scroll or when the excalidraw container resizes (we handle that ourselves). For any other cases if the position of excalidraw is updated (example due to scroll on parent container and not page scroll) you should call this API. | | refresh | `() => void` | Updates the offsets for the Excalidraw component so that the coordinates are computed correctly (for example the cursor position). You don't have to call this when the position is changed on page scroll or when the excalidraw container resizes (we handle that ourselves). For any other cases if the position of excalidraw is updated (example due to scroll on parent container and not page scroll) you should call this API. |
@ -487,7 +487,7 @@ This callback is triggered when the shareable-link button is clicked in the expo
``` ```
1. `exportedElements`: An array of [non deleted elements](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L87) which needs to be exported. 1. `exportedElements`: An array of [non deleted elements](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L87) which needs to be exported.
2. `appState`: [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37) of the scene. 2. `appState`: [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L42) of the scene.
3. `canvas`: The `HTMLCanvasElement` of the scene. 3. `canvas`: The `HTMLCanvasElement` of the scene.
#### `langCode` #### `langCode`
@ -505,10 +505,18 @@ import { defaultLang, languages } from "@excalidraw/excalidraw";
#### `renderTopRightUI` #### `renderTopRightUI`
<pre>
(isMobile: boolean, appState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L42">AppState</a>) => JSX
</pre>
A function returning JSX to render custom UI in the top right corner of the app. A function returning JSX to render custom UI in the top right corner of the app.
#### `renderFooter` #### `renderFooter`
<pre>
(isMobile: boolean, appState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L42">AppState</a>) => JSX
</pre>
A function returning JSX to render custom UI footer. For example, you can use this to render a language picker that was previously being rendered by Excalidraw itself (for now, you'll need to implement your own language picker). A function returning JSX to render custom UI footer. For example, you can use this to render a language picker that was previously being rendered by Excalidraw itself (for now, you'll need to implement your own language picker).
#### `renderCustomStats` #### `renderCustomStats`
@ -673,7 +681,7 @@ This function returns an object where each element is mapped to its id.
**_Signature_** **_Signature_**
<pre> <pre>
restoreAppState(appState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L17">ImportedDataState["appState"]</a>, localAppState: Partial<<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37">AppState</a>> | null): <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37">AppState</a> restoreAppState(appState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L17">ImportedDataState["appState"]</a>, localAppState: Partial<<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L42">AppState</a>> | null): <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L42">AppState</a>
</pre> </pre>
**_How to use_** **_How to use_**
@ -682,7 +690,7 @@ restoreAppState(appState: <a href="https://github.com/excalidraw/excalidraw/blo
import { restoreAppState } from "@excalidraw/excalidraw"; import { restoreAppState } from "@excalidraw/excalidraw";
``` ```
This function will make sure all the keys have appropriate values in [appState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37) and if any key is missing, it will be set to default value. If you pass `localAppState`, `localAppState` value will be preferred over the `appState` passed in params. This function will make sure all the keys have appropriate values in [appState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L42) and if any key is missing, it will be set to default value. If you pass `localAppState`, `localAppState` value will be preferred over the `appState` passed in params.
#### `restoreElements` #### `restoreElements`

View File

@ -183,7 +183,7 @@ export interface ExcalidrawProps {
event: ClipboardEvent | null, event: ClipboardEvent | null,
) => Promise<boolean> | boolean; ) => Promise<boolean> | boolean;
renderTopRightUI?: (isMobile: boolean, appState: AppState) => JSX.Element; renderTopRightUI?: (isMobile: boolean, appState: AppState) => JSX.Element;
renderFooter?: (isMobile: boolean) => JSX.Element; renderFooter?: (isMobile: boolean, appState: AppState) => JSX.Element;
langCode?: Language["code"]; langCode?: Language["code"];
viewModeEnabled?: boolean; viewModeEnabled?: boolean;
zenModeEnabled?: boolean; zenModeEnabled?: boolean;