diff --git a/src/components/Dialog.scss b/src/components/Dialog.scss index 2668f694..1035cd3f 100644 --- a/src/components/Dialog.scss +++ b/src/components/Dialog.scss @@ -19,7 +19,7 @@ @media #{$media-query} { .Dialog { --metric: calc(var(--space-factor) * 4); - --inset-left: #{"max(var(--metric), env(safe-area-inset-left))"}; + --inset-left: #{"max(var(--metric), var(--sal))"}; --inset-right: #{"max(var(--metric), env(safe-area-inset-right))"}; } .Dialog__title { @@ -49,9 +49,9 @@ height: 100%; box-sizing: border-box; overflow-y: auto; - padding-left: #{"max(calc(var(--padding) * var(--space-factor)), env(safe-area-inset-left))"}; - padding-right: #{"max(calc(var(--padding) * var(--space-factor)), env(safe-area-inset-right))"}; - padding-bottom: #{"max(calc(var(--padding) * var(--space-factor)), env(safe-area-inset-bottom))"}; + padding-left: #{"max(calc(var(--padding) * var(--space-factor)), var(--sal))"}; + padding-right: #{"max(calc(var(--padding) * var(--space-factor)), var(--sar))"}; + padding-bottom: #{"max(calc(var(--padding) * var(--space-factor)), var(--sab))"}; } .Dialog .Modal__close { diff --git a/src/components/MobileMenu.tsx b/src/components/MobileMenu.tsx index 89145c4e..67bda75c 100644 --- a/src/components/MobileMenu.tsx +++ b/src/components/MobileMenu.tsx @@ -13,6 +13,7 @@ import { calculateScrollCenter, getTargetElement } from "../scene"; import { SelectedShapeActions, ShapesSwitcher } from "./Actions"; import { Section } from "./Section"; import { RoomDialog } from "./RoomDialog"; +import { SCROLLBAR_WIDTH, SCROLLBAR_MARGIN } from "../scene/scrollbars"; type MobileMenuProps = { appState: AppState; @@ -37,45 +38,6 @@ export function MobileMenu({ }: MobileMenuProps) { return ( <> - {appState.openMenu === "canvas" ? ( -
-
- - {actionManager.renderAction("loadScene")} - {actionManager.renderAction("saveScene")} - {exportButton} - {actionManager.renderAction("clearCanvas")} - - {actionManager.renderAction("changeViewBackgroundColor")} -
- {t("labels.language")} - { - setLanguage(lng); - setAppState({}); - }} - /> -
-
-
-
- ) : appState.openMenu === "shape" && - showSelectedShapeActions(appState, elements) ? ( -
-
- -
-
- ) : null}
{heading => ( @@ -98,26 +60,74 @@ export function MobileMenu({
- +
+ + {appState.openMenu === "canvas" ? ( +
+
+ + {actionManager.renderAction("loadScene")} + {actionManager.renderAction("saveScene")} + {exportButton} + {actionManager.renderAction("clearCanvas")} + + {actionManager.renderAction("changeViewBackgroundColor")} +
+ {t("labels.language")} + { + setLanguage(lng); + setAppState({}); + }} + /> +
+
+
+
+ ) : appState.openMenu === "shape" && + showSelectedShapeActions(appState, elements) ? ( +
+ +
+ ) : null} + +
+
); } diff --git a/src/components/ToolIcon.scss b/src/components/ToolIcon.scss index 30308aba..2446509d 100644 --- a/src/components/ToolIcon.scss +++ b/src/components/ToolIcon.scss @@ -39,6 +39,7 @@ .ToolIcon__label { font-family: var(--ui-font); margin: 0 0.8em; + text-overflow: ellipsis; } .ToolIcon_size_s .ToolIcon__icon { diff --git a/src/locales/en.json b/src/locales/en.json index f1bb77dc..109c6fe7 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -66,7 +66,7 @@ "edit": "Edit", "undo": "Undo", "redo": "Redo", - "roomDialog": "Share a live-collaboration session", + "roomDialog": "Start live collaboration", "createNewRoom": "Create new room" }, "alerts": { diff --git a/src/scene/scrollbars.ts b/src/scene/scrollbars.ts index 6af637f3..48199719 100644 --- a/src/scene/scrollbars.ts +++ b/src/scene/scrollbars.ts @@ -2,8 +2,9 @@ import { ExcalidrawElement } from "../element/types"; import { getCommonBounds } from "../element"; import { FlooredNumber } from "../types"; import { ScrollBars } from "./types"; +import { getGlobalCSSVariable } from "../utils"; -const SCROLLBAR_MARGIN = 4; +export const SCROLLBAR_MARGIN = 4; export const SCROLLBAR_WIDTH = 6; export const SCROLLBAR_COLOR = "rgba(0,0,0,0.3)"; @@ -36,11 +37,18 @@ export function getScrollBars( const viewportWidthDiff = viewportWidth - viewportWidthWithZoom; const viewportHeightDiff = viewportHeight - viewportHeightWithZoom; + const safeArea = { + top: parseInt(getGlobalCSSVariable("sat")), + bottom: parseInt(getGlobalCSSVariable("sab")), + left: parseInt(getGlobalCSSVariable("sal")), + right: parseInt(getGlobalCSSVariable("sar")), + }; + // The viewport is the rectangle currently visible for the user - const viewportMinX = -scrollX + viewportWidthDiff / 2; - const viewportMinY = -scrollY + viewportHeightDiff / 2; - const viewportMaxX = viewportMinX + viewportWidthWithZoom; - const viewportMaxY = viewportMinY + viewportHeightWithZoom; + const viewportMinX = -scrollX + viewportWidthDiff / 2 + safeArea.left; + const viewportMinY = -scrollY + viewportHeightDiff / 2 + safeArea.top; + const viewportMaxX = viewportMinX + viewportWidthWithZoom - safeArea.right; + const viewportMaxY = viewportMinY + viewportHeightWithZoom - safeArea.bottom; // The scene is the bounding box of both the elements and viewport const sceneMinX = Math.min(elementsMinX, viewportMinX); @@ -56,30 +64,36 @@ export function getScrollBars( ? null : { x: + Math.max(safeArea.left, SCROLLBAR_MARGIN) + ((viewportMinX - sceneMinX) / (sceneMaxX - sceneMinX)) * - viewportWidth + - SCROLLBAR_MARGIN, - y: viewportHeight - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN, + viewportWidth, + y: + viewportHeight - + SCROLLBAR_WIDTH - + Math.max(SCROLLBAR_MARGIN, safeArea.bottom), width: ((viewportMaxX - viewportMinX) / (sceneMaxX - sceneMinX)) * viewportWidth - - SCROLLBAR_MARGIN * 2, + Math.max(SCROLLBAR_MARGIN * 2, safeArea.left + safeArea.right), height: SCROLLBAR_WIDTH, }, vertical: viewportMinY === sceneMinY && viewportMaxY === sceneMaxY ? null : { - x: viewportWidth - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN, + x: + viewportWidth - + SCROLLBAR_WIDTH - + Math.max(safeArea.right, SCROLLBAR_MARGIN), y: ((viewportMinY - sceneMinY) / (sceneMaxY - sceneMinY)) * viewportHeight + - SCROLLBAR_MARGIN, + Math.max(safeArea.top, SCROLLBAR_MARGIN), width: SCROLLBAR_WIDTH, height: ((viewportMaxY - viewportMinY) / (sceneMaxY - sceneMinY)) * viewportHeight - - SCROLLBAR_MARGIN * 2, + Math.max(SCROLLBAR_MARGIN * 2, safeArea.top + safeArea.bottom), }, }; } diff --git a/src/styles.scss b/src/styles.scss index 3885ab60..1541e79a 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,6 +1,13 @@ @import "./theme.css"; @import "./_variables"; +:root { + --sat: env(safe-area-inset-top); + --sab: env(safe-area-inset-bottom); + --sal: env(safe-area-inset-left); + --sar: env(safe-area-inset-right); +} + body { margin: 0; --ui-font: Arial, Helvetica, sans-serif; @@ -119,6 +126,7 @@ button, cursor: pointer; &:focus { + outline: transparent; box-shadow: 0 0 0 2px #a5d8ff; } @@ -146,24 +154,38 @@ button, } } -.App-toolbar, -.App-mobile-menu { - --spacing: 0.5rem; - --padding: calc(4 * var(--space-factor)); - padding: var(--padding); - padding-left: #{"max(var(--padding), env(safe-area-inset-left))"}; - padding-right: #{"max(var(--padding), env(safe-area-inset-right))"}; - background: #fcfcfc; - border-top: 1px solid #ccc; - box-sizing: border-box; -} -.App-toolbar { - padding-bottom: #{"max(var(--padding), env(safe-area-inset-bottom))"}; - width: 100%; - box-sizing: border-box; - overflow: auto; +.App-bottom-bar { position: absolute; + top: 0; bottom: 0; + left: 0; + right: 0; + --bar-padding: calc(4 * var(--space-factor)); + padding-top: #{"max(var(--bar-padding), var(--sat))"}; + padding-left: var(--sal); + padding-right: var(--sar); + padding-bottom: var(--sab); + z-index: 4; + display: flex; + align-items: flex-end; + pointer-events: none; + + > .Island { + width: 100%; + max-width: 100%; + min-width: 100%; + box-sizing: border-box; + max-height: 100%; + display: flex; + flex-direction: column; + pointer-events: initial; + } +} + +.App-toolbar { + width: 100%; + + box-sizing: border-box; } .App-toolbar-content { display: flex; @@ -171,18 +193,11 @@ button, justify-content: space-between; } .App-mobile-menu { - --bottom: calc(3rem - 1px + max(var(--padding), env(safe-area-inset-bottom))); - display: grid; - position: fixed; width: 100%; - bottom: var(--bottom); - z-index: 4; - max-height: calc(100% - var(--bottom)); - overflow-y: scroll; -} -.App-mobile-menu .App-mobile-menu-scroller { - background: #fcfcfc; - box-shadow: none; + overflow-x: visible; + overflow-y: auto; + box-sizing: border-box; + margin-bottom: var(--bar-padding); } .App-menu { @@ -358,6 +373,7 @@ button, bottom: 30px; transform: translateX(-50%); padding: 10px 20px; + z-index: -1; } @media #{$media-query} { @@ -366,6 +382,6 @@ button, } .scroll-back-to-content { bottom: 80px; - bottom: calc(80px + env(safe-area-inset-bottom)); + bottom: calc(80px + var(--sab)); } } diff --git a/src/utils.ts b/src/utils.ts index 7fb16485..9bc90f1e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -194,3 +194,9 @@ export function sceneCoordsToViewportCoords( return { x, y }; } + +export function getGlobalCSSVariable(name: string) { + return getComputedStyle(document.documentElement).getPropertyValue( + `--${name}`, + ); +}