Private
Public Access
1
0

feat: Fluent UI Outlook Lite + connections mockup

This commit is contained in:
2026-04-14 18:52:25 +00:00
parent 1199eff6c3
commit dfa4010406
34820 changed files with 1003813 additions and 205 deletions

View File

@@ -0,0 +1,169 @@
'use client';
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
SafeZoneArea: function() {
return SafeZoneArea;
},
isSameCoordinates: function() {
return isSameCoordinates;
},
isSameRect: function() {
return isSameRect;
}
});
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
const _react = require("@griffel/react");
const _reactutilities = require("@fluentui/react-utilities");
const _react1 = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
const _shim = require("use-sync-external-store/shim");
const _getRectCorners = require("./getRectCorners");
const _getMouseAnchor = require("./getMouseAnchor");
const _pointsToSvgPath = require("./pointsToSvgPath");
const _SafeZoneAreastyles = require("./SafeZoneArea.styles");
const _computeOutsideClipPath = require("./computeOutsideClipPath");
// ---
const EMPTY_RECT = {
top: 0,
right: 0,
bottom: 0,
left: 0,
width: 0,
height: 0,
x: 0,
y: 0,
toJSON () {
return '';
}
};
function isSameRect(a, b) {
return a.top === b.top && a.right === b.right && a.bottom === b.bottom && a.left === b.left && a.width === b.width && a.height === b.height;
}
function isSameCoordinates(a, b) {
return a[0] === b[0] && a[1] === b[1];
}
const SafeZoneArea = /*#__PURE__*/ _react1.memo((props)=>{
const { debug, onMouseEnter, onMouseMove, onMouseLeave, stateStore } = props;
const clipPathId = (0, _reactutilities.useId)();
const styles = (0, _SafeZoneAreastyles.useStyles)();
const active = (0, _shim.useSyncExternalStore)(stateStore.subscribe, stateStore.isActive);
const svgRef = _react1.useRef(null);
const [state, setState] = _react1.useState(()=>({
containerRect: EMPTY_RECT,
targetRect: EMPTY_RECT,
mouseCoordinates: [
0,
0
]
}));
_react1.useImperativeHandle(props.imperativeRef, ()=>({
updateSVG (newState) {
setState((prevState)=>{
// Heads up!
// A small optimization to avoid unnecessary re-renders
if (isSameRect(prevState.containerRect, newState.containerRect) && isSameRect(prevState.targetRect, newState.targetRect) && isSameCoordinates(prevState.mouseCoordinates, newState.mouseCoordinates)) {
return prevState;
}
return newState;
});
}
}), []);
const { containerRect, targetRect, mouseCoordinates } = state;
const topOffset = Math.min(targetRect.top, containerRect.top);
const leftOffset = Math.min(targetRect.left, containerRect.left);
const bottomOffset = Math.max(targetRect.bottom, containerRect.bottom);
const rightOffset = Math.max(targetRect.right, containerRect.right);
// ---
const containerCorners = (0, _getRectCorners.getRectCorners)(containerRect, [
leftOffset,
topOffset
]);
const targetCorners = (0, _getRectCorners.getRectCorners)(targetRect, [
leftOffset,
topOffset
]);
// Heads up!
// The SVG coordinate system starts at the top-left corner of the SVG element,
// so we need to adjust the mouse coordinates relative to the SVG's top-left corner.
const relativeMouseCoordinates = [
mouseCoordinates[0] - leftOffset,
mouseCoordinates[1] - topOffset
];
const mouseAnchor = (0, _getMouseAnchor.getMouseAnchor)(containerCorners.topLeft, containerCorners.bottomRight, relativeMouseCoordinates);
const triangleA = [
mouseAnchor,
containerCorners.topLeft,
containerCorners.topRight
];
const triangleB = [
mouseAnchor,
containerCorners.topRight,
containerCorners.bottomRight
];
const triangleC = [
mouseAnchor,
containerCorners.bottomRight,
containerCorners.bottomLeft
];
const triangleD = [
mouseAnchor,
containerCorners.bottomLeft,
containerCorners.topLeft
];
const svgWidth = rightOffset - leftOffset;
const svgHeight = bottomOffset - topOffset;
const clipPath = (0, _computeOutsideClipPath.computeOutsideClipPath)(svgWidth, svgHeight, {
x: targetCorners.topLeft[0],
y: targetCorners.topLeft[1],
width: targetRect.width,
height: targetRect.height
}, {
x: containerCorners.topLeft[0],
y: containerCorners.topLeft[1],
width: containerRect.width,
height: containerRect.height
});
return /*#__PURE__*/ _react1.createElement("div", {
className: (0, _react.mergeClasses)(styles.wrapper, active && styles.wrapperActive),
"data-safe-zone": ""
}, active ? /*#__PURE__*/ _react1.createElement("svg", {
"aria-hidden": true,
className: styles.svg,
xmlns: "http://www.w3.org/2000/svg",
ref: svgRef,
style: {
width: `${svgWidth}px`,
height: `${svgHeight}px`,
transform: `translate(${leftOffset}px, ${topOffset}px)`
}
}, /*#__PURE__*/ _react1.createElement("g", {
className: (0, _react.mergeClasses)(styles.triangle, debug && styles.triangleDebug),
clipPath: `url(#${clipPathId})`,
onMouseEnter: onMouseEnter,
onMouseMove: onMouseMove,
onMouseLeave: onMouseLeave
}, /*#__PURE__*/ _react1.createElement("path", {
d: (0, _pointsToSvgPath.pointsToSvgPath)(triangleA)
}), /*#__PURE__*/ _react1.createElement("path", {
d: (0, _pointsToSvgPath.pointsToSvgPath)(triangleB)
}), /*#__PURE__*/ _react1.createElement("path", {
d: (0, _pointsToSvgPath.pointsToSvgPath)(triangleC)
}), /*#__PURE__*/ _react1.createElement("path", {
d: (0, _pointsToSvgPath.pointsToSvgPath)(triangleD)
})), /*#__PURE__*/ _react1.createElement("clipPath", {
id: clipPathId
}, /*#__PURE__*/ _react1.createElement("path", {
d: clipPath
})), debug && /*#__PURE__*/ _react1.createElement("path", {
className: styles.rectDebug,
d: clipPath
})) : null);
});

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,60 @@
'use client';
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "useStyles", {
enumerable: true,
get: function() {
return useStyles;
}
});
const _react = require("@griffel/react");
const useStyles = /*#__PURE__*/ (0, _react.__styles)({
wrapper: {
mc9l5x: "fjseox",
Bqenvij: "fniina8",
a9b677: "f3tsq5r",
Bkecrkj: "f1aehjj5"
},
wrapperActive: {
mc9l5x: "ftgm304"
},
svg: {
Bkfmm31: "f1au8mb3",
Bkecrkj: "f1aehjj5",
qhf8xq: "f19dog8a",
Bhzewxz: "f15twtuk",
oyh7mz: [
"f1vgc2s3",
"f1e31b4d"
]
},
triangle: {
Bkecrkj: "f1cguypg"
},
triangleDebug: {
Bceei9c: "f7116n6",
Bkfmm31: "f1xab38x"
},
rectDebug: {
Bkfmm31: "fyegryc"
}
}, {
d: [
".fjseox{display:none;}",
".fniina8{height:0;}",
".f3tsq5r{width:0;}",
".f1aehjj5{pointer-events:none;}",
".ftgm304{display:block;}",
".f1au8mb3{fill:transparent;}",
".f19dog8a{position:fixed;}",
".f15twtuk{top:0;}",
".f1vgc2s3{left:0;}",
".f1e31b4d{right:0;}",
".f1cguypg{pointer-events:auto;}",
".f7116n6{cursor:crosshair;}",
".f1xab38x{fill:color-mix(in srgb, var(--colorPaletteGreenBackground3) 20%, transparent);}",
".fyegryc{fill:color-mix(in srgb, var(--colorPaletteRedBackground3) 20%, transparent);}"
]
});

