Major reshuffle
This commit is contained in:
8
package-lock.json
generated
8
package-lock.json
generated
@@ -12,7 +12,7 @@
|
||||
"@armco/configs": "^0.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@armco/types": "^0.0.20",
|
||||
"@armco/types": "^0.0.21",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/node": "^24.10.0",
|
||||
"@types/react": "^19.2.2",
|
||||
@@ -46,9 +46,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@armco/types": {
|
||||
"version": "0.0.20",
|
||||
"resolved": "https://registry.npmjs.org/@armco/types/-/types-0.0.20.tgz",
|
||||
"integrity": "sha512-7Q6iXeeYBLtE88zhDBE78zEdHZVCIq+68RsraJr98xzP0z0wUEvYr2Tfv7wynjV/qv1aH58XK/dAsqXL7IeqNg==",
|
||||
"version": "0.0.21",
|
||||
"resolved": "https://registry.npmjs.org/@armco/types/-/types-0.0.21.tgz",
|
||||
"integrity": "sha512-ixOJBpJP9a+uDnqndaMGyCsGQTOhdwbFazb7+eAYFng2+CCLS9WF//f8ldAlXmeWapeS9+VZxyxe6apaxjQ0yA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@armco/types": "^0.0.20",
|
||||
"@armco/types": "^0.0.21",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/node": "^24.10.0",
|
||||
"@types/react": "^19.2.2",
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import * as d3 from "d3"
|
||||
import { ArrayType, ObjectType } from "@armco/types"
|
||||
|
||||
export const generateBubbleChart = (data: ArrayType | ObjectType) => {
|
||||
const svg = d3.select("#ar-ArViz__chart-container")
|
||||
if (svg && data) {
|
||||
svg
|
||||
.selectAll("circle")
|
||||
.data(data as ArrayType)
|
||||
.enter()
|
||||
.append("circle")
|
||||
.attr("cx", function (d) {
|
||||
return (d as ObjectType).x as number
|
||||
})
|
||||
.attr("cy", function (d) {
|
||||
return (d as ObjectType).y as number
|
||||
})
|
||||
.attr("r", function (d) {
|
||||
return Math.sqrt((d as ObjectType).val as number) / Math.PI
|
||||
})
|
||||
.attr("fill", function (d) {
|
||||
return (d as ObjectType).color as string
|
||||
})
|
||||
|
||||
// Step 5
|
||||
svg
|
||||
.selectAll("text")
|
||||
.data(data as ArrayType)
|
||||
.enter()
|
||||
.append("text")
|
||||
.attr("x", function (d) {
|
||||
const x = (d as ObjectType).x as number
|
||||
const sqrt = Math.sqrt((d as ObjectType).val as number) as number
|
||||
return x + sqrt / Math.PI
|
||||
})
|
||||
.attr("y", function (d) {
|
||||
return ((d as ObjectType).y as number) + 4
|
||||
})
|
||||
.text(function (d) {
|
||||
return (d as ObjectType).source as string
|
||||
})
|
||||
.style("font-family", "arial")
|
||||
.style("font-size", "12px")
|
||||
}
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
import { ArDateMasks, FunctionType } from "@armco/types"
|
||||
|
||||
const token =
|
||||
/d{1,4}|D{3,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|W{1,2}|[LlopSZN]|"[^"]*"|'[^']*'/g
|
||||
const timezone =
|
||||
/\b(?:[A-Z]{1,3}[A-Z][TC])(?:[-+]\d{4})?|((?:Australian )?(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time)\b/g
|
||||
const timezoneClip = /[^-+\dA-Z]/g
|
||||
|
||||
/**
|
||||
* @param {string | number | Date} date
|
||||
* @param {string} mask
|
||||
* @param {boolean} utc
|
||||
* @param {boolean} gmt
|
||||
*/
|
||||
export function dateFormat(
|
||||
date?: number | Date,
|
||||
mask?: ArDateMasks | string,
|
||||
utc?: boolean,
|
||||
gmt?: boolean,
|
||||
) {
|
||||
// You can't provide utc if you skip other args (use the 'UTC:' mask prefix)
|
||||
if (arguments.length === 1 && typeof date === "string" && !/\d/.test(date)) {
|
||||
mask = date
|
||||
date = undefined
|
||||
}
|
||||
|
||||
date = date || date === 0 ? date : new Date()
|
||||
|
||||
if (isNaN(date as number)) {
|
||||
throw TypeError("Invalid date")
|
||||
}
|
||||
|
||||
if (!(date instanceof Date)) {
|
||||
date = new Date(date)
|
||||
}
|
||||
|
||||
mask = String(
|
||||
ArDateMasksRecord[mask as string] || mask || ArDateMasksRecord["DEFAULT"],
|
||||
)
|
||||
|
||||
// Allow setting the utc/gmt argument via the mask
|
||||
const maskSlice = mask.slice(0, 4)
|
||||
if (maskSlice === "UTC:" || maskSlice === "GMT:") {
|
||||
mask = mask.slice(4)
|
||||
utc = true
|
||||
if (maskSlice === "GMT:") {
|
||||
gmt = true
|
||||
}
|
||||
}
|
||||
|
||||
const _ = () => (utc ? "getUTC" : "get")
|
||||
const d = () => (date as Date)[(_() + "Date") as "getDate" | "getUTCDate"]()
|
||||
const D = () => (date as Date)[(_() + "Day") as "getDay" | "getUTCDay"]()
|
||||
const m = () =>
|
||||
(date as Date)[(_() + "Month") as "getMonth" | "getUTCMonth"]()
|
||||
const y = () =>
|
||||
(date as Date)[(_() + "FullYear") as "getFullYear" | "getUTCFullYear"]()
|
||||
const H = () =>
|
||||
(date as Date)[(_() + "Hours") as "getHours" | "getUTCHours"]()
|
||||
const M = () =>
|
||||
(date as Date)[(_() + "Minutes") as "getMinutes" | "getUTCMinutes"]()
|
||||
const s = () =>
|
||||
(date as Date)[(_() + "Seconds") as "getSeconds" | "getUTCSeconds"]()
|
||||
const L = () =>
|
||||
(date as Date)[
|
||||
(_() + "Milliseconds") as "getMilliseconds" | "getUTCMilliseconds"
|
||||
]()
|
||||
const o = () => (utc ? 0 : (date as Date).getTimezoneOffset())
|
||||
const W = () => date instanceof Date && getWeek(date)
|
||||
const N = () => date instanceof Date && getDayOfWeek(date)
|
||||
|
||||
const flags: { [key: string]: FunctionType } = {
|
||||
d: () => d(),
|
||||
dd: () => pad(d()),
|
||||
ddd: () => i18n.dayNames[D()],
|
||||
DDD: () =>
|
||||
getDayName({
|
||||
y: y(),
|
||||
m: m(),
|
||||
d: d(),
|
||||
_: _(),
|
||||
dayName: i18n.dayNames[D()],
|
||||
short: true,
|
||||
}),
|
||||
dddd: () => i18n.dayNames[D() + 7],
|
||||
DDDD: () =>
|
||||
getDayName({
|
||||
y: y(),
|
||||
m: m(),
|
||||
d: d(),
|
||||
_: _(),
|
||||
dayName: i18n.dayNames[D() + 7],
|
||||
}),
|
||||
m: () => m() + 1,
|
||||
mm: () => pad(m() + 1),
|
||||
mmm: () => i18n.monthNames[m()],
|
||||
mmmm: () => i18n.monthNames[m() + 12],
|
||||
yy: () => String(y()).slice(2),
|
||||
yyyy: () => pad(y(), 4),
|
||||
h: () => H() % 12 || 12,
|
||||
hh: () => pad(H() % 12 || 12),
|
||||
H: () => H(),
|
||||
HH: () => pad(H()),
|
||||
M: () => M(),
|
||||
MM: () => pad(M()),
|
||||
s: () => s(),
|
||||
ss: () => pad(s()),
|
||||
l: () => pad(L(), 3),
|
||||
L: () => pad(Math.floor(L() / 10)),
|
||||
t: () => (H() < 12 ? i18n.timeNames[0] : i18n.timeNames[1]),
|
||||
tt: () => (H() < 12 ? i18n.timeNames[2] : i18n.timeNames[3]),
|
||||
T: () => (H() < 12 ? i18n.timeNames[4] : i18n.timeNames[5]),
|
||||
TT: () => (H() < 12 ? i18n.timeNames[6] : i18n.timeNames[7]),
|
||||
Z: () =>
|
||||
gmt ? "GMT" : utc ? "UTC" : date instanceof Date && formatTimezone(date),
|
||||
o: () =>
|
||||
(o() > 0 ? "-" : "+") +
|
||||
pad(Math.floor(Math.abs(o()) / 60) * 100 + (Math.abs(o()) % 60), 4),
|
||||
p: () =>
|
||||
(o() > 0 ? "-" : "+") +
|
||||
pad(Math.floor(Math.abs(o()) / 60), 2) +
|
||||
":" +
|
||||
pad(Math.floor(Math.abs(o()) % 60), 2),
|
||||
S: () =>
|
||||
["th", "st", "nd", "rd"][
|
||||
d() % 10 > 3 ? 0 : (+((d() % 100) - (d() % 10) !== 10) * d()) % 10
|
||||
],
|
||||
W: () => W(),
|
||||
WW: () => {
|
||||
const date = W()
|
||||
date && pad(date)
|
||||
},
|
||||
N: () => N(),
|
||||
}
|
||||
|
||||
return mask.replace(token, (match) => {
|
||||
if (match in flags) {
|
||||
return flags[match]()
|
||||
}
|
||||
return match.slice(1, match.length - 1)
|
||||
})
|
||||
}
|
||||
|
||||
const ArDateMasksRecord: Record<string, string> = {
|
||||
DEFAULT: ArDateMasks.DEFAULT,
|
||||
SHORTDATE: ArDateMasks.SHORTDATE,
|
||||
PADDEDSHORTDATE: ArDateMasks.PADDEDSHORTDATE,
|
||||
MEDIUMDATE: ArDateMasks.MEDIUMDATE,
|
||||
LONGDATE: ArDateMasks.LONGDATE,
|
||||
FULLDATE: ArDateMasks.FULLDATE,
|
||||
SHORTTIME: ArDateMasks.SHORTTIME,
|
||||
MEDIUMTIME: ArDateMasks.MEDIUMTIME,
|
||||
LONGTIME: ArDateMasks.LONGTIME,
|
||||
ISODATE: ArDateMasks.ISODATE,
|
||||
ISOTIME: ArDateMasks.ISOTIME,
|
||||
ISODATETIME: ArDateMasks.ISODATETIME,
|
||||
ISOUTCDATETIME: ArDateMasks.ISOUTCDATETIME,
|
||||
EXPIRESHEADERFORMAT: ArDateMasks.EXPIRESHEADERFORMAT,
|
||||
}
|
||||
|
||||
// Internationalization strings
|
||||
export let i18n = {
|
||||
dayNames: [
|
||||
"Sun",
|
||||
"Mon",
|
||||
"Tue",
|
||||
"Wed",
|
||||
"Thu",
|
||||
"Fri",
|
||||
"Sat",
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
],
|
||||
monthNames: [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
],
|
||||
timeNames: ["a", "p", "am", "pm", "A", "P", "AM", "PM"],
|
||||
}
|
||||
|
||||
const pad = (val: number, len = 2) => String(val).padStart(len, "0")
|
||||
|
||||
/**
|
||||
* Get day name
|
||||
* Yesterday, Today, Tomorrow if the date lies within, else fallback to Monday - Sunday
|
||||
* @param {Object}
|
||||
* @return {String}
|
||||
*/
|
||||
const getDayName = ({
|
||||
y,
|
||||
m,
|
||||
d,
|
||||
_,
|
||||
dayName,
|
||||
short = false,
|
||||
}: {
|
||||
y: number
|
||||
m: number
|
||||
d: number
|
||||
_: string
|
||||
dayName: string
|
||||
short?: boolean
|
||||
}) => {
|
||||
const today = new Date()
|
||||
const yesterday = new Date()
|
||||
const tomorrow = new Date()
|
||||
const dateAccessor = (_ + "Date") as "getDate" | "getUTCDate"
|
||||
const monthAccessor = (_ + "Month") as "getMonth" | "getUTCMonth"
|
||||
const yearAccessor = (_ + "FullYear") as "getFullYear" | "getUTCFullYear"
|
||||
yesterday.setDate(yesterday[dateAccessor]() - 1)
|
||||
tomorrow.setDate(tomorrow[dateAccessor]() + 1)
|
||||
const today_d = () => today[dateAccessor]()
|
||||
const today_m = () => today[monthAccessor]()
|
||||
const today_y = () => today[yearAccessor]()
|
||||
const yesterday_d = () => yesterday[dateAccessor]()
|
||||
const yesterday_m = () => yesterday[monthAccessor]()
|
||||
const yesterday_y = () => yesterday[yearAccessor]()
|
||||
const tomorrow_d = () => tomorrow[dateAccessor]()
|
||||
const tomorrow_m = () => tomorrow[monthAccessor]()
|
||||
const tomorrow_y = () => tomorrow[yearAccessor]()
|
||||
|
||||
if (today_y() === y && today_m() === m && today_d() === d) {
|
||||
return short ? "Tdy" : "Today"
|
||||
} else if (
|
||||
yesterday_y() === y &&
|
||||
yesterday_m() === m &&
|
||||
yesterday_d() === d
|
||||
) {
|
||||
return short ? "Ysd" : "Yesterday"
|
||||
} else if (tomorrow_y() === y && tomorrow_m() === m && tomorrow_d() === d) {
|
||||
return short ? "Tmw" : "Tomorrow"
|
||||
}
|
||||
return dayName
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ISO 8601 week number
|
||||
* Based on comments from
|
||||
* http://techblog.procurios.nl/k/n618/news/view/33796/14863/Calculate-ISO-8601-week-and-year-in-javascript.html
|
||||
*
|
||||
* @param {Date} `date`
|
||||
* @return {Number}
|
||||
*/
|
||||
const getWeek = (date: Date) => {
|
||||
// Remove time components of date
|
||||
const targetThursday = new Date(
|
||||
date.getFullYear(),
|
||||
date.getMonth(),
|
||||
date.getDate(),
|
||||
)
|
||||
|
||||
// Change date to Thursday same week
|
||||
targetThursday.setDate(
|
||||
targetThursday.getDate() - ((targetThursday.getDay() + 6) % 7) + 3,
|
||||
)
|
||||
|
||||
// Take January 4th as it is always in week 1 (see ISO 8601)
|
||||
const firstThursday = new Date(targetThursday.getFullYear(), 0, 4)
|
||||
|
||||
// Change date to Thursday same week
|
||||
firstThursday.setDate(
|
||||
firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3,
|
||||
)
|
||||
|
||||
// Check if daylight-saving-time-switch occurred and correct for it
|
||||
const ds =
|
||||
targetThursday.getTimezoneOffset() - firstThursday.getTimezoneOffset()
|
||||
targetThursday.setHours(targetThursday.getHours() - ds)
|
||||
|
||||
// Number of weeks between target Thursday and first Thursday
|
||||
const weekDiff =
|
||||
(targetThursday.getTime() - firstThursday.getTime()) / (86400000 * 7)
|
||||
return 1 + Math.floor(weekDiff)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ISO-8601 numeric representation of the day of the week
|
||||
* 1 (for Monday) through 7 (for Sunday)
|
||||
*
|
||||
* @param {Date} `date`
|
||||
* @return {Number}
|
||||
*/
|
||||
const getDayOfWeek = (date: Date) => {
|
||||
let dow = date.getDay()
|
||||
if (dow === 0) {
|
||||
dow = 7
|
||||
}
|
||||
return dow
|
||||
}
|
||||
|
||||
/**
|
||||
* Get proper timezone abbreviation or timezone offset.
|
||||
*
|
||||
* This will fall back to `GMT+xxxx` if it does not recognize the
|
||||
* timezone within the `timezone` RegEx above. Currently only common
|
||||
* American and Australian timezone abbreviations are supported.
|
||||
*
|
||||
* @param {String | Date} date
|
||||
* @return {String}
|
||||
*/
|
||||
export const formatTimezone = (date: Date) => {
|
||||
const strDate = String(date)
|
||||
const match = strDate.match(timezone) || [""]
|
||||
return match
|
||||
.pop()
|
||||
?.replace(timezoneClip, "")
|
||||
.replace(/GMT\+0000/g, "UTC")
|
||||
}
|
||||
|
||||
export { dateFormat as DateFormatter }
|
||||
337
src/domHelper.ts
337
src/domHelper.ts
@@ -1,340 +1,3 @@
|
||||
import { Children } from "react"
|
||||
import {
|
||||
CarouselProps,
|
||||
ArPopoverPositions,
|
||||
ObjectType,
|
||||
Position,
|
||||
} from "@armco/types"
|
||||
|
||||
const style = window.getComputedStyle(document.documentElement)
|
||||
const fontSize = parseFloat(style.fontSize)
|
||||
const markerSize = 7
|
||||
|
||||
export function outerWidth(el: HTMLElement) {
|
||||
let width = el.offsetWidth
|
||||
const style = getComputedStyle(el)
|
||||
|
||||
width += parseInt(style.marginLeft) + parseInt(style.marginRight)
|
||||
return width
|
||||
}
|
||||
|
||||
export function translate(
|
||||
position: number,
|
||||
metric: "px" | "%",
|
||||
axis: "horizontal" | "vertical",
|
||||
) {
|
||||
const positionPercent = position === 0 ? position : position + metric
|
||||
const positionCss =
|
||||
axis === "horizontal" ? [positionPercent, 0, 0] : [0, positionPercent, 0]
|
||||
const transitionProp = "translate3d"
|
||||
|
||||
const translatedPosition = "(" + positionCss.join(",") + ")"
|
||||
|
||||
return transitionProp + translatedPosition
|
||||
}
|
||||
|
||||
export function hexToHsl(color: string, returnRaw?: boolean) {
|
||||
const [r, g, b] = hexToRgb(color)
|
||||
return rgbToHsl(r, g, b, returnRaw)
|
||||
}
|
||||
|
||||
export function hexToRgb(color: string) {
|
||||
const r = parseInt(color.substring(1, 3), 16) // Grab the hex representation of red (chars 1-2) and convert to decimal (base 10).
|
||||
const g = parseInt(color.substring(3, 5), 16)
|
||||
const b = parseInt(color.substring(5, 7), 16)
|
||||
return [r, g, b]
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an RGB color value to HSL. Conversion formula
|
||||
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||
* Assumes r, g, and b are contained in the set [0, 255] and
|
||||
* returns h, s, and l in the set [0, 1].
|
||||
*
|
||||
* @param Number r The red color value
|
||||
* @param Number g The green color value
|
||||
* @param Number b The blue color value
|
||||
* @return Array The HSL representation
|
||||
*/
|
||||
export function rgbToHsl(r: number, g: number, b: number, returnRaw?: boolean) {
|
||||
r /= 255
|
||||
g /= 255
|
||||
b /= 255
|
||||
|
||||
const max = Math.max(r, g, b),
|
||||
min = Math.min(r, g, b)
|
||||
let h,
|
||||
s,
|
||||
l = (max + min) / 2
|
||||
|
||||
if (max === min) {
|
||||
h = s = 0 // achromatic
|
||||
} else {
|
||||
const d = max - min
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
|
||||
|
||||
switch (max) {
|
||||
case r:
|
||||
h = (g - b) / d + (g < b ? 6 : 0)
|
||||
break
|
||||
case g:
|
||||
h = (b - r) / d + 2
|
||||
break
|
||||
case b:
|
||||
h = (r - g) / d + 4
|
||||
break
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
h /= 6
|
||||
}
|
||||
const colorInHSL = "hsl(" + h + ", " + s + "%, " + l + "%)"
|
||||
return returnRaw ? [h, s, l] : colorInHSL
|
||||
}
|
||||
|
||||
export function hslToRgb(h: number, s: number, l: number) {
|
||||
let r, g, b
|
||||
let hue2rgb
|
||||
if (s === 0) {
|
||||
r = g = b = l // achromatic
|
||||
} else {
|
||||
hue2rgb = (p: number, q: number, t: number) => {
|
||||
if (t < 0) t += 1
|
||||
if (t > 1) t -= 1
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t
|
||||
if (t < 1 / 2) return q
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6
|
||||
return p
|
||||
}
|
||||
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s
|
||||
const p = 2 * l - q
|
||||
|
||||
r = hue2rgb && hue2rgb(p, q, h + 1 / 3)
|
||||
g = hue2rgb && hue2rgb(p, q, h)
|
||||
b = hue2rgb && hue2rgb(p, q, h - 1 / 3)
|
||||
}
|
||||
|
||||
return [r * 255, g * 255, b * 255]
|
||||
}
|
||||
|
||||
export function rgbToHex(r: number, g: number, b: number) {
|
||||
function componentToHex(c: number) {
|
||||
var hex = c.toString(16)
|
||||
return hex.length === 1 ? "0" + hex : hex
|
||||
}
|
||||
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b)
|
||||
}
|
||||
|
||||
export function hslToHex(h: number, s: number, l: number) {
|
||||
const [r, g, b] = hslToRgb(h, s, l)
|
||||
return rgbToHex(Math.round(r), Math.round(g), Math.round(b))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list 'position' relative to a current index
|
||||
* @param index
|
||||
*/
|
||||
export function getPosition(index: number, props: CarouselProps): number {
|
||||
if (props.infiniteLoop) {
|
||||
// index has to be added by 1 because of the first cloned slide
|
||||
++index
|
||||
}
|
||||
|
||||
if (index === 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const childrenLength = Children.count(props.children)
|
||||
if (props.centerMode && props.axis === "horizontal") {
|
||||
let currentPosition = -index * props.centerSlidePercentage
|
||||
const lastPosition = childrenLength - 1
|
||||
|
||||
if (index && (index !== lastPosition || props.infiniteLoop)) {
|
||||
currentPosition += (100 - props.centerSlidePercentage) / 2
|
||||
} else if (index === lastPosition) {
|
||||
currentPosition += 100 - props.centerSlidePercentage
|
||||
}
|
||||
|
||||
return currentPosition
|
||||
}
|
||||
|
||||
return -index * 100
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the 'position' transform for sliding animations
|
||||
* @param position
|
||||
* @param forceReflow
|
||||
*/
|
||||
export function setPosition(
|
||||
position: number,
|
||||
axis: "horizontal" | "vertical",
|
||||
): React.CSSProperties {
|
||||
const style = {}
|
||||
;[
|
||||
"WebkitTransform",
|
||||
"MozTransform",
|
||||
"MsTransform",
|
||||
"OTransform",
|
||||
"transform",
|
||||
"msTransform",
|
||||
].forEach((prop) => {
|
||||
// @ts-ignore
|
||||
style[prop] = CSSTranslate(position, "%", axis)
|
||||
})
|
||||
|
||||
return style
|
||||
}
|
||||
|
||||
export function isKeyboardEvent(
|
||||
e?: React.MouseEvent | React.KeyboardEvent,
|
||||
): e is React.KeyboardEvent {
|
||||
return e ? e.hasOwnProperty("key") : false
|
||||
}
|
||||
|
||||
export function shouldUsePosition(
|
||||
position: ArPopoverPositions,
|
||||
anchorRect: DOMRect,
|
||||
popoverRect: DOMRect,
|
||||
) {
|
||||
const exceeds: ObjectType = {
|
||||
bottom:
|
||||
anchorRect.top + anchorRect.height + popoverRect.height >
|
||||
window.innerHeight,
|
||||
right:
|
||||
anchorRect.left + anchorRect.width + popoverRect.width >
|
||||
window.innerWidth,
|
||||
left: anchorRect.left - popoverRect.width < 0,
|
||||
top: anchorRect.top - popoverRect.height < 0,
|
||||
}
|
||||
if (exceeds[position]) return false
|
||||
if (position === "left" || position === "right") {
|
||||
if (fontSize > anchorRect.top || fontSize > anchorRect.bottom) return false
|
||||
} else {
|
||||
if (fontSize > anchorRect.left || fontSize > anchorRect.right) return false
|
||||
}
|
||||
return position
|
||||
}
|
||||
|
||||
export function adjustPosition(
|
||||
position: ArPopoverPositions,
|
||||
anchorRect: DOMRect,
|
||||
popoverRect: DOMRect,
|
||||
hideMarker?: boolean,
|
||||
topOffset?: number,
|
||||
) {
|
||||
const popoverPositions: { [key: string]: Position } = {
|
||||
[ArPopoverPositions.BOTTOM]: {
|
||||
top:
|
||||
anchorRect.top +
|
||||
anchorRect.height +
|
||||
(hideMarker ? 0 : markerSize) +
|
||||
(topOffset || 0),
|
||||
left: anchorRect.left + (anchorRect.width - popoverRect.width) / 2,
|
||||
},
|
||||
[ArPopoverPositions.RIGHT]: {
|
||||
top: anchorRect.top + (anchorRect.height - popoverRect.height) / 2,
|
||||
left: anchorRect.left + anchorRect.width + (hideMarker ? 0 : markerSize),
|
||||
},
|
||||
[ArPopoverPositions.LEFT]: {
|
||||
top: anchorRect.top + (anchorRect.height - popoverRect.height) / 2,
|
||||
left: anchorRect.left - popoverRect.width - (hideMarker ? 0 : markerSize),
|
||||
},
|
||||
[ArPopoverPositions.TOP]: {
|
||||
top:
|
||||
anchorRect.top -
|
||||
popoverRect.height -
|
||||
(hideMarker ? 0 : markerSize) -
|
||||
(topOffset || 0),
|
||||
left: anchorRect.left + (anchorRect.width - popoverRect.width) / 2,
|
||||
},
|
||||
}
|
||||
let { left, top } = popoverPositions[position]
|
||||
if (
|
||||
position === ArPopoverPositions.BOTTOM ||
|
||||
position === ArPopoverPositions.TOP
|
||||
) {
|
||||
if ((left as number) < 0) {
|
||||
left = fontSize
|
||||
}
|
||||
if ((left as number) + popoverRect.width > window.innerWidth) {
|
||||
left = `calc(100% - ${popoverRect.width}px - ${fontSize}px)`
|
||||
}
|
||||
} else {
|
||||
if ((top as number) < 0) {
|
||||
top = fontSize
|
||||
}
|
||||
if ((top as number) + popoverRect.height > window.innerHeight) {
|
||||
top = `calc(100% - ${popoverRect.height}px - ${fontSize}px)`
|
||||
}
|
||||
}
|
||||
return { left, top }
|
||||
}
|
||||
|
||||
export function getPositionToUse(
|
||||
anchorRect: DOMRect,
|
||||
popoverRect: DOMRect,
|
||||
requestedPosition?: ArPopoverPositions,
|
||||
) {
|
||||
if (requestedPosition && requestedPosition !== ArPopoverPositions.AUTO) {
|
||||
return requestedPosition
|
||||
}
|
||||
const positionsPriority = [
|
||||
ArPopoverPositions.BOTTOM,
|
||||
ArPopoverPositions.RIGHT,
|
||||
ArPopoverPositions.LEFT,
|
||||
ArPopoverPositions.TOP,
|
||||
]
|
||||
|
||||
for (const position of positionsPriority) {
|
||||
if (shouldUsePosition(position, anchorRect, popoverRect)) {
|
||||
return position
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
return ArPopoverPositions.BOTTOM
|
||||
}
|
||||
|
||||
export function calculatePopoverPosition(
|
||||
anchorRect: DOMRect,
|
||||
popoverRect: DOMRect,
|
||||
requestedPosition?: ArPopoverPositions,
|
||||
hideMarker?: boolean,
|
||||
clickCoordinates?: Array<number> | null,
|
||||
topOffset?: number,
|
||||
) {
|
||||
anchorRect = clickCoordinates
|
||||
? {
|
||||
left: clickCoordinates[0],
|
||||
x: clickCoordinates[0],
|
||||
top: clickCoordinates[1],
|
||||
y: clickCoordinates[1],
|
||||
height: 0,
|
||||
width: 0,
|
||||
right: window.screen.width - clickCoordinates[0],
|
||||
bottom: window.screen.height - clickCoordinates[1],
|
||||
toJSON: () => {},
|
||||
}
|
||||
: anchorRect
|
||||
const position = getPositionToUse(anchorRect, popoverRect, requestedPosition)
|
||||
const { left, top } = adjustPosition(
|
||||
position,
|
||||
anchorRect,
|
||||
popoverRect,
|
||||
hideMarker,
|
||||
topOffset,
|
||||
)
|
||||
return {
|
||||
leftTop: {
|
||||
top: ("" + top).startsWith("calc") ? top : top + "px",
|
||||
left: ("" + left).startsWith("calc") ? left : left + "px",
|
||||
},
|
||||
position,
|
||||
}
|
||||
}
|
||||
|
||||
export function openInNewTab(url: string) {
|
||||
const win = window.open(url, "_blank")
|
||||
if (win != null) {
|
||||
|
||||
17
src/enums.ts
Normal file
17
src/enums.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export enum ArThemes {
|
||||
LIGHT1 = "th-light-1",
|
||||
DARK1 = "th-dark-1",
|
||||
}
|
||||
|
||||
export enum ArAlertType {
|
||||
SUCCESS = "success",
|
||||
WARNING = "warning",
|
||||
ERROR = "error",
|
||||
INFORMATION = "information",
|
||||
}
|
||||
|
||||
|
||||
export enum RecusionConditionTypes {
|
||||
KEY_EXISTS = "keyExists",
|
||||
KEY_VALUE = "keyValue",
|
||||
}
|
||||
327
src/helper.tsx
327
src/helper.tsx
@@ -2,27 +2,14 @@ import { JSX, lazy } from "react"
|
||||
import {
|
||||
FunctionType,
|
||||
ObjectType,
|
||||
SearchArgs,
|
||||
RouteConfig,
|
||||
} from "@armco/types"
|
||||
|
||||
const validImageMimeTypes = [
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/gif",
|
||||
"image/webp",
|
||||
"image/svg+xml",
|
||||
"image/tiff",
|
||||
"image/bmp",
|
||||
"image/x-icon",
|
||||
]
|
||||
|
||||
interface ReplacerFunction {
|
||||
(key: string, value: any): any
|
||||
}
|
||||
|
||||
interface StringifyOnce {
|
||||
(obj: any, replacer?: ReplacerFunction | null, indent?: number): string
|
||||
export interface SearchArgs {
|
||||
obj: ObjectType | Array<ObjectType>
|
||||
key: string
|
||||
value?: string
|
||||
parent?: ObjectType | Array<ObjectType>
|
||||
}
|
||||
|
||||
export function populatePagesInRoutes(
|
||||
@@ -68,6 +55,17 @@ export function populateComponentsInRoutes(
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* WHAT: Recursively filters through an array of objects based on a filter string.
|
||||
* HOW: Checks specified keys in each object for a match with the filter string.
|
||||
* If an object has children, it recursively filters the children as well.
|
||||
* An object is included in the result if it or any of its children match the filter.
|
||||
* @param data
|
||||
* @param filter
|
||||
* @param matchCase
|
||||
* @param searchKeys
|
||||
* @returns
|
||||
*/
|
||||
export function recrusiveFilter(
|
||||
data: Array<any>,
|
||||
filter: string,
|
||||
@@ -103,6 +101,14 @@ export function recrusiveFilter(
|
||||
return filteredItems
|
||||
}
|
||||
|
||||
/**
|
||||
* WHAT: Searches through a nested object or array to find an object with a specific key-value pair.
|
||||
* HOW: Recursively traverses the object/array, checking each object's keys and values.
|
||||
* If the specified key is found and its value matches (if provided), the object is returned.
|
||||
* If the object contains nested objects or arrays, the function calls itself on those nested structures.
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
export function search({
|
||||
obj,
|
||||
key,
|
||||
@@ -127,121 +133,13 @@ export function search({
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function searchBy(
|
||||
obj: any,
|
||||
query: Record<string, string>,
|
||||
matchType: "AND" | "OR" = "AND",
|
||||
): ObjectType | undefined {
|
||||
// Helper function to check if an object matches the query
|
||||
const matchesQuery = (item: any): boolean => {
|
||||
const conditions = Object.entries(query).map(([key, value]) => {
|
||||
return key in item && item[key] === value;
|
||||
});
|
||||
|
||||
// Match all conditions (AND) or any condition (OR)
|
||||
return matchType === "AND" ? conditions.every(Boolean) : conditions.some(Boolean);
|
||||
};
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
// If obj is an array, recursively search each item
|
||||
for (const item of obj) {
|
||||
const result = searchBy(item, query, matchType);
|
||||
if (result) return result;
|
||||
}
|
||||
} else if (obj && typeof obj === "object" && !(obj instanceof Date)) {
|
||||
// If obj is an object, check if it matches the query
|
||||
if (matchesQuery(obj)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Recursively search through the object's values
|
||||
for (const value of Object.values(obj)) {
|
||||
if (typeof value === "object" && !(value instanceof Date)) {
|
||||
const result = searchBy(value, query, matchType);
|
||||
if (result) return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function filterTreeStructure(
|
||||
args: SearchArgs,
|
||||
): boolean | Array<ObjectType> | ObjectType | void {
|
||||
let { obj, key, value } = args
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.filter((item, index) => {
|
||||
if (typeof item === "object" && !(item instanceof Date)) {
|
||||
args.obj = item
|
||||
|
||||
const match = filterTreeStructure(args)
|
||||
if (Array.isArray(match) && match.length > 0) {
|
||||
; (obj as unknown as Array<Array<ObjectType>>).splice(index, 1, match)
|
||||
}
|
||||
return match
|
||||
}
|
||||
return false
|
||||
})
|
||||
} else {
|
||||
if (obj) {
|
||||
let found = false
|
||||
if (key in obj && (value === undefined || obj[key] === value)) {
|
||||
obj.hasMatched = true
|
||||
}
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const item = (obj as ObjectType)[key]
|
||||
if (typeof item === "object" && !(item instanceof Date)) {
|
||||
args.obj = item as ObjectType
|
||||
|
||||
const match = filterTreeStructure(args)
|
||||
if (Array.isArray(match) ? match.length > 0 : match) {
|
||||
found = true
|
||||
} else {
|
||||
delete (obj as ObjectType)[key]
|
||||
}
|
||||
if (Array.isArray(match) && match.length > 0) {
|
||||
; (obj as ObjectType)[key] = match
|
||||
}
|
||||
} else (obj as ObjectType).hasMatched || delete (obj as ObjectType)[key]
|
||||
})
|
||||
return (obj.hasMatched as boolean) || found
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function aggregate(
|
||||
data: any,
|
||||
aggregator: string,
|
||||
): { [key: string]: Array<string> } {
|
||||
let aggregated: { [key: string]: Array<string> } = {}
|
||||
data.forEach((item: any) => {
|
||||
const key = item[aggregator]
|
||||
let aggregatedArray: Array<string> | undefined = aggregated[key]
|
||||
if (!aggregatedArray) {
|
||||
aggregatedArray = []
|
||||
aggregated[key] = aggregatedArray
|
||||
}
|
||||
aggregatedArray.push(item)
|
||||
})
|
||||
return aggregated
|
||||
}
|
||||
|
||||
export function generateCategories(
|
||||
data: any,
|
||||
categories: Array<string> | undefined,
|
||||
): { [key: string]: { [key: string]: Array<string> } } {
|
||||
const groups: { [key: string]: { [key: string]: Array<string> } } = {}
|
||||
if (categories && data) {
|
||||
categories.forEach((key) => {
|
||||
let group: { [key: string]: Array<string> } = aggregate(data, key)
|
||||
groups[key] = group
|
||||
})
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
/**
|
||||
* WHAT: Converts a string to camelCase format.
|
||||
* @param str
|
||||
* @param separater
|
||||
* @param includeFirst
|
||||
* @returns
|
||||
*/
|
||||
export function toCamelCase(
|
||||
str: string,
|
||||
separater: string,
|
||||
@@ -257,12 +155,11 @@ export function toCamelCase(
|
||||
.join("")
|
||||
}
|
||||
|
||||
export function generateRandomId(length: number = 8) {
|
||||
return Math.random()
|
||||
.toString(36)
|
||||
.substring(2, length + 2)
|
||||
}
|
||||
|
||||
/**
|
||||
* WHAT: Copies text to clipboard or prompts user to copy if clipboard access is not available.
|
||||
* @param text
|
||||
* @param cb
|
||||
*/
|
||||
export function copyOrPrompt(text?: string, cb?: FunctionType) {
|
||||
if (copyToClipboard(text)) {
|
||||
cb && cb()
|
||||
@@ -271,6 +168,11 @@ export function copyOrPrompt(text?: string, cb?: FunctionType) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WHAT: Copies text to clipboard if supported by the browser.
|
||||
* @param text
|
||||
* @returns
|
||||
*/
|
||||
export function copyToClipboard(text?: string) {
|
||||
const isNotSafari = typeof (window as any).safari === "undefined"
|
||||
if (isNotSafari) {
|
||||
@@ -281,40 +183,6 @@ export function copyToClipboard(text?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function matchArrayFilters(
|
||||
searchList: Array<any> | undefined,
|
||||
filters: Array<any> | false | undefined,
|
||||
match: string,
|
||||
type?: string,
|
||||
) {
|
||||
if (filters && searchList) {
|
||||
return type && type === "starts"
|
||||
? searchList.filter(
|
||||
(item) =>
|
||||
filters.findIndex((al) =>
|
||||
item[match].toLowerCase().startsWith(al.toLowerCase()),
|
||||
) > -1,
|
||||
)
|
||||
: searchList.filter(
|
||||
(item) =>
|
||||
filters.findIndex(
|
||||
(al) => item[match].toLowerCase() === al.value.toLowerCase(),
|
||||
) > -1,
|
||||
)
|
||||
}
|
||||
return searchList
|
||||
}
|
||||
|
||||
export function importComponent(
|
||||
name: string,
|
||||
hierarchy: string,
|
||||
fallback: FunctionType,
|
||||
) {
|
||||
return lazy(() =>
|
||||
import(`../components/${hierarchy}/${name}/${name}.tsx`).catch(fallback),
|
||||
)
|
||||
}
|
||||
|
||||
export function getStylesFromClass(className: string): Record<string, string> {
|
||||
const styleSheet = Array.from(document.styleSheets).find((sheet) =>
|
||||
Array.from(sheet.cssRules).some(
|
||||
@@ -342,6 +210,14 @@ export function getStylesFromClass(className: string): Record<string, string> {
|
||||
return {}
|
||||
}
|
||||
|
||||
/**
|
||||
* WHAT: Creates a debounced version of a function that delays its execution until after a specified wait time has elapsed since the last time it was invoked.
|
||||
* HOW: Uses a timeout to track the delay period. If the debounced function is called again before the timeout expires, the previous timeout is cleared and a new one is set.
|
||||
* @param func
|
||||
* @param wait
|
||||
* @param immediate
|
||||
* @returns
|
||||
*/
|
||||
export function debounce<T extends (...args: any[]) => void>(
|
||||
func: T,
|
||||
wait?: number,
|
||||
@@ -368,6 +244,11 @@ export function debounce<T extends (...args: any[]) => void>(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WHAT: Detects if the current device is a mobile device based on the user agent string.
|
||||
* HOW: Uses a regular expression to match common mobile device identifiers in the user agent string.
|
||||
* @returns
|
||||
*/
|
||||
export function isMobile(): boolean {
|
||||
let check = false
|
||||
; (function (a: string) {
|
||||
@@ -407,98 +288,28 @@ export function clearCookie(cname: string) {
|
||||
document.cookie = cname + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
|
||||
}
|
||||
|
||||
/**
|
||||
* Pads a number with leading zeros to ensure it has at least two digits.
|
||||
* Optionally appends a separator at the end.
|
||||
* @param num
|
||||
* @param separator
|
||||
* @returns
|
||||
*/
|
||||
export function pad(num: number, separator?: string) {
|
||||
return ("" + num).padStart(2, "0") + (separator ? separator : "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a string and converts it to a more readable format.
|
||||
* HOW: Splits camelCase words and capitalizes the first letter of each word.
|
||||
*
|
||||
* @param str Input string to be converted
|
||||
* @returns Converted string
|
||||
*/
|
||||
export function toReadable(str?: string) {
|
||||
return str
|
||||
?.replace(/([a-z])([A-Z])/g, "$1 $2")
|
||||
.split(" ")
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
export function generateFileKey(file: File | { name: string }) {
|
||||
return "size" in file
|
||||
? `${file.name}_${file.size}_${file.type}_${file.lastModified}`
|
||||
: file.name + "-preexisting-" + Date.now()
|
||||
}
|
||||
|
||||
export function convertToBytes(input: string): number {
|
||||
const trimmedInput = input.trim().toLowerCase()
|
||||
|
||||
try {
|
||||
if (trimmedInput.endsWith("kb")) {
|
||||
const number = parseFloat(trimmedInput.slice(0, -2))
|
||||
return number * 1024
|
||||
} else if (trimmedInput.endsWith("mb")) {
|
||||
const number = parseFloat(trimmedInput.slice(0, -2))
|
||||
return number * 1024 * 1024
|
||||
} else if (trimmedInput.endsWith("bytes")) {
|
||||
const number = parseFloat(trimmedInput.slice(0, -5))
|
||||
return number
|
||||
} else if (!isNaN(parseFloat(trimmedInput))) {
|
||||
return parseFloat(trimmedInput)
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
} catch (error) {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
export function isImage(type: string) {
|
||||
return !!type && validImageMimeTypes.indexOf(type) > -1
|
||||
}
|
||||
|
||||
export const stringifyOnce: StringifyOnce = (obj, replacer, indent) => {
|
||||
const printedObjects: any[] = []
|
||||
const printedObjectKeys: string[] = []
|
||||
|
||||
function printOnceReplacer(key: string, value: any): any {
|
||||
if (printedObjects.length > 2000) {
|
||||
// browsers will not print more than 20K, I don't see the point to allow 2K.. algorithm will not be fast anyway if we have too many objects
|
||||
return "object too long"
|
||||
}
|
||||
|
||||
let printedObjIndex: number | false = false
|
||||
printedObjects.forEach((obj, index) => {
|
||||
if (obj === value) {
|
||||
printedObjIndex = index
|
||||
}
|
||||
})
|
||||
|
||||
if (key === "") {
|
||||
// root element
|
||||
printedObjects.push(obj)
|
||||
printedObjectKeys.push("root")
|
||||
return value
|
||||
} else if (printedObjIndex !== false && typeof value === "object") {
|
||||
if (printedObjectKeys[printedObjIndex] === "root") {
|
||||
return "(pointer to root)"
|
||||
} else {
|
||||
return (
|
||||
"(see " +
|
||||
(!!value && !!value.constructor
|
||||
? value.constructor.name.toLowerCase()
|
||||
: typeof value) +
|
||||
" with key " +
|
||||
printedObjectKeys[printedObjIndex] +
|
||||
")"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
const qualifiedKey = key || "(empty key)"
|
||||
printedObjects.push(value)
|
||||
printedObjectKeys.push(qualifiedKey)
|
||||
if (replacer) {
|
||||
return replacer(key, value)
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(obj, printOnceReplacer, indent)
|
||||
}
|
||||
}
|
||||
53
src/hooks.ts
53
src/hooks.ts
@@ -24,59 +24,6 @@ export function useSlotted(componentName: string) {
|
||||
setSlotted([...slotted, componentName])
|
||||
}
|
||||
|
||||
export function useStateWithHistory<T>(
|
||||
initialState?: T,
|
||||
): [T | undefined, FunctionType, FunctionType, FunctionType, boolean, boolean] {
|
||||
const [past, setPast] = useState<T[]>([])
|
||||
const [present, setPresent] = useState<T | undefined>()
|
||||
const [future, setFuture] = useState<T[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
!present && setPresent(initialState)
|
||||
// eslint-disable-next-line
|
||||
}, [initialState])
|
||||
|
||||
const undo = useCallback(() => {
|
||||
if (past.length === 0) return
|
||||
|
||||
const newPast = [...past]
|
||||
const newPresent = newPast.pop()
|
||||
|
||||
setPast(newPast)
|
||||
present && setFuture([present, ...future])
|
||||
setPresent(newPresent)
|
||||
}, [future, past, present])
|
||||
|
||||
const redo = useCallback(() => {
|
||||
if (future.length === 0) return
|
||||
|
||||
const newFuture = [...future]
|
||||
const newPresent = newFuture.shift()
|
||||
|
||||
past && present && setPast([...past, present])
|
||||
setFuture(newFuture)
|
||||
setPresent(newPresent)
|
||||
}, [future, past, present])
|
||||
|
||||
const updatePresent = useCallback(
|
||||
(newState: any, skipHistory?: boolean) => {
|
||||
!skipHistory && past && present && setPast([...past, present])
|
||||
setPresent(newState)
|
||||
!skipHistory && setFuture([])
|
||||
},
|
||||
[past, present],
|
||||
)
|
||||
|
||||
return [
|
||||
present,
|
||||
updatePresent,
|
||||
undo,
|
||||
redo,
|
||||
past.length > 0,
|
||||
future.length > 0,
|
||||
]
|
||||
}
|
||||
|
||||
export const useTheme = (): {
|
||||
theme: ArThemes
|
||||
setTheme: (theme: ArThemes) => void
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
export * from "./dateformat"
|
||||
export * from "./domHelper"
|
||||
export * from "./helper"
|
||||
export * from "./network"
|
||||
export * from "./validators"
|
||||
export * from "../../taskerclient/src/validators"
|
||||
export * from "./recursionHelper"
|
||||
export * from "./chartGenerators"
|
||||
export * from "./hooks"
|
||||
export * from "./contexts"
|
||||
export * from "./providers"
|
||||
export * from "./HOC"
|
||||
export * from "./enums"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { v4 as uuid } from "uuid"
|
||||
import { FunctionType, ObjectType, RecusionConditionTypes } from "@armco/types"
|
||||
import { FunctionType, ObjectType } from "@armco/types"
|
||||
import { RecusionConditionTypes } from "./enums"
|
||||
|
||||
export interface RecursionArgs {
|
||||
data: any
|
||||
|
||||
50
src/types.ts
Normal file
50
src/types.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { CSSProperties, ReactNode } from "react"
|
||||
import { ArAlertType } from "./enums"
|
||||
import { BaseProps, FunctionType } from "@armco/types"
|
||||
|
||||
export interface DrawerProps extends BaseProps {
|
||||
children?: ReactNode
|
||||
collapsed?: boolean
|
||||
contentClasses?: string
|
||||
isCollapsible?: boolean
|
||||
title?: string
|
||||
}
|
||||
|
||||
export interface ModalProps extends BaseProps {
|
||||
body?: ReactNode
|
||||
closeHandler?: FunctionType
|
||||
content?: ReactNode
|
||||
contentClasses?: string
|
||||
footer?: ReactNode
|
||||
header?: ReactNode
|
||||
hideClose?: boolean
|
||||
isSticky?: boolean
|
||||
modalClasses?: string
|
||||
primaryButtonLabel?: string
|
||||
primaryHandler?: FunctionType
|
||||
secondaryButtonLabel?: string
|
||||
shouldScale?: boolean
|
||||
shouldFadeIn?: boolean
|
||||
show?: boolean
|
||||
}
|
||||
|
||||
// Contexts
|
||||
export interface ArContextType {
|
||||
theme: ArThemes
|
||||
modalState?: ModalProps
|
||||
notification?: AlertProps
|
||||
drawerState?: DrawerProps
|
||||
isLoggedIn?: boolean
|
||||
user?: User | null
|
||||
leftPanelContent?: PanelContent
|
||||
setLeftPanelContent: (leftPanelContent: PanelContent) => void
|
||||
rightPanelContent?: PanelContent
|
||||
setRightPanelContent: (rightPanelContent: PanelContent) => void
|
||||
setTheme: (theme: ArThemes) => void
|
||||
setLoggedIn: (isLoggedIn: boolean) => void
|
||||
setUser: (user: User | null) => void
|
||||
clearUser: () => void
|
||||
setModalState: (modalState: ModalProps | undefined) => void
|
||||
notify: (notification: AlertProps | undefined) => void
|
||||
setDrawerState: (drawerState: DrawerProps) => void
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { ObjectType } from "@armco/types"
|
||||
|
||||
export function validateTask(taskObj: ObjectType) {
|
||||
return (
|
||||
taskObj &&
|
||||
taskObj.name &&
|
||||
taskObj.client &&
|
||||
taskObj.target &&
|
||||
(taskObj.target as ObjectType).endpoint &&
|
||||
taskObj.schedule &&
|
||||
(taskObj.schedule as ObjectType).cron
|
||||
)
|
||||
}
|
||||
@@ -23,5 +23,5 @@
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
, "../Calendar/src/dateHelper.ts" ]
|
||||
, "../taskerclient/src/validators.ts" ],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user