Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions src/enumerations/window-events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum WindowEvents {
Resize = "resize",
}
37 changes: 37 additions & 0 deletions src/hooks/make-cancellable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { PromiseFactory } from "andculturecode-javascript-core";

// ---------------------------------------------------------
// #region Public Methods
// ---------------------------------------------------------

/**
* Wrap the provided promise in a promise that intercepts cancellation requests
*/
const makeCancellable = (promise: Promise<any>) => {
let isCanceled = false;

const wrappedPromise = new Promise((resolve, reject) =>
promise
.then((value: any) =>
isCanceled ? PromiseFactory.pending() : resolve(value)
)
.catch((error: any) =>
isCanceled ? PromiseFactory.pending() : reject(error)
)
);

return {
promise: wrappedPromise,
cancel() {
isCanceled = true;
},
};
};

// #endregion Public Methods

// ---------------------------------------------------------
// Exports
// ---------------------------------------------------------

export { makeCancellable };
3 changes: 3 additions & 0 deletions src/hooks/use-debounce.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
describe("useDebounce", () => {
test.skip("TODO - https://github.com/AndcultureCode/AndcultureCode.JavaScript.React/issues/20", () => {});
});
17 changes: 17 additions & 0 deletions src/hooks/use-debounce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useEffect, useState } from "react";

/**
* Throttles the supplied value for a set amount of milliseconds
* @param value
* @param delay number of milliseconds to delay
*/
export function useDebounce<T>(value: T, delay: number = 200) {
const [debouncedValue, setDebouncedValue] = useState<T>(value);

useEffect(() => {
const handler = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(handler);
}, [value, delay]);

return debouncedValue;
}
3 changes: 3 additions & 0 deletions src/hooks/use-onclick-outside.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
describe("useOnclickOutside", () => {
test.skip("TODO - https://github.com/AndcultureCode/AndcultureCode.JavaScript.React/issues/21", () => {});
});
54 changes: 54 additions & 0 deletions src/hooks/use-onclick-outside.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { useEffect, useState } from "react";

/**
* Custom hook providing utility to take some action when a mouse event is fired outside of an element.
* @param ref
* @param handler
* @param deps
*/
export function useOnClickOutside(
ref: React.RefObject<HTMLElement>,
handler: () => void,
deps?: React.DependencyList | undefined
) {
// Ensure we only attach one event
const [hasEvent, setHasEvent] = useState<boolean>(false);

useEffect(() => {
const layout = document.getElementById("root");
if (hasEvent || layout == null) {
return;
}

const event = (e: Event) => {
handleClickOutside(e);
toggleEvent("remove", event);
};

const handleClickOutside = (event: Event) => {
if (
ref?.current == null ||
ref.current.contains(event.target as Node)
) {
return true;
}

handler();
};

const toggleEvent = (
action: "add" | "remove",
e: (e: Event) => void
) => {
const fn =
action === "add"
? layout.addEventListener
: layout.removeEventListener;

fn("mousedown", e);
setHasEvent(action === "add");
};

toggleEvent("add", event);
}, [deps, handler, hasEvent, ref]);
}
3 changes: 3 additions & 0 deletions src/hooks/use-page-errors.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
describe("usePageErrors", () => {
test.skip("TODO - https://github.com/AndcultureCode/AndcultureCode.JavaScript.React/issues/22", () => {});
});
29 changes: 29 additions & 0 deletions src/hooks/use-page-errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useState, useCallback } from "react";
import { ResultRecord } from "andculturecode-javascript-core";

/**
* Hook to bundle common page error handling functionality
*/
export function usePageErrors() {
const [pageErrors, setPageErrors] = useState<Array<string>>([]);

const handlePageLoadError = useCallback((result: any) => {
if (result instanceof ResultRecord) {
setPageErrors((e) => [...e, ...result.listErrorMessages()]);
return;
}

setPageErrors((e) => [...e, result]);
}, []);

const resetPageErrors = useCallback(() => {
setPageErrors([]);
}, []);

return {
handlePageLoadError,
pageErrors,
resetPageErrors,
setPageErrors,
};
}
3 changes: 3 additions & 0 deletions src/hooks/use-text-overflow.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
describe("useTextOverflow", () => {
test.skip("TODO - https://github.com/AndcultureCode/AndcultureCode.JavaScript.React/issues/23", () => {});
});
30 changes: 30 additions & 0 deletions src/hooks/use-text-overflow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { RefObject, useCallback, useEffect, useState } from "react";
import { useWindow } from "./use-window";

/**
* A custom hook for determining if elements overflow their containers.
* Useful for when you have text-overflow: ellipsis; and you want to
* detect whether the text is actually long enough to trigger the ellipsis
* to appear.
* @param ref
*/
export function useTextOverflow<T extends HTMLElement>(ref: RefObject<T>) {
const getIsOverflowed = useCallback((): boolean => {
if (ref.current == null) {
return false;
}

return ref.current.offsetWidth < ref.current.scrollWidth;
}, [ref]);

const { width, height } = useWindow();
const [isOverflowed, setIsOverflowed] = useState(getIsOverflowed());

useEffect(() => setIsOverflowed(getIsOverflowed()), [
getIsOverflowed,
width,
height,
]);

return isOverflowed;
}
3 changes: 3 additions & 0 deletions src/hooks/use-window.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
describe("useWindow", () => {
test.skip("TODO - https://github.com/AndcultureCode/AndcultureCode.JavaScript.React/issues/24", () => {});
});
33 changes: 33 additions & 0 deletions src/hooks/use-window.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useEffect, useState } from "react";
import { WindowEvents } from "../enumerations/window-events";

/**
* Get window properties.
* Hook into window events and make properties more easily accessible to components.
*/
export function useWindow() {
const [height, setHeight] = useState<number>(window.innerHeight);
const [width, setWidth] = useState<number>(window.innerWidth);

useEffect(() => {
const handleWindowResize = () => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
};

const toggleEvent = (action: "add" | "remove") => {
const fn =
action === "add"
? window.addEventListener
: window.removeEventListener;

fn(WindowEvents.Resize, handleWindowResize);
};

toggleEvent("add");

return () => toggleEvent("remove");
}, []);

return { width, height };
}
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@ export { Redirects, RedirectsProps } from "./components/routing/redirects";
// #region Hooks
// -----------------------------------------------------------------------------------------

export { makeCancellable } from "./hooks/make-cancellable";
export { useCancellablePromise } from "./hooks/use-cancellable-promise";
export { useDebounce } from "./hooks/use-debounce";
export { useLocalization } from "./hooks/use-localization";
export { useOnClickOutside } from "./hooks/use-onclick-outside";
export { usePageErrors } from "./hooks/use-page-errors";
export { useTextOverflow } from "./hooks/use-text-overflow";
export { useWindow } from "./hooks/use-window";

// #endregion

Expand Down