View File

@@ -0,0 +1 @@
{"version":3,"sources":["SafeZoneArea.styles.js"],"sourcesContent":["'use client';\nimport { makeStyles } from '@griffel/react';\nimport { tokens } from '@fluentui/react-theme';\nexport const useStyles = makeStyles({\n wrapper: {\n display: 'none',\n height: 0,\n width: 0,\n pointerEvents: 'none'\n },\n wrapperActive: {\n display: 'block'\n },\n svg: {\n fill: 'transparent',\n pointerEvents: 'none',\n position: 'fixed',\n top: 0,\n left: 0\n },\n triangle: {\n pointerEvents: 'auto'\n },\n triangleDebug: {\n cursor: 'crosshair',\n fill: `color-mix(in srgb, ${tokens.colorPaletteGreenBackground3} 20%, transparent)`\n },\n rectDebug: {\n fill: `color-mix(in srgb, ${tokens.colorPaletteRedBackground3} 20%, transparent)`\n }\n});\n"],"names":["__styles","tokens","useStyles","wrapper","mc9l5x","Bqenvij","a9b677","Bkecrkj","wrapperActive","svg","Bkfmm31","qhf8xq","Bhzewxz","oyh7mz","triangle","triangleDebug","Bceei9c","rectDebug","d"],"mappings":"AAAA,YAAY;;;;;+BAGCE,SAAS;;;;;;uBAFK,gBAAgB;AAEpC,kBAAe,WAAA,OAAGF,eAAA,EAAA;IAAAG,OAAA,EAAA;QAAAC,MAAA,EAAA;QAAAC,OAAA,EAAA;QAAAC,MAAA,EAAA;QAAAC,OAAA,EAAA;IAAA;IAAAC,aAAA,EAAA;QAAAJ,MAAA,EAAA;IAAA;IAAAK,GAAA,EAAA;QAAAC,OAAA,EAAA;QAAAH,OAAA,EAAA;QAAAI,MAAA,EAAA;QAAAC,OAAA,EAAA;QAAAC,MAAA,EAAA;YAAA;YAAA;SAAA;IAAA;IAAAC,QAAA,EAAA;QAAAP,OAAA,EAAA;IAAA;IAAAQ,aAAA,EAAA;QAAAC,OAAA,EAAA;QAAAN,OAAA,EAAA;IAAA;IAAAO,SAAA,EAAA;QAAAP,OAAA,EAAA;IAAA;AAAA,GAAA;IAAAQ,CAAA,EAAA;QAAA;QAAA;QAAA;QAAA;QAAA;QAAA;QAAA;QAAA;QAAA;QAAA;QAAA;QAAA;QAAA;QAAA;KAAA;AAAA,CA2BxB,CAAC"}

