diff --git a/docs/src/components/Hero/SelectedLabel/SelectionBox/index.tsx b/docs/src/components/Hero/SelectedLabel/SelectionBox/index.tsx new file mode 100644 index 000000000000..96ee1b185487 --- /dev/null +++ b/docs/src/components/Hero/SelectedLabel/SelectionBox/index.tsx @@ -0,0 +1,76 @@ +import clsx from 'clsx'; +import React from 'react'; +import styles from './styles.module.css'; +import Draggable from 'react-draggable'; + +export enum DraggableId { + TOP_LEFT, + TOP_RIGHT, + BOTTOM_LEFT, + BOTTOM_RIGHT, + CENTER, +} + +const getClassListByIdentifier = ( + identifier: DraggableId, + isInteractive: boolean +) => { + const isBottom = + identifier == DraggableId.BOTTOM_LEFT || + identifier == DraggableId.BOTTOM_RIGHT; + const isLeft = + identifier == DraggableId.BOTTOM_LEFT || identifier == DraggableId.TOP_LEFT; + const isCenter = identifier === DraggableId.CENTER; + + let classList = styles.centerDraggable; + + if (!isCenter) { + classList = clsx( + styles.selectionBox, + isBottom ? styles.boxLower : styles.boxUpper, + isLeft ? styles.boxLeft : styles.boxRight + ); + } + + if (isInteractive) { + classList = clsx(classList, styles.movable); + } + + return classList; +}; + +const SelectionBox: React.FC<{ + propagationFunction: ( + movementDelta: { x: number; y: number }, + draggableIdentifier: DraggableId + ) => void; + draggableIdentifier: DraggableId; + children?: React.ReactNode; + isInteractive: boolean; +}> = ({ + propagationFunction, + draggableIdentifier, + children, + isInteractive, +}) => { + const classList = getClassListByIdentifier( + draggableIdentifier, + isInteractive + ); + + return ( + { + propagationFunction( + { x: event.movementX, y: event.movementY }, + draggableIdentifier + ); + }} + allowAnyClick={false} + axis={'none'}> +
{children}
+
+ ); +}; + +export default SelectionBox; diff --git a/docs/src/components/Hero/SelectedLabel/SelectionBox/styles.module.css b/docs/src/components/Hero/SelectedLabel/SelectionBox/styles.module.css new file mode 100644 index 000000000000..85c5b2c34f37 --- /dev/null +++ b/docs/src/components/Hero/SelectedLabel/SelectionBox/styles.module.css @@ -0,0 +1,64 @@ +.movable { + cursor: grab; +} + +.movable:active { + cursor: grabbing; +} + +.selectionBox { + position: absolute; + width: 12px; + height: 12px; + z-index: 2; + background: var(--swm-white); + border: 2px solid var(--swm-landing-heading-selected-border); + transition: scale 50ms ease-in-out; +} + +.movable.selectionBox:active { + scale: 1.35; +} + +.movable.selectionBox:hover { + scale: 1.35; +} + +.centerDraggable { + width: 100%; + height: 100%; +} + +.boxUpper { + top: -7px; +} + +.boxLower { + bottom: -13px; +} + +.boxLeft { + left: -15px; +} + +.boxRight { + right: -15px; +} + +@media (max-width: 768px) { + .boxUpper { + top: -11px; + } + + .boxLower { + bottom: -13px; + } + + .boxLeft { + left: -7px; + } + + .boxRight { + right: -7px; + } +} diff --git a/docs/src/components/Hero/SelectedLabel/index.tsx b/docs/src/components/Hero/SelectedLabel/index.tsx index eac983ba2a31..e901cd2b3063 100644 --- a/docs/src/components/Hero/SelectedLabel/index.tsx +++ b/docs/src/components/Hero/SelectedLabel/index.tsx @@ -1,38 +1,127 @@ import clsx from 'clsx'; -import React from 'react'; +import React, { useEffect, useRef } from 'react'; import styles from './styles.module.css'; +import SelectionBox, { DraggableId } from './SelectionBox'; +import { + DynamicStyles, + StaticStyles, + TextScaleStyles, + computeSelectionStyles, + computeTextStyles, +} from './utils'; + +const SelectedLabel: React.FC<{ + children: React.ReactNode; + isInteractive: boolean; +}> = ({ children, isInteractive = false }) => { + // DOM refs + const selectionRef = useRef(null); + const selectionContainerRef = useRef(null); + const textLabelRef = useRef(null); + + // Render-persistent label positioning styles + const dynamicStyles = useRef({ + top: 0, + left: 0, + width: null, + height: null, + }); + + const staticStyles = useRef({ + initialWidth: null, + initialHeight: null, + enabledTextInteractivity: false, + }); + + const applyDynamicStyles = (newDynamicStyles: DynamicStyles) => { + const currentDynamicStyles = dynamicStyles.current; + + // save changes + currentDynamicStyles.top = newDynamicStyles.top; + currentDynamicStyles.left = newDynamicStyles.left; + currentDynamicStyles.width = newDynamicStyles.width; + currentDynamicStyles.height = newDynamicStyles.height; + + // apply changes to refs + selectionRef.current.style.transform = `translate(${currentDynamicStyles.left}px, ${currentDynamicStyles.top}px)`; + selectionContainerRef.current.style.width = `${currentDynamicStyles.width}px`; + selectionContainerRef.current.style.height = `${currentDynamicStyles.height}px`; + }; + + const applyTextScale = (scaleObject: TextScaleStyles) => { + textLabelRef.current.style.transform = `translate(-50%, -50%) scale(${scaleObject.x}, ${scaleObject.y})`; + }; + + useEffect(() => { + if (!isInteractive) return; + + let rect = selectionContainerRef.current.getBoundingClientRect(); + staticStyles.current = { + initialWidth: rect.width, + initialHeight: rect.height, + enabledTextInteractivity: true, + }; + + applyDynamicStyles({ + top: 0, + left: 0, + width: rect.width, + height: rect.height, + }); + }, [isInteractive]); + + const movementPropagator = ( + movementDelta: { x: number; y: number }, + draggableIdentifier: DraggableId + ) => { + if (!isInteractive) return; + + applyDynamicStyles( + computeSelectionStyles( + movementDelta, + draggableIdentifier, + dynamicStyles.current + ) + ); + applyTextScale( + computeTextStyles(dynamicStyles.current, staticStyles.current) + ); + }; + + const classList = clsx( + isInteractive ? styles.preEnabledTextInteractivity : null, + staticStyles.current.enabledTextInteractivity + ? styles.interactiveHeaderText + : styles.headerText + ); -const SelectedLabel: React.FC<{ children: React.ReactNode }> = ({ - children, -}) => { return ( - -
-
-
-
-
- {children} + +
+ + + + + + + {children} + +
); diff --git a/docs/src/components/Hero/SelectedLabel/styles.module.css b/docs/src/components/Hero/SelectedLabel/styles.module.css index e699321122b8..a578b10f7364 100644 --- a/docs/src/components/Hero/SelectedLabel/styles.module.css +++ b/docs/src/components/Hero/SelectedLabel/styles.module.css @@ -5,44 +5,62 @@ .selection { display: inline-block; - padding: 2px 10px 8px; + position: absolute; + padding: 3px 10px 8px; color: var(--swm-landing-heading-selected); line-height: 1; + z-index: 1; /* normally one would do this with border dashed but that doesn't allow for modifying spacing between dashes */ /* prettier-ignore */ background: - linear-gradient(to right, var(--swm-landing-heading-selected-border) 50%, transparent 0%) top/var(--swm-dash-spacing) var(--swm-dash-width) repeat-x, /* top */ - linear-gradient(var(--swm-landing-heading-selected-border) 50%, transparent 0%) right/var(--swm-dash-width) var(--swm-dash-spacing) repeat-y, /* right */ - linear-gradient(to right, var(--swm-landing-heading-selected-border) 50%, transparent 0%) bottom/var(--swm-dash-spacing) var(--swm-dash-width) repeat-x, /* bottom */ - linear-gradient(var(--swm-landing-heading-selected-border) 50%, transparent 0%) left/var(--swm-dash-width) var(--swm-dash-spacing) repeat-y; /* left */ + linear-gradient(to right, var(--swm-landing-heading-selected-border) 50%, transparent 0%) top/var(--swm-dash-spacing) var(--swm-dash-width) repeat-x, + /* top */ + linear-gradient(var(--swm-landing-heading-selected-border) 50%, transparent 0%) right/var(--swm-dash-width) var(--swm-dash-spacing) repeat-y, + /* right */ + linear-gradient(to right, var(--swm-landing-heading-selected-border) 50%, transparent 0%) bottom/var(--swm-dash-spacing) var(--swm-dash-width) repeat-x, + /* bottom */ + linear-gradient(var(--swm-landing-heading-selected-border) 50%, transparent 0%) left/var(--swm-dash-width) var(--swm-dash-spacing) repeat-y; + /* left */ } .selectionContainer { position: relative; + text-align: center; } -.selectionBox { - position: absolute; - width: 12px; - height: 12px; - - background: var(--swm-white); - border: 2px solid var(--swm-landing-heading-selected-border); +.headerText { + position: relative; } -.boxUpper { - top: -6px; +.interactiveHeaderText { + position: absolute; + transform: translate(-50%, -50%); + left: 50%; + top: 50%; + user-select: none; } -.boxLower { - bottom: -14px; +.preEnabledTextInteractivity { + margin-right: 24px; } -.boxLeft { - left: -14px; -} +@media (max-width: 768px) { + :root { + --swm-dash-spacing: 20px; + } + + .selection { + padding: 6px 2px 8px; + display: block; + margin: 6px 0; + } + + .headerText { + margin: 0 10px; + } -.boxRight { - right: -14px; + .preInteractiveHeaderText { + margin-right: 0; + } } diff --git a/docs/src/components/Hero/SelectedLabel/utils.ts b/docs/src/components/Hero/SelectedLabel/utils.ts new file mode 100644 index 000000000000..09ade9b2e3ec --- /dev/null +++ b/docs/src/components/Hero/SelectedLabel/utils.ts @@ -0,0 +1,101 @@ +import { DraggableId } from './SelectionBox'; + +export type DynamicStyles = { + top: number; + left: number; + width: number; + height: number; +}; + +export type StaticStyles = { + initialWidth: number; + initialHeight: number; + enabledTextInteractivity: boolean; +}; + +export type TextScaleStyles = { + x: number; + y: number; +}; + +export const computeSelectionStyles = ( + position: { x: number; y: number }, + draggableIdentifier: DraggableId, + dynamicStyles +): DynamicStyles => { + const isLeft = + draggableIdentifier === DraggableId.BOTTOM_LEFT || + draggableIdentifier === DraggableId.TOP_LEFT; + const isTop = + draggableIdentifier === DraggableId.TOP_LEFT || + draggableIdentifier === DraggableId.TOP_RIGHT; + const isCenter = draggableIdentifier === DraggableId.CENTER; + + // depedning on whether draggable is on left, right, top or bottom, + // we want to either resize, or move and resize our object + const positionAdjustment = { + x: isLeft ? position.x : 0, + y: isTop ? position.y : 0, + }; + + const sizeChange = { + x: isLeft ? -position.x : position.x, + y: isTop ? -position.y : position.y, + }; + + // adjust variables when dragging the center + if (isCenter) { + positionAdjustment.x = sizeChange.x; + positionAdjustment.y = sizeChange.y; + sizeChange.x = 0; + sizeChange.y = 0; + } + + // stop overreduction in size + if (dynamicStyles.width + sizeChange.x < 0) + sizeChange.x = -dynamicStyles.width; + if (dynamicStyles.height + sizeChange.y < 0) + sizeChange.y = -dynamicStyles.height; + + // stop dragging a minimized object + if (!isCenter) { + if (dynamicStyles.width - positionAdjustment.x < 0) + positionAdjustment.x = 0; + if (dynamicStyles.height - positionAdjustment.y < 0) + positionAdjustment.y = 0; + } + + return { + left: dynamicStyles.left + positionAdjustment.x, + top: dynamicStyles.top + positionAdjustment.y, + width: dynamicStyles.width + sizeChange.x, + height: dynamicStyles.height + sizeChange.y, + }; +}; + +export const computeTextStyles = ( + dynamicStyles: DynamicStyles, + staticStyles: StaticStyles +): TextScaleStyles => { + // these magic numbers are a result of disparity between font's apparent and actual size + const sizeOffsetX = 0.99; + const sizeOffsetY = 1.21; + + // scale starts at 1 and as it gets larger approaches sizeOffset + const textScale = { + x: + (dynamicStyles.width / staticStyles.initialWidth) * sizeOffsetX - + sizeOffsetX + + 1, + y: + (dynamicStyles.height / staticStyles.initialHeight) * sizeOffsetY - + sizeOffsetY + + 1, + }; + + // prevent text overadjustment + if (textScale.x < 0) textScale.x = 0; + if (textScale.y < 0) textScale.y = 0; + + return textScale; +}; diff --git a/docs/src/components/Hero/StartScreen/index.tsx b/docs/src/components/Hero/StartScreen/index.tsx index f3e01cf9ef4a..09f2960a3816 100644 --- a/docs/src/components/Hero/StartScreen/index.tsx +++ b/docs/src/components/Hero/StartScreen/index.tsx @@ -13,8 +13,8 @@ const StartScreen = () => {

- React Native - Reanimated + React Native + Reanimated

Beyond the limitations

diff --git a/docs/src/components/Hero/StartScreen/styles.module.css b/docs/src/components/Hero/StartScreen/styles.module.css index 5ee38fa5e4df..390e228545e3 100644 --- a/docs/src/components/Hero/StartScreen/styles.module.css +++ b/docs/src/components/Hero/StartScreen/styles.module.css @@ -26,8 +26,8 @@ color: var(--swm-landing-heading); } -.headingLabel span { - margin-right: 8px; +.rnLabel { + margin-right: 10px; } .subheadingLabel { @@ -39,7 +39,7 @@ margin-top: 32px; margin-bottom: 56px; - width: 600px; + width: fit-content; color: var(--swm-landing-heading); } @@ -101,10 +101,15 @@ flex-direction: column; justify-content: space-between; - margin-top: 64px; + margin-top: 22px; height: 100%; } + .subheadingLabel { + margin-top: 64px; + margin-bottom: 24px; + } + .foregroundLabel { display: flex; flex-direction: column;