Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
70b9aa5
Minimal functional version
latekvo Mar 6, 2024
46f5eb0
Reworked movement so that jittering is insignificant and layout is no…
latekvo Mar 6, 2024
a4ef1bc
last functional version, added mouse capture
latekvo Mar 7, 2024
f7f65db
added panning, visual changes
latekvo Mar 7, 2024
da5e289
converted rendering system to use references instead of rerenders
latekvo Mar 7, 2024
08b0763
prettier - fixed file formatting for PR
latekvo Mar 7, 2024
c37135f
Merge branch 'main' into @latekvo/docs/make-title-movable
latekvo Mar 7, 2024
cf75df5
Merge branch 'main' into @latekvo/docs/make-title-movable
latekvo Mar 11, 2024
18e9440
Apply css suggestion.
latekvo Mar 12, 2024
7efee6d
Applied review suggestion.
latekvo Mar 12, 2024
c752c64
Fixed sudden moves being stopped.
latekvo Mar 13, 2024
db00f72
Added unstaged changes from previous commit.
latekvo Mar 13, 2024
e44b0d0
By default, title is not interactive, and looks exactly how it used t…
latekvo Mar 13, 2024
bc9b9cc
Removed unnecessary useEffects.
latekvo Mar 14, 2024
0207edc
Simplified code. Ran prettier.
latekvo Mar 14, 2024
2df6ca1
Resolve merge
latekvo Mar 14, 2024
9791989
Unify styling for interactive and non-interactive components.
latekvo Mar 14, 2024
e5af116
Fix styling for mobile devices, this may require more work.
latekvo Mar 14, 2024
960642f
Fix failing git checks. Ran prettier.
latekvo Mar 14, 2024
7bb1ceb
Fixed website layout so it appears on mobile exactly how it used to b…
latekvo Mar 14, 2024
69287e6
Fix git CI errors.
latekvo Mar 14, 2024
13bcce4
Cleaned up code.
latekvo Mar 14, 2024
96f78b6
Simplify code, fix formatting.
latekvo Mar 14, 2024
0fb792d
Moved logic to a separate file.
latekvo Mar 14, 2024
9662155
Remove unnecessary code.
latekvo Mar 15, 2024
3c4ab42
Merge branch 'main' into @latekvo/docs/make-title-movable
latekvo Mar 15, 2024
475ef5a
Updated styles to match their original look on mobile.
latekvo Mar 15, 2024
e8db12b
Merge branch 'software-mansion:@latekvo/docs/make-title-movable' into…
latekvo Mar 15, 2024
a6166c8
Fix lint errors.
latekvo Mar 15, 2024
b7e2d0c
Merge branch 'software-mansion:@latekvo/docs/make-title-movable' into…
latekvo Mar 15, 2024
5e2b849
Apply review suggestion.
latekvo Mar 18, 2024
95cf005
Merge branch 'software-mansion:@latekvo/docs/make-title-movable' into…
latekvo Mar 18, 2024
a0af3fa
Merge branch 'main' into @latekvo/docs/make-title-movable
latekvo Mar 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions docs/src/components/Hero/SelectedLabel/SelectionBox/index.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Draggable
onDrag={(event: any) => {
propagationFunction(
{ x: event.movementX, y: event.movementY },
draggableIdentifier
);
}}
allowAnyClick={false}
axis={'none'}>
<div className={classList}>{children}</div>
</Draggable>
);
};

export default SelectionBox;
Original file line number Diff line number Diff line change
@@ -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;
}
}
151 changes: 120 additions & 31 deletions docs/src/components/Hero/SelectedLabel/index.tsx
Original file line number Diff line number Diff line change
@@ -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<DynamicStyles>({
top: 0,
left: 0,
width: null,
height: null,
});

const staticStyles = useRef<StaticStyles>({
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 (
<span className={clsx(styles.headingLabel, styles.selection)}>
<div className={styles.selectionContainer}>
<div
className={clsx(
styles.selectionBox,
styles.boxUpper,
styles.boxLeft
)}></div>
<div
className={clsx(
styles.selectionBox,
styles.boxUpper,
styles.boxRight
)}></div>
<div
className={clsx(
styles.selectionBox,
styles.boxLower,
styles.boxLeft
)}></div>
<div
className={clsx(
styles.selectionBox,
styles.boxLower,
styles.boxRight
)}></div>
{children}
<span ref={selectionRef} className={styles.selection}>
<div ref={selectionContainerRef} className={styles.selectionContainer}>
<SelectionBox
propagationFunction={movementPropagator}
draggableIdentifier={DraggableId.TOP_LEFT}
isInteractive={isInteractive}></SelectionBox>
<SelectionBox
propagationFunction={movementPropagator}
draggableIdentifier={DraggableId.TOP_RIGHT}
isInteractive={isInteractive}></SelectionBox>
<SelectionBox
propagationFunction={movementPropagator}
draggableIdentifier={DraggableId.BOTTOM_LEFT}
isInteractive={isInteractive}></SelectionBox>
<SelectionBox
propagationFunction={movementPropagator}
draggableIdentifier={DraggableId.BOTTOM_RIGHT}
isInteractive={isInteractive}></SelectionBox>
<SelectionBox
propagationFunction={movementPropagator}
draggableIdentifier={DraggableId.CENTER}
isInteractive={isInteractive}>
<span ref={textLabelRef} className={classList}>
{children}
</span>
</SelectionBox>
</div>
</span>
);
Expand Down
60 changes: 39 additions & 21 deletions docs/src/components/Hero/SelectedLabel/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Loading