View File

@@ -0,0 +1,41 @@
'use client';
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "useStyles", {
enumerable: true,
get: function() {
return useStyles;
}
});
const _react = require("@griffel/react");
const _reacttheme = require("@fluentui/react-theme");
const useStyles = (0, _react.makeStyles)({
wrapper: {
display: 'none',
height: 0,
width: 0,
pointerEvents: 'none'
},
wrapperActive: {
display: 'block'
},
svg: {
fill: 'transparent',
pointerEvents: 'none',
position: 'fixed',
top: 0,
left: 0
},
triangle: {
pointerEvents: 'auto'
},
triangleDebug: {
cursor: 'crosshair',
fill: `color-mix(in srgb, ${_reacttheme.tokens.colorPaletteGreenBackground3} 20%, transparent)`
},
rectDebug: {
fill: `color-mix(in srgb, ${_reacttheme.tokens.colorPaletteRedBackground3} 20%, transparent)`
}
});

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useSafeZoneArea/SafeZoneArea.styles.ts"],"sourcesContent":["'use client';\n\nimport { makeStyles } from '@griffel/react';\nimport { tokens } from '@fluentui/react-theme';\n\nexport const useStyles = makeStyles({\n wrapper: {\n display: 'none',\n height: 0,\n width: 0,\n pointerEvents: 'none',\n },\n wrapperActive: {\n display: 'block',\n },\n svg: {\n fill: 'transparent',\n pointerEvents: 'none',\n position: 'fixed',\n top: 0,\n left: 0,\n },\n triangle: {\n pointerEvents: 'auto',\n },\n triangleDebug: {\n cursor: 'crosshair',\n fill: `color-mix(in srgb, ${tokens.colorPaletteGreenBackground3} 20%, transparent)`,\n },\n rectDebug: {\n fill: `color-mix(in srgb, ${tokens.colorPaletteRedBackground3} 20%, transparent)`,\n },\n});\n"],"names":["makeStyles","tokens","useStyles","wrapper","display","height","width","pointerEvents","wrapperActive","svg","fill","position","top","left","triangle","triangleDebug","cursor","colorPaletteGreenBackground3","rectDebug","colorPaletteRedBackground3"],"mappings":"AAAA;;;;;+BAKaE;;;;;;uBAHc,iBAAiB;4BACrB,wBAAwB;AAExC,sBAAkBF,iBAAAA,EAAW;IAClCG,SAAS;QACPC,SAAS;QACTC,QAAQ;QACRC,OAAO;QACPC,eAAe;IACjB;IACAC,eAAe;QACbJ,SAAS;IACX;IACAK,KAAK;QACHC,MAAM;QACNH,eAAe;QACfI,UAAU;QACVC,KAAK;QACLC,MAAM;IACR;IACAC,UAAU;QACRP,eAAe;IACjB;IACAQ,eAAe;QACbC,QAAQ;QACRN,MAAM,CAAC,mBAAmB,EAAET,kBAAAA,CAAOgB,4BAA4B,CAAC,kBAAkB,CAAC;IACrF;IACAC,WAAW;QACTR,MAAM,CAAC,mBAAmB,EAAET,kBAAAA,CAAOkB,0BAA0B,CAAC,kBAAkB,CAAC;IACnF;AACF,GAAG"}

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "computeOutsideClipPath", {
enumerable: true,
get: function() {
return computeOutsideClipPath;
}
});
function drawRectangle(rect) {
if (rect.width <= 0 || rect.height <= 0) {
return '';
}
let pathData = '';
// Creates a subpath moving in counterclockwise direction to create a hole
pathData += `M ${rect.x},${rect.y} `;
pathData += `V ${rect.y + rect.height} `; // Down to bottom-left
pathData += `H ${rect.x + rect.width} `; // Right to bottom-right
pathData += `V ${rect.y} `; // Up to top-right
pathData += `H ${rect.x} `; // Left to top-left (closing)
pathData += `Z `; // Close path
return pathData;
}
function computeOutsideClipPath(svgWidth, svgHeight, targetRect, containerRect) {
let pathData = `M 0,0 H ${svgWidth} V ${svgHeight} H 0 Z `;
// For each rectangle, add a subpath that "cuts out" the rectangle
// The trick is to draw each rectangle in the counterclockwise direction
// which creates a "hole" in the main path
pathData += drawRectangle(targetRect);
pathData += drawRectangle(containerRect);
return pathData;
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useSafeZoneArea/computeOutsideClipPath.ts"],"sourcesContent":["function drawRectangle(rect: { x: number; y: number; width: number; height: number }): string {\n if (rect.width <= 0 || rect.height <= 0) {\n return '';\n }\n\n let pathData = '';\n\n // Creates a subpath moving in counterclockwise direction to create a hole\n\n pathData += `M ${rect.x},${rect.y} `;\n pathData += `V ${rect.y + rect.height} `; // Down to bottom-left\n pathData += `H ${rect.x + rect.width} `; // Right to bottom-right\n pathData += `V ${rect.y} `; // Up to top-right\n pathData += `H ${rect.x} `; // Left to top-left (closing)\n pathData += `Z `; // Close path\n\n return pathData;\n}\n\n/**\n * Computes a clip path that covers the area outside multiple rectangles.\n *\n * @internal\n */\nexport function computeOutsideClipPath(\n svgWidth: number,\n svgHeight: number,\n targetRect: { x: number; y: number; width: number; height: number },\n containerRect: { x: number; y: number; width: number; height: number },\n): string {\n let pathData = `M 0,0 H ${svgWidth} V ${svgHeight} H 0 Z `;\n\n // For each rectangle, add a subpath that \"cuts out\" the rectangle\n // The trick is to draw each rectangle in the counterclockwise direction\n // which creates a \"hole\" in the main path\n\n pathData += drawRectangle(targetRect);\n pathData += drawRectangle(containerRect);\n\n return pathData;\n}\n"],"names":["drawRectangle","rect","width","height","pathData","x","y","computeOutsideClipPath","svgWidth","svgHeight","targetRect","containerRect"],"mappings":";;;;;;;eAwBgBO;;;AAxBhB,SAASP,cAAcC,IAA6D;IAClF,IAAIA,KAAKC,KAAK,IAAI,KAAKD,KAAKE,MAAM,IAAI,GAAG;QACvC,OAAO;IACT;IAEA,IAAIC,WAAW;IAEf,0EAA0E;IAE1EA,YAAY,CAAC,EAAE,EAAEH,KAAKI,CAAC,CAAC,CAAC,EAAEJ,KAAKK,CAAC,CAAC,CAAC,CAAC;IACpCF,YAAY,CAAC,EAAE,EAAEH,KAAKK,CAAC,GAAGL,KAAKE,MAAM,CAAC,CAAC,CAAC,EAAE,sBAAsB;IAChEC,YAAY,CAAC,EAAE,EAAEH,KAAKI,CAAC,GAAGJ,KAAKC,KAAK,CAAC,CAAC,CAAC,EAAE,wBAAwB;IACjEE,YAAY,CAAC,EAAE,EAAEH,KAAKK,CAAC,CAAC,CAAC,CAAC,EAAE,kBAAkB;IAC9CF,YAAY,CAAC,EAAE,EAAEH,KAAKI,CAAC,CAAC,CAAC,CAAC,EAAE,6BAA6B;IACzDD,YAAY,CAAC,EAAE,CAAC,EAAE,aAAa;IAE/B,OAAOA;AACT;AAOO,gCACLI,QAAgB,EAChBC,SAAiB,EACjBC,UAAmE,EACnEC,aAAsE;IAEtE,IAAIP,WAAW,CAAC,QAAQ,EAAEI,SAAS,GAAG,EAAEC,UAAU,OAAO,CAAC;IAE1D,kEAAkE;IAClE,wEAAwE;IACxE,0CAA0C;IAE1CL,YAAYJ,cAAcU;IAC1BN,YAAYJ,cAAcW;IAE1B,OAAOP;AACT"}

View File

@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "createSafeZoneAreaStateStore", {
enumerable: true,
get: function() {
return createSafeZoneAreaStateStore;
}
});
function createSafeZoneAreaStateStore() {
let isActive = false;
const listeners = [];
return {
isActive () {
return isActive;
},
toggleActive (newIsActive) {
if (isActive === newIsActive) {
return;
}
isActive = newIsActive;
listeners.forEach((listener)=>listener(isActive));
},
subscribe (listener) {
listeners.push(listener);
return ()=>{
const index = listeners.indexOf(listener);
if (index > -1) {
listeners.splice(index, 1);
}
};
}
};
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useSafeZoneArea/createSafeZoneAreaStateStore.ts"],"sourcesContent":["export function createSafeZoneAreaStateStore(): {\n isActive: () => boolean;\n toggleActive: (newIsActive: boolean) => void;\n subscribe: (listener: (value: boolean) => void) => () => void;\n} {\n let isActive = false;\n const listeners: ((value: boolean) => void)[] = [];\n\n return {\n isActive() {\n return isActive;\n },\n toggleActive(newIsActive: boolean) {\n if (isActive === newIsActive) {\n return;\n }\n\n isActive = newIsActive;\n listeners.forEach(listener => listener(isActive));\n },\n\n subscribe(listener: (value: boolean) => void) {\n listeners.push(listener);\n\n return () => {\n const index = listeners.indexOf(listener);\n\n if (index > -1) {\n listeners.splice(index, 1);\n }\n };\n },\n };\n}\n"],"names":["createSafeZoneAreaStateStore","isActive","listeners","toggleActive","newIsActive","forEach","listener","subscribe","push","index","indexOf","splice"],"mappings":";;;;+BAAgBA;;;;;;AAAT;IAKL,IAAIC,WAAW;IACf,MAAMC,YAA0C,EAAE;IAElD,OAAO;QACLD;YACE,OAAOA;QACT;QACAE,cAAaC,WAAoB;YAC/B,IAAIH,aAAaG,aAAa;gBAC5B;YACF;YAEAH,WAAWG;YACXF,UAAUG,OAAO,CAACC,CAAAA,WAAYA,SAASL;QACzC;QAEAM,WAAUD,QAAkC;YAC1CJ,UAAUM,IAAI,CAACF;YAEf,OAAO;gBACL,MAAMG,QAAQP,UAAUQ,OAAO,CAACJ;gBAEhC,IAAIG,QAAQ,CAAC,GAAG;oBACdP,UAAUS,MAAM,CAACF,OAAO;gBAC1B;YACF;QACF;IACF;AACF"}

View File

@@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
getMouseAnchor: function() {
return getMouseAnchor;
},
getUnitVector: function() {
return getUnitVector;
},
measureDistance: function() {
return measureDistance;
}
});
const OFFSET_DISTANCE = 20;
function measureDistance(a, b) {
return Math.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2);
}
function getUnitVector(a, b) {
const distance = measureDistance(a, b);
if (distance === 0) {
return [
0,
0
];
}
return [
(a[0] - b[0]) / distance,
(a[1] - b[1]) / distance
];
}
function getMouseAnchor(topLeftCorner, bottomRightCorner, mouseCoordinates) {
const containerCenter = [
(topLeftCorner[0] + bottomRightCorner[0]) / 2,
(topLeftCorner[1] + bottomRightCorner[1]) / 2
];
const unitVector = getUnitVector([
mouseCoordinates[0],
mouseCoordinates[1]
], [
containerCenter[0],
containerCenter[1]
]);
const distance = measureDistance([
containerCenter[0],
containerCenter[1]
], [
mouseCoordinates[0],
mouseCoordinates[1]
]);
return [
containerCenter[0] + unitVector[0] * (distance + OFFSET_DISTANCE),
containerCenter[1] + unitVector[1] * (distance + OFFSET_DISTANCE)
];
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useSafeZoneArea/getMouseAnchor.ts"],"sourcesContent":["import type { Point } from './types';\n\nconst OFFSET_DISTANCE = 20;\n\n/**\n * Measures the distance between two points in a 2D space.\n */\nexport function measureDistance(a: Point, b: Point): number {\n return Math.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2);\n}\n\n/**\n * Returns a unit vector pointing from point `b` to point `a`.\n * If the distance is zero, returns a zero vector.\n */\nexport function getUnitVector(a: Point, b: Point): Point {\n const distance = measureDistance(a, b);\n\n if (distance === 0) {\n return [0, 0];\n }\n\n return [(a[0] - b[0]) / distance, (a[1] - b[1]) / distance];\n}\n\n/**\n * Calculates the anchor point for a mouse position within a container defined by its top-left and bottom-right corners.\n * The anchor point is calculated as an offset from the center of the container in the direction of the mouse position.\n *\n * @internal\n */\nexport function getMouseAnchor(topLeftCorner: Point, bottomRightCorner: Point, mouseCoordinates: Point): Point {\n const containerCenter: Point = [\n (topLeftCorner[0] + bottomRightCorner[0]) / 2,\n (topLeftCorner[1] + bottomRightCorner[1]) / 2,\n ];\n\n const unitVector = getUnitVector(\n [mouseCoordinates[0], mouseCoordinates[1]],\n [containerCenter[0], containerCenter[1]],\n );\n const distance = measureDistance(\n [containerCenter[0], containerCenter[1]],\n [mouseCoordinates[0], mouseCoordinates[1]],\n );\n\n return [\n containerCenter[0] + unitVector[0] * (distance + OFFSET_DISTANCE),\n containerCenter[1] + unitVector[1] * (distance + OFFSET_DISTANCE),\n ];\n}\n"],"names":["OFFSET_DISTANCE","measureDistance","a","b","Math","sqrt","getUnitVector","distance","getMouseAnchor","topLeftCorner","bottomRightCorner","mouseCoordinates","containerCenter","unitVector"],"mappings":";;;;;;;;;;;IA+BgBQ,cAAAA;;;iBAhBAF;;;mBARAL;;;;AALhB,MAAMD,kBAAkB;AAKjB,SAASC,gBAAgBC,CAAQ,EAAEC,CAAQ;IAChD,OAAOC,KAAKC,IAAI,CAAEH,CAAAA,CAAC,CAAC,EAAE,GAAGC,CAAC,CAAC,EAAA,AAAC,KAAM,IAAKD,CAAAA,CAAC,CAAC,EAAE,GAAGC,CAAC,CAAC,EAAA,AAAC,KAAM;AACzD;AAMO,SAASG,cAAcJ,CAAQ,EAAEC,CAAQ;IAC9C,MAAMI,WAAWN,gBAAgBC,GAAGC;IAEpC,IAAII,aAAa,GAAG;QAClB,OAAO;YAAC;YAAG;SAAE;IACf;IAEA,OAAO;SAAEL,CAAC,CAAC,EAAE,GAAGC,CAAC,CAAC,EAAC,AAAD,IAAMI;SAAWL,CAAC,CAAC,EAAE,GAAGC,CAAC,CAAC,EAAA,AAAC,IAAKI;KAAS;AAC7D;AAQO,wBAAwBE,aAAoB,EAAEC,iBAAwB,EAAEC,gBAAuB;IACpG,MAAMC,kBAAyB;SAC5BH,aAAa,CAAC,EAAE,GAAGC,iBAAiB,CAAC,EAAA,AAAC,IAAK;SAC3CD,aAAa,CAAC,EAAE,GAAGC,iBAAiB,CAAC,EAAA,AAAC,IAAK;KAC7C;IAED,MAAMG,aAAaP,cACjB;QAACK,gBAAgB,CAAC,EAAE;QAAEA,gBAAgB,CAAC,EAAE;KAAC,EAC1C;QAACC,eAAe,CAAC,EAAE;QAAEA,eAAe,CAAC,EAAE;KAAC;IAE1C,MAAML,WAAWN,gBACf;QAACW,eAAe,CAAC,EAAE;QAAEA,eAAe,CAAC,EAAE;KAAC,EACxC;QAACD,gBAAgB,CAAC,EAAE;QAAEA,gBAAgB,CAAC,EAAE;KAAC;IAG5C,OAAO;QACLC,eAAe,CAAC,EAAE,GAAGC,UAAU,CAAC,EAAE,GAAIN,CAAAA,WAAWP,eAAAA,CAAc;QAC/DY,eAAe,CAAC,EAAE,GAAGC,UAAU,CAAC,EAAE,GAAIN,CAAAA,WAAWP,eAAAA,CAAc;KAChE;AACH"}

View File

@@ -0,0 +1,34 @@
/**
* Calculates the corners of a rectangle based on its DOMRect and an offset.
*
* @internal
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "getRectCorners", {
enumerable: true,
get: function() {
return getRectCorners;
}
});
function getRectCorners(rect, offset) {
return {
topLeft: [
rect.left - offset[0],
rect.top - offset[1]
],
topRight: [
rect.right - offset[0],
rect.top - offset[1]
],
bottomRight: [
rect.right - offset[0],
rect.bottom - offset[1]
],
bottomLeft: [
rect.left - offset[0],
rect.bottom - offset[1]
]
};
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useSafeZoneArea/getRectCorners.ts"],"sourcesContent":["import type { Point } from './types';\n\n/**\n * Calculates the corners of a rectangle based on its DOMRect and an offset.\n *\n * @internal\n */\nexport function getRectCorners(\n rect: DOMRect,\n offset: Point,\n): Record<'topLeft' | 'topRight' | 'bottomRight' | 'bottomLeft', Point> {\n return {\n topLeft: [rect.left - offset[0], rect.top - offset[1]],\n topRight: [rect.right - offset[0], rect.top - offset[1]],\n bottomRight: [rect.right - offset[0], rect.bottom - offset[1]],\n bottomLeft: [rect.left - offset[0], rect.bottom - offset[1]],\n };\n}\n"],"names":["getRectCorners","rect","offset","topLeft","left","top","topRight","right","bottomRight","bottom","bottomLeft"],"mappings":"AAEA;;;;CAIC,GACD;;;;;;;;;;AAAO,SAASA,eACdC,IAAa,EACbC,MAAa;IAEb,OAAO;QACLC,SAAS;YAACF,KAAKG,IAAI,GAAGF,MAAM,CAAC,EAAE;YAAED,KAAKI,GAAG,GAAGH,MAAM,CAAC,EAAE;SAAC;QACtDI,UAAU;YAACL,KAAKM,KAAK,GAAGL,MAAM,CAAC,EAAE;YAAED,KAAKI,GAAG,GAAGH,MAAM,CAAC,EAAE;SAAC;QACxDM,aAAa;YAACP,KAAKM,KAAK,GAAGL,MAAM,CAAC,EAAE;YAAED,KAAKQ,MAAM,GAAGP,MAAM,CAAC,EAAE;SAAC;QAC9DQ,YAAY;YAACT,KAAKG,IAAI,GAAGF,MAAM,CAAC,EAAE;YAAED,KAAKQ,MAAM,GAAGP,MAAM,CAAC,EAAE;SAAC;IAC9D;AACF"}

View File

@@ -0,0 +1,17 @@
/**
* Calculates the corners of a rectangle based on its DOMRect and an offset.
*
* @internal
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "pointsToSvgPath", {
enumerable: true,
get: function() {
return pointsToSvgPath;
}
});
function pointsToSvgPath(points) {
return `M ${points} z`;
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useSafeZoneArea/pointsToSvgPath.ts"],"sourcesContent":["import type { Point } from './types';\n\n/**\n * Calculates the corners of a rectangle based on its DOMRect and an offset.\n *\n * @internal\n */\nexport function pointsToSvgPath(points: Point[]): string {\n return `M ${points} z`;\n}\n"],"names":["pointsToSvgPath","points"],"mappings":"AAEA;;;;CAIC,GACD;;;;;;;;;;AAAO,SAASA,gBAAgBC,MAAe;IAC7C,OAAO,CAAC,EAAE,EAAEA,OAAO,EAAE,CAAC;AACxB"}

View File

@@ -0,0 +1,8 @@
/**
* A type representing a point in a 2D space as an array of two numbers (x & y coordinates).
*
* @internal
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useSafeZoneArea/types.ts"],"sourcesContent":["/**\n * A type representing a point in a 2D space as an array of two numbers (x & y coordinates).\n *\n * @internal\n */\nexport type Point = [number, number];\n"],"names":[],"mappings":"AAAA;;;;CAIC,GACD,WAAqC"}

View File

@@ -0,0 +1,156 @@
'use client';
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "useSafeZoneArea", {
enumerable: true,
get: function() {
return useSafeZoneArea;
}
});
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
const _reactutilities = require("@fluentui/react-utilities");
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
const _createSafeZoneAreaStateStore = require("./createSafeZoneAreaStateStore");
const _SafeZoneArea = require("./SafeZoneArea");
/**
* Time in milliseconds after which the safe zone area will be cleared if no mouse movement is detected.
*
* Only affects the target element, not the safe zone area itself.
*/ const MOUSE_MOVE_TARGET_POLLING_TIMEOUT = 2000;
function useSafeZoneArea({ debug = false, disabled = false, onSafeZoneEnter, onSafeZoneMove, onSafeZoneLeave, onSafeZoneTimeout, timeout = 1500 } = {}) {
const [stateStore] = _react.useState(_createSafeZoneAreaStateStore.createSafeZoneAreaStateStore);
const safeZoneAreaRef = _react.useRef(null);
const containerRef = _react.useRef(null);
const targetRef = _react.useRef(null);
const [setSafeZoneCloseTimeout, clearSafeZoneCloseTimeout] = (0, _reactutilities.useTimeout)();
const [requestUpdateFrame, clearUpdateFrame] = (0, _reactutilities.useAnimationFrame)();
const mouseCoordinatesRef = _react.useRef({
x: 0,
y: 0
});
const containerListenerRef = _react.useMemo(()=>{
if (disabled) {
return ()=>{
// do nothing
};
}
let containerEl = null;
function onContainerMouseEnter() {
clearSafeZoneCloseTimeout();
stateStore.toggleActive(false);
}
return (el)=>{
if (el === null) {
containerEl === null || containerEl === void 0 ? void 0 : containerEl.removeEventListener('mouseenter', onContainerMouseEnter);
}
containerEl = el;
el === null || el === void 0 ? void 0 : el.addEventListener('mouseenter', onContainerMouseEnter);
};
}, [
clearSafeZoneCloseTimeout,
disabled,
stateStore
]);
const targetListenerRef = _react.useMemo(()=>{
if (disabled) {
return ()=>{
// do nothing
};
}
let targetEl = null;
function onTargetMouseMove(e) {
mouseCoordinatesRef.current = {
x: e.clientX,
y: e.clientY
};
if (!stateStore.isActive()) {
stateStore.toggleActive(true);
}
setSafeZoneCloseTimeout(()=>{
stateStore.toggleActive(false);
}, MOUSE_MOVE_TARGET_POLLING_TIMEOUT);
}
return (el)=>{
if (el === null) {
clearUpdateFrame();
clearSafeZoneCloseTimeout();
targetEl === null || targetEl === void 0 ? void 0 : targetEl.removeEventListener('mousemove', onTargetMouseMove);
}
targetEl = el;
el === null || el === void 0 ? void 0 : el.addEventListener('mousemove', onTargetMouseMove);
};
}, [
clearUpdateFrame,
clearSafeZoneCloseTimeout,
disabled,
stateStore,
setSafeZoneCloseTimeout
]);
const onSvgMouseEnter = (0, _reactutilities.useEventCallback)((e)=>{
onSafeZoneEnter === null || onSafeZoneEnter === void 0 ? void 0 : onSafeZoneEnter(e);
setSafeZoneCloseTimeout(()=>{
stateStore.toggleActive(false);
onSafeZoneTimeout === null || onSafeZoneTimeout === void 0 ? void 0 : onSafeZoneTimeout();
}, timeout);
});
const onSvgMouseMove = (0, _reactutilities.useEventCallback)((e)=>{
setSafeZoneCloseTimeout(()=>{
stateStore.toggleActive(false);
onSafeZoneTimeout === null || onSafeZoneTimeout === void 0 ? void 0 : onSafeZoneTimeout();
}, timeout);
onSafeZoneMove === null || onSafeZoneMove === void 0 ? void 0 : onSafeZoneMove(e);
});
const onSvgMouseLeave = (0, _reactutilities.useEventCallback)((e)=>{
onSafeZoneLeave === null || onSafeZoneLeave === void 0 ? void 0 : onSafeZoneLeave(e);
});
_react.useEffect(()=>{
return stateStore.subscribe((isActive)=>{
if (isActive) {
function updateSVGs() {
const containerEl = containerRef.current;
const targetEl = targetRef.current;
if (containerEl && targetEl) {
var _safeZoneAreaRef_current;
(_safeZoneAreaRef_current = safeZoneAreaRef.current) === null || _safeZoneAreaRef_current === void 0 ? void 0 : _safeZoneAreaRef_current.updateSVG({
containerRect: containerEl.getBoundingClientRect(),
mouseCoordinates: [
mouseCoordinatesRef.current.x,
mouseCoordinatesRef.current.y
],
targetRect: targetEl.getBoundingClientRect()
});
}
requestUpdateFrame(updateSVGs);
}
updateSVGs();
return;
}
clearUpdateFrame();
});
}, [
clearUpdateFrame,
requestUpdateFrame,
stateStore
]);
return {
containerRef: (0, _reactutilities.useMergedRefs)(containerRef, containerListenerRef),
targetRef: (0, _reactutilities.useMergedRefs)(targetRef, targetListenerRef),
elementToRender: _react.useMemo(()=>disabled ? null : /*#__PURE__*/ _react.createElement(_SafeZoneArea.SafeZoneArea, {
debug: debug,
onMouseEnter: onSvgMouseEnter,
onMouseMove: onSvgMouseMove,
onMouseLeave: onSvgMouseLeave,
imperativeRef: safeZoneAreaRef,
stateStore: stateStore
}), [
disabled,
debug,
onSvgMouseEnter,
onSvgMouseMove,
onSvgMouseLeave,
stateStore
])
};
}

File diff suppressed because one or more lines are too long