From c468c378831873c090922530552375f31ff453c7 Mon Sep 17 00:00:00 2001 From: Mohit Nagar Date: Thu, 5 Sep 2024 23:34:35 +0530 Subject: [PATCH 1/6] Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..5f5be52 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# utils +Armco frontend shared utilities From bbdb7894b2b23e7b8dbf3b0d8b8ebbca718889e5 Mon Sep 17 00:00:00 2001 From: mohiit1502 Date: Wed, 18 Sep 2024 16:31:20 +0530 Subject: [PATCH 2/6] Publishable version of configs completed --- .DS_Store | Bin 0 -> 6148 bytes .gitignore | 1 + package.json | 51 +++ publish.sh | 8 + src/HOC.tsx | 11 + src/adapters.ts | 128 +++++++ src/chartGenerators.ts | 45 +++ src/contexts.ts | 21 ++ src/dateHelper.ts | 546 +++++++++++++++++++++++++++++ src/dateformat.ts | 336 ++++++++++++++++++ src/domHelper.ts | 456 +++++++++++++++++++++++++ src/gridHelper.ts | 737 ++++++++++++++++++++++++++++++++++++++++ src/helper.tsx | 549 ++++++++++++++++++++++++++++++ src/hooks.ts | 170 +++++++++ src/index.ts | 14 + src/network.ts | 247 ++++++++++++++ src/providers.tsx | 53 +++ src/recursionHelper.tsx | 66 ++++ src/validators.ts | 17 + tsconfig.json | 4 + vite.config.ts | 34 ++ 21 files changed, 3494 insertions(+) create mode 100644 .DS_Store create mode 100644 .gitignore create mode 100644 package.json create mode 100755 publish.sh create mode 100644 src/HOC.tsx create mode 100644 src/adapters.ts create mode 100644 src/chartGenerators.ts create mode 100644 src/contexts.ts create mode 100644 src/dateHelper.ts create mode 100644 src/dateformat.ts create mode 100644 src/domHelper.ts create mode 100644 src/gridHelper.ts create mode 100644 src/helper.tsx create mode 100644 src/hooks.ts create mode 100644 src/index.ts create mode 100644 src/network.ts create mode 100644 src/providers.tsx create mode 100644 src/recursionHelper.tsx create mode 100644 src/validators.ts create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..76fe15cef025a58475821e92ed4421f4e13c75d1 GIT binary patch literal 6148 zcmeHK%}T>S5Z<+|O({YS3Oz1(Z87yy#Y>3w1&ruHr6#0kFlI}W*h4AgtS{t~_&m<+ zZlJ~BQN+%`?l(I>yO|HNKa4T%7159}n=xiVL*%H`2%2kMJ0=*Bt2v@@k6Q_|}c(O>NOy&C8VRfv|#O^JZ1E((r&Z;k#!_o0c zUkshm>1x%n4i1mbE+@~)ODf+qksKITvS+Y@cTm_UYz?rNl-7$62V8EELCjpzRb{4%wV z{LK^^5d*})KVyJ5$Nt!ZqRiR)tvozy1+)ifC>U3w0s{KRB>)DvkMxvN`vvL{=Nc?E U;w)%a>40<*P=ruN4EzEEU;7$Lxc~qF literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c795b05 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..7919338 --- /dev/null +++ b/package.json @@ -0,0 +1,51 @@ +{ + "name": "@armco/utils", + "version": "0.0.4", + "type": "module", + "scripts": { + "build": "rm -rf build && tsc && vite build", + "format": "prettier --write .", + "lint": "eslint .", + "publish:sh": "./publish.sh" + }, + "devDependencies": { + "react": "^18.3.1", + "typescript": "^5.0.2" + }, + "dependencies": { + "@armco/configs": "0.0.2" + }, + "peerDependencies": { + "react": ">16.8.1" + }, + "eslintConfig": { + "plugins": [ + "prettier" + ], + "rules": { + "prettier/prettier": "error" + } + }, + "prettier": "prettier-config-nick", + "main": "build/cjs/index.js", + "module": "build/es/index.js", + "types": "build/types/index.d.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/ReStruct-Corporate-Advantage/utils.git" + }, + "files": [ + "build" + ], + "keywords": [ + "components", + "atomic", + "building-blocks", + "foundation" + ], + "license": "ISC", + "bugs": { + "url": "https://github.com/ReStruct-Corporate-Advantage/utils/issues" + }, + "homepage": "https://github.com/ReStruct-Corporate-Advantage/utils#readme" +} diff --git a/publish.sh b/publish.sh new file mode 100755 index 0000000..a0d2fb4 --- /dev/null +++ b/publish.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +semver=${1:-patch} + +npm run build +set -e +npm --no-git-tag-version version ${semver} +npm publish --access public --loglevel verbose diff --git a/src/HOC.tsx b/src/HOC.tsx new file mode 100644 index 0000000..f8f0cef --- /dev/null +++ b/src/HOC.tsx @@ -0,0 +1,11 @@ +import { ComponentType } from "react" +import { useTheme } from "." // Adjust the import path as needed + +export const withTheme =

( + Component: ComponentType

, +): ComponentType

=> { + return (props: P) => { + const { theme, setTheme } = useTheme() + return + } +} diff --git a/src/adapters.ts b/src/adapters.ts new file mode 100644 index 0000000..fa4fb7d --- /dev/null +++ b/src/adapters.ts @@ -0,0 +1,128 @@ +import { + ComponentDescription, + ProgressiveChartData, + ThreeDChartDataArrayFormat, + ThreeDChartDataObjectFormat, + TreeListData, + RecusionConditionTypes, +} from "@armco/types" +import RecursionHelper from "./recursionHelper" + +const years = Array.from({ length: 60 }, (_, i) => 1964 + i) +const countries = ["IN", "US", "RU", "CN", "UK", "JP", "FR", "IT", "SP"] + +// Shuffle the years +for (let i = years.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)) + ;[years[i], years[j]] = [years[j], years[i]] +} + +const dummyProgressiveChartData: ThreeDChartDataArrayFormat = countries.flatMap( + (country) => { + return years.map((year) => { + return [year, country, Math.floor(Math.random() * 100000000000000)] as [ + string | number, + string | number, + number, + ] + }) + }, +) + +class Adapter { + static adaptToTreeFromComponentConfig(data: any): Array { + const returnTreeList: Array = [] + Object.keys(data).forEach((key) => { + const groupConfig = data[key as keyof any] + const components = groupConfig.components + components.sort((c1: ComponentDescription, c2: ComponentDescription) => + (c1.name as any) > (c2.name as any) ? 1 : -1, + ) + const obj: TreeListData = { + label: groupConfig.label, + children: [], + data: { ...groupConfig }, + } + components.forEach((item: any) => { + const treeItem: TreeListData = { + label: typeof item === "string" ? item : item.name, + data: { + component: typeof item === "string" ? item : item.name, + hierarchy: key, + }, + } + const children: Array = [] + treeItem.children = children + if (item.variants && Object.keys(item.variants.length > 0)) { + Object.keys(item.variants).forEach((variantKey) => { + const variants = item.variants[variantKey] + treeItem.children && + treeItem.children.push({ + label: variantKey, + data: { name: typeof item === "string" ? item : item.name }, + children: variants.map((v: string) => ({ + label: v, + data: { + component: typeof item === "string" ? item : item.name, + props: { [variantKey]: v }, + hierarchy: key, + }, + })), + }) + }) + } + obj.children && obj.children.push(treeItem) + }) + returnTreeList.push(obj) + }) + RecursionHelper.injectIds({ + data: returnTreeList, + condition: { type: RecusionConditionTypes.KEY_EXISTS, key: "label" }, + iterateOn: "children", + }) + return returnTreeList + } + + static adaptToProgressiveChart( + inputData: ProgressiveChartData, + demo?: boolean, + ): ThreeDChartDataObjectFormat { + if (demo && !inputData) { + inputData = dummyProgressiveChartData + } + let unsortedData: ThreeDChartDataObjectFormat = {} + + // Transform the data into the desired format + if (Array.isArray(inputData)) { + inputData.forEach(([key, xValue, yValue]) => { + if (!unsortedData[key]) { + unsortedData[key] = [[xValue, yValue]] + } else { + unsortedData[key].push([xValue, yValue]) + } + }) + } else { + unsortedData = inputData + } + + // Sort the keys + const keys = Object.keys(unsortedData) + keys.sort((a, b) => { + if (typeof a === "number" && typeof b === "number") { + return a - b + } else { + return a.localeCompare(b) + } + }) + + // Create a new object with the sorted keys + let sortedData: ThreeDChartDataObjectFormat = {} + keys.forEach((key) => { + sortedData[key] = unsortedData[key] + }) + + return sortedData + } +} + +export default Adapter diff --git a/src/chartGenerators.ts b/src/chartGenerators.ts new file mode 100644 index 0000000..6c221b0 --- /dev/null +++ b/src/chartGenerators.ts @@ -0,0 +1,45 @@ +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") + } +} diff --git a/src/contexts.ts b/src/contexts.ts new file mode 100644 index 0000000..ca75068 --- /dev/null +++ b/src/contexts.ts @@ -0,0 +1,21 @@ +import { createContext } from "react" +import { ArContextType, ArThemes, FunctionType } from "@armco/types" +import { Helper } from "." + +export const SlotterContext = createContext<{ + slotted: Array + setSlotted?: FunctionType +}>({ slotted: [] }) + +export const ArContext = createContext({ + theme: ArThemes.DARK1, + drawerState: { collapsed: Helper.isMobile() }, + notify: () => {}, + setDrawerState: () => {}, + setLeftPanelContent: () => {}, + setLoggedIn: () => {}, + setModalState: () => {}, + setRightPanelContent: () => {}, + setTheme: () => {}, + setUser: () => {}, +}) diff --git a/src/dateHelper.ts b/src/dateHelper.ts new file mode 100644 index 0000000..edf1e5b --- /dev/null +++ b/src/dateHelper.ts @@ -0,0 +1,546 @@ +import { CalendarDate, Event, ArDateFormats } from "@armco/types" +import { MONTH_INDEX } from "@armco/configs" +import { Helper } from "." + +export interface CalendarOptions { + /** + * Date object indicating the selected start date + */ + startDate?: CalendarDate | null + + /** + * Date object indicating the selected end date + */ + endDate?: CalendarDate | null + + /** + * Calculate dates from sibling months (before and after the current month, based on weekStart) + */ + siblingMonths?: boolean + + /** + * Calculate the week days + */ + weekNumbers?: boolean + + /** + * Day of the week to start the calendar, respects `Date.prototype.getDay` (defaults to `0`, Sunday) + */ + weekStart?: number +} + +export const sub = (num: number, separator?: string) => { + return ("" + num).substring(2) + (separator ? separator : "") +} + +export const str = (num: number, trim?: boolean, separator?: string) => { + const month = MONTH_INDEX[num] + return (trim ? month.substring(0, 4) : month) + (separator ? separator : "") +} + +export type CalendarInstance = Calendar +/** + * Calendar object + */ +class Calendar { + startDate: CalendarDate | null + endDate: CalendarDate | null + siblingMonths: boolean + weekNumbers: boolean + weekStart: number + + /** + * Calendar constructor + * + * @param options Calendar options + */ + constructor({ + startDate = null, + endDate = null, + siblingMonths = false, + weekNumbers = false, + weekStart = 0, + }: CalendarOptions = {}) { + this.startDate = startDate + this.endDate = endDate + this.siblingMonths = siblingMonths + this.weekNumbers = weekNumbers + this.weekStart = weekStart + } + + /** + * Calculate a calendar month + * + * @param year Year + * @param month Month [0-11] + * @return Calendar days + */ + getCalendar(year: number, month: number) { + const date = new Date(Date.UTC(year, month, 1, 0, 0, 0, 0)) + + year = date.getFullYear() + month = date.getMonth() + + const calendar: (CalendarDate | false)[] = [] + const firstDay = date.getDay() + const firstDate = -((7 - this.weekStart + firstDay) % 7) + const lastDate = Calendar.daysInMonth(year, month) + const lastDay = (lastDate - firstDate) % 7 + const lastDatePreviousMonth = Calendar.daysInMonth(year, month - 1) + + let i = firstDate + let currentDay + let currentDate + let currentDateObject: CalendarDate | false = false + let currentWeekNumber = null + let otherMonth + let otherYear + + const max = lastDate - i + (lastDay !== 0 ? 7 - lastDay : 0) + firstDate + + while (i < max) { + currentDate = i + 1 + currentDay = ((i < 1 ? 7 + i : i) + firstDay) % 7 + if (currentDate < 1 || currentDate > lastDate) { + if (this.siblingMonths) { + if (currentDate < 1) { + otherMonth = month - 1 + otherYear = year + if (otherMonth < 0) { + otherMonth = 11 + otherYear-- + } + currentDate = lastDatePreviousMonth + currentDate + } else if (currentDate > lastDate) { + otherMonth = month + 1 + otherYear = year + if (otherMonth > 11) { + otherMonth = 0 + otherYear++ + } + currentDate = i - lastDate + 1 + } + + if (otherMonth !== undefined && otherYear !== undefined) { + currentDateObject = { + day: currentDate, + weekDay: currentDay, + month: otherMonth, + year: otherYear, + siblingMonth: true, + } + } + } else { + currentDateObject = false + } + } else { + currentDateObject = { + day: currentDate, + weekDay: currentDay, + month: month, + year: year, + } + } + + if (currentDateObject && this.weekNumbers) { + if (currentWeekNumber === null) { + currentWeekNumber = Calendar.calculateWeekNumber(currentDateObject) + } else if (currentDay === 1 && currentWeekNumber === 52) { + currentWeekNumber = 1 + } else if (currentDay === 1) { + currentWeekNumber++ + } + currentDateObject.weekNumber = currentWeekNumber + } + + if (currentDateObject && this.startDate) { + currentDateObject.selected = this.isDateSelected(currentDateObject) + } + + calendar.push(currentDateObject) + i++ + } + + return calendar + } + + /** + * Checks if a date is selected + * + * @param date Date object + * @return Selected status of the date + */ + isDateSelected(date: CalendarDate, endDate?: CalendarDate | null) { + // Hack to disregard this.endDate in further calculations for finding selectable candidates (hovered) + // by explicitly sending a "null" endDate, if endDate is not sent at all (undefined) then only function behaves as + // initially intended and considers endDate for calculations + if (!this.startDate || endDate === null) { + return false + } + let startDate = this.startDate + if (endDate) { + if (Calendar.compare(this.startDate, endDate) === 1) { + startDate = endDate + endDate = this.startDate + } + } else { + endDate = this.endDate + } + if ( + date.year === startDate.year && + date.month === startDate.month && + date.day === startDate.day + ) { + return true + } + + if (!endDate) { + return false + } + + if ( + date.year === startDate.year && + date.month === startDate.month && + date.day < startDate.day + ) { + return false + } + + if ( + date.year === endDate.year && + date.month === endDate.month && + date.day > endDate.day + ) { + return false + } + + if (date.year === startDate.year && date.month < startDate.month) { + return false + } + + if (date.year === endDate.year && date.month > endDate.month) { + return false + } + + if (date.year < startDate.year) { + return false + } + + if (date.year > endDate.year) { + return false + } + + return true + } + + /** + * Sets the selected period start + * + * @param date Date object + */ + setStartDate(date: CalendarDate | null) { + this.startDate = date + } + + /** + * Sets the selected period end + * + * @param date Date object + */ + setEndDate(date: CalendarDate | null) { + this.endDate = date + } + + /** + * Sets one selected date + * + * @param date Date object + */ + setDate(date: CalendarDate) { + return this.setStartDate(date) + } + + /** + * Calculates the difference between two dates (date1 - date2), in days + * + * @param dateLeft Date object + * @param dateRight Date object + * @return Days between the dates + */ + static diff(dateLeft: CalendarDate, dateRight: CalendarDate) { + const dateLeftDate = new Date( + Date.UTC(dateLeft.year, dateLeft.month, dateLeft.day, 0, 0, 0, 0), + ) + const dateRightDate = new Date( + Date.UTC(dateRight.year, dateRight.month, dateRight.day, 0, 0, 0, 0), + ) + return Math.ceil( + (dateLeftDate.getTime() - dateRightDate.getTime()) / 86400000, + ) + } + + /** + * Calculates the interval between two dates + * + * @param dateLeft Date object + * @param dateRight Date object + * @return Number of days between dates + */ + static interval(dateLeft: CalendarDate, dateRight: CalendarDate) { + return Math.abs(Calendar.diff(dateLeft, dateRight)) + 1 + } + + /** + * Quickly compare two dates + * + * @param dateLeft Left `CalendarDate` object + * @param dateRight Right `CalendarDate` object + * @return Comparison result: -1 (left < right), 0 (equal) or 1 (left > right) + */ + static compare(dateLeft: CalendarDate, dateRight: CalendarDate) { + if ( + typeof dateLeft !== "object" || + typeof dateRight !== "object" || + dateLeft === null || + dateRight === null + ) { + throw new TypeError("dates must be objects") + } + + if (dateLeft.year < dateRight.year) { + return -1 + } + + if (dateLeft.year > dateRight.year) { + return 1 + } + + if (dateLeft.month < dateRight.month) { + return -1 + } + + if (dateLeft.month > dateRight.month) { + return 1 + } + + if (dateLeft.day < dateRight.day) { + return -1 + } + + if (dateLeft.day > dateRight.day) { + return 1 + } + + return 0 + } + + /** + * Calculates the number of days in a month + * + * @param year Year + * @param month Month [0-11] + * @return Length of the month + */ + static daysInMonth(year: number, month: number) { + return new Date(year, month + 1, 0).getDate() + } + + /** + * Calculates if a given year is a leap year + * + * @param year Year + * @return Leap year or not + */ + static isLeapYear(year: number) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0 + } + + /** + * Calculates the week number for a given date + * + * @param date Date object + * @return Week number + */ + // Adapted from http://techblog.procurios.nl/k/news/view/33796/14863/calculate-iso-8601-week-and-year-in-javascript.html + static calculateWeekNumber(date: CalendarDate) { + // Creates the requested date + const current = new Date( + Date.UTC(date.year, date.month, date.day, 0, 0, 0, 0), + ) + + // Create a copy of the object + const target = new Date(current.valueOf()) + + // ISO week date weeks start on monday so correct the day number + const dayNr = (current.getUTCDay() + 6) % 7 + + // ISO 8601 states that week 1 is the week with the first thursday of that + // year. Set the target date to the thursday in the target week. + target.setUTCDate(target.getUTCDate() - dayNr + 3) + + // Store the millisecond value of the target date + const firstThursday = target.valueOf() + + // Set the target to the first thursday of the year + + // First set the target to january first + target.setUTCMonth(0, 1) + + // Not a thursday? Correct the date to the next thursday + if (target.getUTCDay() !== 4) { + target.setUTCMonth(0, 1 + ((4 - target.getUTCDay() + 7) % 7)) + } + + // The week number is the number of weeks between the first thursday of the + // year and the thursday in the target week. + // 604800000 = 7 * 24 * 3600 * 1000 + return 1 + Math.ceil((firstThursday - target.getTime()) / 604800000) + } + + static toString( + date: CalendarDate, + separator: string = "/", + format: string = "DDMMYYYY", + ) { + const day = Helper.pad(date.day, separator) + const month = Helper.pad(date.month + 1, separator) + const year = date.year + + switch (format) { + case ArDateFormats.MMDDYY: + return month + day + sub(year) + case ArDateFormats.DDMMYYYY: + return day + month + year + case ArDateFormats.MMDDYYYY: + return month + day + year + case ArDateFormats.DDMMMYY: + return day + str(date.month + 1, true, separator) + sub(year) + case ArDateFormats.DDMMMYYYY: + return day + str(date.month + 1, true, separator) + year + case ArDateFormats.MMMDDYY: + return str(date.month + 1, true, separator) + day + sub(year) + case ArDateFormats.MMMDDYYYY: + return str(date.month + 1, true, separator) + day + year + case ArDateFormats.YYMMDD: + return sub(year) + month + day + case ArDateFormats.YYMMMDD: + return sub(year) + str(date.month + 1, true, separator) + day + case ArDateFormats.YYYYMMDD: + return year + month + day + case ArDateFormats.YYYYMMMDD: + return year + str(date.month + 1, true, separator) + day + case ArDateFormats.DDMMYY: + default: + return day + month + sub(year) + } + } + + static getPreviousMonth(date: Date) { + const month = date.getMonth() + const year = date.getFullYear() + + const previousMonth = month === 0 ? 11 : month - 1 + const previousYear = month === 0 ? year - 1 : year + + const returnDate = new Date() + returnDate.setDate(date.getDate()) + returnDate.setMonth(previousMonth) + returnDate.setFullYear(previousYear) + + return returnDate + } + + static getNextMonth(date: Date) { + const month = date.getMonth() + const year = date.getFullYear() + + const previousMonth = month === 11 ? 0 : month + 1 + const previousYear = month === 11 ? year + 1 : year + + const returnDate = new Date() + returnDate.setDate(date.getDate()) + returnDate.setMonth(previousMonth) + returnDate.setFullYear(previousYear) + + return returnDate + } + + static getPreviousYear(date: Date) { + const year = date.getFullYear() + const returnDate = new Date() + returnDate.setDate(date.getDate()) + returnDate.setMonth(date.getMonth()) + returnDate.setFullYear(year === 1 ? 1 : year - 1) + return returnDate + } + + static getNextYear(date: Date) { + const year = date.getFullYear() + const returnDate = new Date() + returnDate.setDate(date.getDate()) + returnDate.setMonth(date.getMonth()) + returnDate.setFullYear(year + 1) + return returnDate + } +} + +export const toDate = ( + calDate: CalendarDate, + hour?: number, + minute?: number, + ampm: string = "AM", +) => { + if (hour) { + let adjustedHour = hour + if (ampm === "PM" && hour !== 12) { + adjustedHour += 12 // Add 12 hours for PM time, except when it's 12 PM + } else if (ampm === "AM" && hour === 12) { + adjustedHour = 0 // 12 AM is equivalent to 0 hours + } + + return new Date( + calDate.year, + calDate.month, + calDate.day, + adjustedHour, + minute, + ) + } else return new Date(calDate.year, calDate.month, calDate.day) +} + +export const isWithinSchedule = (date: CalendarDate, event: Event) => { + const startDate = event.schedule?.starts + const endDate = event.schedule?.ends + + const startCalDate = startDate && { + day: (startDate as Date).getDate(), + month: (startDate as Date).getMonth(), + year: (startDate as Date).getFullYear(), + } + + const endCalDate = endDate && { + day: (endDate as Date).getDate(), + month: (endDate as Date).getMonth(), + year: (endDate as Date).getFullYear(), + } + + if (startCalDate && endCalDate) { + const checkStart = Calendar.compare(date, startCalDate) + const checkEnd = Calendar.compare(date, endCalDate) + const amPm = (endDate as Date) + ?.toLocaleString("en-US", { hour12: true }) + .split(" ")[2] + const shouldExcludeEnd = + (endDate as Date).getHours() === 0 && + amPm === "AM" && + date.day === endCalDate.day + + return checkStart >= 0 && checkEnd <= 0 && !shouldExcludeEnd + } + return false +} + +/** + * Exports the Calendar + */ +export { Calendar as CalHelper } diff --git a/src/dateformat.ts b/src/dateformat.ts new file mode 100644 index 0000000..e7b31cb --- /dev/null +++ b/src/dateformat.ts @@ -0,0 +1,336 @@ +import { FunctionType } from "@armco/types" +import { ArDateMasks } 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 = { + 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") +} diff --git a/src/domHelper.ts b/src/domHelper.ts new file mode 100644 index 0000000..31c5feb --- /dev/null +++ b/src/domHelper.ts @@ -0,0 +1,456 @@ +import { Children } from "react" +import { + CarouselProps, + IconProps, + ArPopoverPositions, + ObjectType, + Position, +} from "@armco/types" +import { Helper } from "." + +class DomHelper { + static style = window.getComputedStyle(document.documentElement) + static fontSize = parseFloat(DomHelper.style.fontSize) + static markerSize = 7 + + static outerWidth(el: HTMLElement) { + let width = el.offsetWidth + const style = getComputedStyle(el) + + width += parseInt(style.marginLeft) + parseInt(style.marginRight) + return width + } + + static 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 + } + + static hexToHsl(color: string, returnRaw?: boolean) { + const [r, g, b] = DomHelper.hexToRgb(color) + return DomHelper.rgbToHsl(r, g, b, returnRaw) + } + + static 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 + */ + static 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 + } + + static 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] + } + + static 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) + } + + static hslToHex(h: number, s: number, l: number) { + const [r, g, b] = DomHelper.hslToRgb(h, s, l) + return DomHelper.rgbToHex(Math.round(r), Math.round(g), Math.round(b)) + } + + static implementSvgStyles( + svg: SVGSVGElement, + svgProps?: IconProps["attributes"], + flags?: Array, + ) { + const { size, classes, colors, strokeWidth } = svgProps || {} + const { fillColor, strokeColor } = colors || {} + const [skipPathColorFill] = flags || [] + let styles = null + if (classes) { + styles = Helper.getStylesFromClass(classes) + styles = Object.entries(styles || {}) + .map(([property, value]) => `${property}: ${value}`) + .join("; ") + } + svg.setAttribute("height", size || "1rem") + svg.setAttribute("width", size || "1rem") + svg.setAttribute("class", classes || "") + svg.setAttribute("style", styles || "") + svg.setAttribute("stroke", strokeColor || "black") + strokeWidth + ? svg.setAttribute("stroke-width", strokeWidth) + : strokeColor && svg.setAttribute("stroke-width", "1") + let path: SVGPathElement | NodeListOf = + svg.querySelectorAll("path") + if (path.length === 1 && !skipPathColorFill) { + path = path[0] + // Override stroke and color of "path" node inside SVG at below line. + path && + strokeColor && + !!path.getAttribute("stroke") && + path.setAttribute("stroke", strokeColor) + path && + strokeWidth && + !!path.getAttribute("stroke-width") && + path.setAttribute("stroke-width", strokeWidth) + path && + fillColor && + !!path.getAttribute("fill") && + path.setAttribute("fill", fillColor) + } + svg.style.color = fillColor || "black" + return svg + } + + /** + * Gets the list 'position' relative to a current index + * @param index + */ + static 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 + */ + static 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 + } + + static isKeyboardEvent( + e?: React.MouseEvent | React.KeyboardEvent, + ): e is React.KeyboardEvent { + return e ? e.hasOwnProperty("key") : false + } + + static getDocumentElement(demo?: boolean) { + // Check if the component is running inside an iframe + if (demo) { + const iframeElement = + document.querySelector(".ar-Editor__frame") + if (iframeElement) { + return iframeElement.contentDocument + } + } + // Return the parent document element by default + return document + } + + static getWindowElement(demo?: boolean) { + // Check if the component is running inside an iframe + if (demo) { + const iframeElement = + document.querySelector(".ar-Editor__frame") + + if (iframeElement) { + return iframeElement.contentWindow + } + } + // Return the parent document element by default + return window + } + + static shouldUsePosition( + position: ArPopoverPositions, + anchorRect: DOMRect, + popoverRect: DOMRect, + demo?: boolean, + ) { + const windowObj = DomHelper.getWindowElement(demo) || window + const exceeds: ObjectType = { + bottom: + anchorRect.top + anchorRect.height + popoverRect.height > + windowObj.innerHeight, + right: + anchorRect.left + anchorRect.width + popoverRect.width > + windowObj.innerWidth, + left: anchorRect.left - popoverRect.width < 0, + top: anchorRect.top - popoverRect.height < 0, + } + if (exceeds[position]) return false + if (position === "left" || position === "right") { + if ( + DomHelper.fontSize > anchorRect.top || + DomHelper.fontSize > anchorRect.bottom + ) + return false + } else { + if ( + DomHelper.fontSize > anchorRect.left || + DomHelper.fontSize > anchorRect.right + ) + return false + } + return position + } + + static 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 : DomHelper.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 : DomHelper.markerSize), + }, + [ArPopoverPositions.LEFT]: { + top: anchorRect.top + (anchorRect.height - popoverRect.height) / 2, + left: + anchorRect.left - + popoverRect.width - + (hideMarker ? 0 : DomHelper.markerSize), + }, + [ArPopoverPositions.TOP]: { + top: + anchorRect.top - + popoverRect.height - + (hideMarker ? 0 : DomHelper.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 = DomHelper.fontSize + } + if ((left as number) + popoverRect.width > window.innerWidth) { + left = `calc(100% - ${popoverRect.width}px - ${DomHelper.fontSize}px)` + } + } else { + if ((top as number) < 0) { + top = DomHelper.fontSize + } + if ((top as number) + popoverRect.height > window.innerHeight) { + top = `calc(100% - ${popoverRect.height}px - ${DomHelper.fontSize}px)` + } + } + return { left, top } + } + + static getPositionToUse( + anchorRect: DOMRect, + popoverRect: DOMRect, + requestedPosition?: ArPopoverPositions, + demo?: boolean, + ) { + if (requestedPosition && requestedPosition !== ArPopoverPositions.AUTO) { + return requestedPosition + } + const positionsPriority = [ + ArPopoverPositions.BOTTOM, + ArPopoverPositions.RIGHT, + ArPopoverPositions.LEFT, + ArPopoverPositions.TOP, + ] + + for (const position of positionsPriority) { + if ( + DomHelper.shouldUsePosition(position, anchorRect, popoverRect, demo) + ) { + return position + } + continue + } + + return ArPopoverPositions.BOTTOM + } + + static calculatePopoverPosition( + anchorRect: DOMRect, + popoverRect: DOMRect, + requestedPosition?: ArPopoverPositions, + hideMarker?: boolean, + clickCoordinates?: Array | null, + demo?: boolean, + topOffset?: number, + ) { + const windowObj = DomHelper.getWindowElement(demo) || window + anchorRect = clickCoordinates + ? { + left: clickCoordinates[0], + x: clickCoordinates[0], + top: clickCoordinates[1], + y: clickCoordinates[1], + height: 0, + width: 0, + right: windowObj.screen.width - clickCoordinates[0], + bottom: windowObj.screen.height - clickCoordinates[1], + toJSON: () => {}, + } + : anchorRect + const position = DomHelper.getPositionToUse( + anchorRect, + popoverRect, + requestedPosition, + demo, + ) + const { left, top } = DomHelper.adjustPosition( + position, + anchorRect, + popoverRect, + hideMarker, + topOffset, + ) + return { + leftTop: { + top: ("" + top).startsWith("calc") ? top : top + "px", + left: ("" + left).startsWith("calc") ? left : left + "px", + }, + position, + } + } + + static openInNewTab(url: string) { + const win = window.open(url, "_blank") + if (win != null) { + win.focus() + } + } + + static download(dataURL: any) { + const anchor = document.createElement("a") + anchor.href = dataURL + anchor.download = "edited-image.png" + document.body.appendChild(anchor) + anchor.click() + document.body.removeChild(anchor) + } +} + +export default DomHelper diff --git a/src/gridHelper.ts b/src/gridHelper.ts new file mode 100644 index 0000000..397b42c --- /dev/null +++ b/src/gridHelper.ts @@ -0,0 +1,737 @@ +import { v4 as uuid } from "uuid" +import { FunctionType, GridToolbarSpecs, SlotDescriptor } from "@armco/types" +import { DomHelper } from "." + +class GridHelper { + // For x and y toolbars only + static generateGridToolbarSpecs( + gridArea: Array>, + rowHeight?: Array | string, + colWidth?: Array | string, + ) { + const rowToolsGridArea = gridArea.map((row) => [ + row.every((cell) => cell === row[0]) ? row[0] : "ga-" + uuid(), + ]) + const colToolsGridArea = gridArea[0].map((_, i) => + gridArea.every((row) => row[i] === gridArea[0][i]) + ? gridArea[0][i] + : "ga-" + uuid(), + ) + const rowSlots = GridHelper.countOccurrences( + rowToolsGridArea.map((ga) => ga[0]), + ).map((slotConfig) => { + return { + slot: slotConfig.slotId, + gridArea: slotConfig.slotId, + row: slotConfig.location, + column: 0, + rowSpan: slotConfig.span, + colSpan: 1, + style: { gridArea: slotConfig.slotId }, + } + }) + const colSlots = GridHelper.countOccurrences(colToolsGridArea).map( + (slotConfig) => { + return { + slot: slotConfig.slotId, + gridArea: slotConfig.slotId, + row: 0, + column: slotConfig.location, + rowSpan: 1, + colSpan: slotConfig.span, + style: { gridArea: slotConfig.slotId }, + } + }, + ) + return { + rowTools: { + slots: rowSlots, + gridTemplate: this.generateGridTemplate( + rowToolsGridArea, + rowHeight || "minmax(auto, 1fr)", + colWidth || "1fr", + ), + }, + colTools: { + slots: colSlots, + gridTemplate: this.generateGridTemplate( + [colToolsGridArea], + "1fr", + "1fr", + ), + }, + } + } + + static calculateRowHeights( + slots: Array, + rowHeights?: Array | string, + demo?: boolean, + ): Array { + const totalRows = Math.max(...slots.map((slot) => slot.row + slot.rowSpan)) + const allRowHeights: Array = [] + const doc = DomHelper.getDocumentElement(demo) + rowHeights = Array.isArray(rowHeights) + ? rowHeights + : (Array.from( + { length: totalRows }, + () => rowHeights || "minmax(2rem, auto)", + ) as Array) + + for (let rowIndex = 0; rowIndex < totalRows; rowIndex++) { + if ( + rowHeights[rowIndex] && + rowHeights[rowIndex] !== "" && + rowHeights[rowIndex].indexOf("auto") === -1 + ) { + allRowHeights[rowIndex] = rowHeights[rowIndex] + continue + } + + const rowSlots = slots.filter( + (slot) => slot.row <= rowIndex && rowIndex < slot.row + slot.rowSpan, + ) + const heights = rowSlots.map((slot) => { + const element = doc?.getElementById(slot.slot) + return element + ? Math.floor(element.getBoundingClientRect().height / slot.rowSpan) + : 0 + }) + const maxHeight = Math.max(...heights) + allRowHeights[rowIndex] = + maxHeight === 0 ? "minmax(2rem, auto)" : `${maxHeight}px` + } + + return allRowHeights + } + + static generateGridAreaAndSizes( + slots: Array, + currentRowHeights?: string | Array, + ): { gridArea: Array>; rowHeights: Array } { + let gridArea: Array> = [[]] + const rowHeights: Array = [] + slots.forEach((slot) => { + let endRow = slot.row + slot.rowSpan + let endColumn = slot.column + slot.colSpan + for (let row = slot.row; row < endRow; row++) { + const currentRowHeight = Array.isArray(currentRowHeights) + ? currentRowHeights[row] + : currentRowHeights + if (slot.content) { + rowHeights[row] = currentRowHeight || "auto" + } else { + rowHeights[row] = "minmax(2rem, auto)" + } + if (!gridArea[row]) { + gridArea[row] = [] + } + for (let col = slot.column; col < endColumn; col++) { + gridArea[row][col] = slot.gridArea + } + } + }) + + return { gridArea, rowHeights } + } + + static generateGridTemplate( + gridArea: Array>, + rowHeight?: Array | string, + colWidth?: Array | string, + ): string { + let areas = gridArea + .map( + (row, i) => + `"${row.join(" ")}" ${ + Array.isArray(rowHeight) + ? rowHeight[i] || "auto" + : rowHeight || "minmax(3rem, 1fr)" + }`, + ) + .join(" ") + + const gridTemplateColumns = + " / " + + (Array.isArray(colWidth) + ? colWidth.join(" ") + : Array.from( + { length: gridArea[0].length }, + () => colWidth || "1fr", + ).join(" ")) + areas += gridTemplateColumns + + return areas.trim() + } + + static mergeHandler( + setSlots: FunctionType, + slotConfigs: Array, + minMaxSelections: { + minRow: number + maxRow: number + minColumn: number + maxColumn: number + }, + primarySlotConfig?: SlotDescriptor, + ) { + if (slotConfigs) { + slotConfigs = JSON.parse(JSON.stringify(slotConfigs)) + const selectedSlotConfigs = slotConfigs.filter((sc) => sc.isSelected) + selectedSlotConfigs.sort((a, b) => { + if (a.row !== b.row) { + return a.row - b.row + } else { + return a.column - b.column + } + }) + const firstSlotConfig = selectedSlotConfigs[0] + if (primarySlotConfig) { + primarySlotConfig = slotConfigs.find( + (s) => s.slot === primarySlotConfig?.slot, + ) as SlotDescriptor + primarySlotConfig.row = firstSlotConfig.row + primarySlotConfig.column = firstSlotConfig.column + } else { + primarySlotConfig = firstSlotConfig + } + primarySlotConfig.rowSpan = + minMaxSelections.maxRow - minMaxSelections.minRow + 1 + primarySlotConfig.colSpan = + minMaxSelections.maxColumn - minMaxSelections.minColumn + 1 + selectedSlotConfigs.forEach((ssc) => { + const matchedSlotConfigIndex = slotConfigs.findIndex( + (sc) => sc.slot === ssc.slot, + ) + const matchedSlotConfig = slotConfigs[matchedSlotConfigIndex] + if ( + matchedSlotConfig && + matchedSlotConfig.slot !== primarySlotConfig?.slot + ) { + slotConfigs.splice(matchedSlotConfigIndex, 1) + } + }) + // slotConfigs.forEach((slotConfig) => (slotConfig.isSelected = false)) + setSlots(slotConfigs) + } + } + + static splitHandler( + setSlots: FunctionType, + slotConfigs: Array, + orientation: "horizontal" | "vertical", + placement: "before" | "after", + slot?: SlotDescriptor, + ) { + slotConfigs = JSON.parse(JSON.stringify(slotConfigs)) + let selectedSlots = [] + if (slot) { + slot = slotConfigs.find((s) => s.slot === slot?.slot) + slot && selectedSlots.push(slot) + } else { + selectedSlots = slotConfigs.filter((sc) => sc.isSelected) + } + selectedSlots.forEach((selectedSlot) => { + const { + row, + slot: selectedSlotId, + column, + colSpan, + rowSpan, + } = selectedSlot + const isHorizontal = orientation === "horizontal" + const isAfter = placement === "after" + const slotId = uuid() + const gridAreaValue = `ga-${slotId}` + const span = isHorizontal ? "rowSpan" : "colSpan" + const dimension = isHorizontal ? "row" : "column" + let newDimSpan = selectedSlot[span] + if (newDimSpan > 1) { + newDimSpan = selectedSlot[span] - 1 + selectedSlot[span] = newDimSpan + } + const newSlot: SlotDescriptor = { + slot: slotId, + row: isHorizontal ? (isAfter ? newDimSpan + row : row) : row, + column: isHorizontal ? column : isAfter ? newDimSpan + column : column, + rowSpan: isHorizontal ? 1 : rowSpan, + colSpan: isHorizontal ? colSpan : 1, + gridArea: gridAreaValue, + splitFrom: selectedSlotId, + } + + const shouldUpdateOtherSlots = isHorizontal + ? rowSpan === 1 + : colSpan === 1 + + // Update slot configs + shouldUpdateOtherSlots && + slotConfigs.forEach((slot) => { + const { + row: currRow, + column: currColumn, + slot: currSlot, + colSpan: currColSpan, + rowSpan: currRowSpan, + } = slot + if (isHorizontal) { + if ( + row >= currRow && + row < currRow + currRowSpan && + currSlot !== selectedSlotId + ) { + slot.rowSpan += 1 + } else if (currRow > row) { + slot.row += 1 + } + } else { + if ( + column >= currColumn && + column < currColumn + currColSpan && + currSlot !== selectedSlotId + ) { + slot.colSpan += 1 + } else if (currColumn > column) { + slot.column += 1 + } + } + }) + if (!isAfter) { + selectedSlot[dimension] += 1 + } + slotConfigs.push(newSlot) + }) + + setSlots(slotConfigs) + } + + static removeHandler( + slotConfigs: Array, + gridToolbarSpecs: GridToolbarSpecs, + type: string, + isInlineDelete?: boolean, + ): Array { + slotConfigs = JSON.parse(JSON.stringify(slotConfigs)) + const isRow = type === "row" + const toolbarSlots = isRow + ? gridToolbarSpecs.rowTools.slots + : gridToolbarSpecs.colTools.slots + // Find and sort selected slots from toolbarSpecs + const selectedToolbarSlots = toolbarSlots + .filter((slot) => + isInlineDelete ? slot.isSelectedForInlineDelete : slot.isSelected, + ) + .sort((a, b) => (isRow ? a.row - b.row : a.column - b.column)) + + // Iterate over each selected slot + selectedToolbarSlots.forEach((toolbarSlot) => { + // Find intersecting slots from slotConfigs + const intersectingSlots = slotConfigs.filter((slot) => + isRow + ? toolbarSlot.row >= slot.row && + toolbarSlot.row < slot.row + slot.rowSpan + : toolbarSlot.column >= slot.column && + toolbarSlot.column < slot.column + slot.colSpan, + ) + + // Delete slots with colSpan 1, decrement colSpan of rest by 1 + intersectingSlots.forEach((intersectingSlot) => { + if ( + isRow + ? intersectingSlot.rowSpan <= toolbarSlot.rowSpan + : intersectingSlot.colSpan <= toolbarSlot.colSpan + ) { + const index = slotConfigs.indexOf(intersectingSlot) + if (index !== -1) slotConfigs.splice(index, 1) + } else { + isRow + ? (intersectingSlot.rowSpan -= 1) + : (intersectingSlot.colSpan -= 1) + } + }) + + // Find next slots and decrement their row/column value by 1 + let succeedingSlots = slotConfigs.filter((slot) => + isRow ? slot.row > toolbarSlot.row : slot.column > toolbarSlot.column, + ) + succeedingSlots.forEach((succeedingSlot) => + isRow ? (succeedingSlot.row -= 1) : (succeedingSlot.column -= 1), + ) + + // Find succeeding toolbar slot and decrement their row/column value by 1 + succeedingSlots = toolbarSlots.filter((currToolbarSlot) => + isRow + ? currToolbarSlot.row > toolbarSlot.row + : currToolbarSlot.column > toolbarSlot.column, + ) + succeedingSlots.forEach((succeedingSlot) => + isRow ? (succeedingSlot.row -= 1) : (succeedingSlot.column -= 1), + ) + toolbarSlots.splice(toolbarSlots.indexOf(toolbarSlot), 1) + }) + + return slotConfigs + } + static checkIfAdjacent(slotConfigs: Array) { + const selectedSlotConfigs = slotConfigs.filter((sc) => sc.isSelected) + let minRow = Infinity + let maxRow = -Infinity + let minColumn = Infinity + let maxColumn = -Infinity + let totalCells = 0 + selectedSlotConfigs.forEach((config: SlotDescriptor) => { + if (config.row !== undefined && config.column !== undefined) { + minRow = Math.min(minRow, config.row) + maxRow = Math.max(maxRow, config.row + (config.rowSpan - 1)) + minColumn = Math.min(minColumn, config.column) + maxColumn = Math.max(maxColumn, config.column + (config.colSpan - 1)) + totalCells += config.rowSpan * config.colSpan + } + }) + const rectangleArea = (maxRow - minRow + 1) * (maxColumn - minColumn + 1) + + return { + areAdjacent: + selectedSlotConfigs.length > 1 && rectangleArea === totalCells, + minRow, + maxRow, + minColumn, + maxColumn, + } + } + + static isSlotSelected( + slot: SlotDescriptor, + rowSlots: Array, + colSlots: Array, + selectForDelete?: boolean, + ) { + const { row, rowSpan, column, colSpan } = slot + // For cells spanning multiple rows or columns we check if all rows or columns (toolbar cells) + // corresponding to this slot are selected, if either or all rows or all columns intersecting this cell + // are selected, we mark this cell selected. + const allRowsSelected = rowSlots + .filter((ts) => ts.row < row + rowSpan && ts.row + ts.rowSpan > row) + .every((ts) => + selectForDelete !== undefined + ? ts.isSelectedForInlineDelete + : ts.isSelected, + ) + const allColumnSelected = colSlots + .filter( + (ts) => ts.column < column + colSpan && ts.column + ts.colSpan > column, + ) + .every((ts) => + selectForDelete !== undefined + ? ts.isSelectedForInlineDelete + : ts.isSelected, + ) + return allRowsSelected || allColumnSelected + } + + static selectCellsInSelectedRowCol( + gridToolbarSpecs: GridToolbarSpecs, + slots: Array, + selectForDelete?: boolean, + ) { + const rowSlots = gridToolbarSpecs.rowTools.slots + const colSlots = gridToolbarSpecs.colTools.slots + slots.forEach((slot) => { + slot[ + selectForDelete !== undefined + ? "isSelectedForInlineDelete" + : "isSelected" + ] = this.isSlotSelected(slot, rowSlots, colSlots, selectForDelete) + }) + return [...slots] + } + + static countOccurrences(array: Array) { + const slots: Array<{ slotId: string; span: number; location: number }> = [] + for (let i = 0; i < array.length; i++) { + const value = array[i] + const existingObject = slots.find((obj) => obj.slotId === value) + if (existingObject === undefined) { + slots.push({ slotId: value, span: 1, location: i }) + } else { + existingObject.span++ + } + } + return slots + } + + static isIntersecting( + toolSlot: SlotDescriptor, + slot: SlotDescriptor, + isRow: boolean, + ) { + const dimension = isRow ? "row" : "column" + const span = isRow ? "rowSpan" : "colSpan" + const sRow = slot[dimension] + const sSpan = slot[span] + const tRow = toolSlot[dimension] + const tSpan = toolSlot[span] + return sRow <= tRow && sRow + sSpan >= tRow + tSpan + } + + static findIntersectingSlots( + slots: Array, + toolSlot: SlotDescriptor, + isRow: boolean, + ) { + return slots.filter((slot) => this.isIntersecting(toolSlot, slot, isRow)) + } + + static getCreateOrExtend( + slot: SlotDescriptor, + selectedToolSlot: SlotDescriptor, + placement: "before" | "after", + isRow: boolean, + ) { + const dimension = isRow ? "row" : "column" + const span = isRow ? "rowSpan" : "colSpan" + return placement === "before" + ? slot[dimension] === selectedToolSlot[dimension] + : slot[dimension] + slot[span] === + selectedToolSlot[dimension] + selectedToolSlot[span] + } + + static insertDimension( + type: "row" | "column", + gridToolbarSpecs: GridToolbarSpecs, + slots: Array, + setSlots: FunctionType, + placement: "before" | "after", + selection?: number, + ) { + slots = JSON.parse(JSON.stringify(slots)) + const isRow = type === "row" + let referenceSlots = gridToolbarSpecs[isRow ? "colTools" : "rowTools"].slots + let newSlots: Array = [] + if (referenceSlots.length > 0) { + const insertAt: Array = this.generateInsertionIndexes( + gridToolbarSpecs, + isRow, + placement, + selection, + ) + insertAt.sort() + const trackedSlotsForSpanIncrease: { + [key: string]: { d: SlotDescriptor; count: number } + } = {} + insertAt.forEach((insertionIndex, index) => { + // Insertion indexes represent which row/column new item will end up, and not what was actually selected, + // Below line gets the selected slot by adjusting index back + const selectedToolSlot = + gridToolbarSpecs[isRow ? "rowTools" : "colTools"].slots[ + insertionIndex - index - (placement === "after" ? 1 : 0) + ] + const intersectedSlots = this.findIntersectingSlots( + slots, + selectedToolSlot, + isRow, + ) + const newSlotsAtCurrentDimension = referenceSlots + .map((referenceSlot) => { + const lateralRefToolIntersectedSlot = intersectedSlots.find((s) => + this.isIntersecting(referenceSlot, s, !isRow), + ) + if (lateralRefToolIntersectedSlot) { + const shouldCreate = this.getCreateOrExtend( + lateralRefToolIntersectedSlot, + selectedToolSlot, + placement, + isRow, + ) + if (shouldCreate) { + const slotId = uuid() + const gridArea = `ga-${slotId}` + return { + slot: slotId, + row: isRow ? insertionIndex : referenceSlot.row, + column: isRow ? referenceSlot.column : insertionIndex, + rowSpan: isRow ? 1 : referenceSlot.rowSpan, + colSpan: isRow ? referenceSlot.colSpan : 1, + gridArea, + } + } else { + if ( + !trackedSlotsForSpanIncrease[ + lateralRefToolIntersectedSlot.slot + ] + ) { + trackedSlotsForSpanIncrease[ + lateralRefToolIntersectedSlot.slot + ] = { d: lateralRefToolIntersectedSlot, count: 1 } + } else { + trackedSlotsForSpanIncrease[ + lateralRefToolIntersectedSlot.slot + ].count += 1 + } + } + } + }) + .filter((s) => !!s) as Array + newSlots = newSlots.concat(newSlotsAtCurrentDimension) + }) + insertAt.forEach((dimNum) => { + const fixDim = isRow ? "row" : "column" + const correctableSlots = slots.filter((s) => s[fixDim] >= dimNum) + correctableSlots.forEach((s) => s[fixDim]++) + }) + Object.values(trackedSlotsForSpanIncrease).forEach( + (obj) => (obj.d[isRow ? "rowSpan" : "colSpan"] += obj.count), + ) + } else { + const slotId = uuid() + const gridArea = `ga-${slotId}` + newSlots.push({ + slot: uuid(), + row: 0, + column: 0, + rowSpan: 1, + colSpan: 1, + gridArea, + }) + } + slots = slots.concat(newSlots) + setSlots(slots) + } + + static generateInsertionIndexes( + gridToolbarSpecs: GridToolbarSpecs, + isRow: boolean, + placement: "before" | "after", + selection?: number, + ) { + let insertAt: Array = [] + if (selection !== undefined) { + insertAt.push(placement === "after" ? selection + 1 : selection) + } else { + if (isRow) { + insertAt = this.generateInsertionIndexesForDimension( + gridToolbarSpecs, + "rowTools", + "row", + placement, + isRow, + ) + } else { + insertAt = this.generateInsertionIndexesForDimension( + gridToolbarSpecs, + "colTools", + "column", + placement, + isRow, + ) + } + } + insertAt.sort() + // If multiple row/col to be added, indexes of all rows (columns) except the first one will change by a value + // equal to their index value in insertAt array (second should be incremented by 1, third by 2 and so on hence we can use indexes) + insertAt = insertAt.map((dimNum, index) => dimNum + index) + return insertAt + } + + static generateInsertionIndexesForDimension( + gridToolbarSpecs: GridToolbarSpecs, + tools: "rowTools" | "colTools", + dimension: "row" | "column", + placement: "before" | "after", + isRow: boolean, + ) { + let insertAt: Array = [] + const reverseDimSlots = + gridToolbarSpecs[isRow ? "rowTools" : "colTools"].slots + const selections = gridToolbarSpecs[tools].slots + .filter((s) => s.isSelected) + .map((s) => s[dimension] + (placement === "before" ? 0 : 1)) + if (selections.length > 0) { + insertAt = insertAt.concat(selections) + } else { + if (placement === "before") { + insertAt.push(0) + } else { + insertAt.push( + Math.max(...reverseDimSlots.map((rS) => rS[dimension])) + 1, + ) + } + } + return insertAt + } + + static generateSlots(rows: Array): Array { + const slotConfigs: Array = [] + const processedGridAreas = new Set() + + rows.forEach((row, rowIndex) => { + const parts = row.split(" ") + + parts.forEach((part, partIndex) => { + const gridArea = part.trim() + + if (!processedGridAreas.has(gridArea)) { + processedGridAreas.add(gridArea) + + // Calculate rowSpan and colSpan based on the repetitions in the grid template + const rowSpan = rows.filter((row) => row.includes(gridArea)).length + const colSpan = parts.filter((part) => part === gridArea).length + + slotConfigs.push({ + slot: gridArea, + gridArea, + row: rowIndex, + column: partIndex, + rowSpan, + colSpan, + }) + } + }) + }) + + return slotConfigs + } + + static generateSlotConfigs(gridTemplate: string): { + slotConfigs: Array + rowHeights: Array + colWidths: Array + } { + // Split by double quotes to separate row and column definitions + const parts = gridTemplate + .split('"') + .map((p) => p.trim()) + .filter((p) => p) + let colWidths + + const rowsAndHeights: { [key: string]: string } = {} + + // Identify row and column parts + parts.forEach((part, index) => { + if (part.startsWith("ga-")) { + rowsAndHeights[part] = "auto" + } else { + if (part.includes("calc(")) { + const calcEndIndex = part.lastIndexOf(")") + const calcExpression = part.substring(0, calcEndIndex + 1) + rowsAndHeights[parts[index - 1]] = calcExpression + + const remainingPart = part.substring(calcEndIndex + 1).trim() + if (remainingPart.includes("/")) { + const colPart = remainingPart.split("/")[1].trim() + colWidths = colPart.split(" ").map((p) => p.trim()) + } + } else { + rowsAndHeights[parts[index - 1]] = part + } + } + }) + + const rowHeights = Object.values(rowsAndHeights) + const slotConfigs = this.generateSlots(Object.keys(rowsAndHeights)) + + return { slotConfigs, rowHeights, colWidths: colWidths || [] } + } +} + +export default GridHelper diff --git a/src/helper.tsx b/src/helper.tsx new file mode 100644 index 0000000..7a5eb23 --- /dev/null +++ b/src/helper.tsx @@ -0,0 +1,549 @@ +import { lazy } from "react" +import { + FunctionType, + ObjectType, + PageInfoType, + SearchArgs, + ComponentDescription, + ComponentList, + 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 +} + +class Helper { + static populatePagesInRoutes(routes: RouteConfig[], fallback: FunctionType) { + routes && + routes.forEach((route) => { + if (typeof route.element === "string") { + const elementName = route.element + const Component: JSX.ElementType = lazy(() => + import(`../pages/${elementName}/${elementName}.tsx`).catch( + fallback, + ), + ) + route.element = + if (route.children) { + Helper.populateComponentsInRoutes(route.children, fallback) + } + } + }) + } + + static populateComponentsInRoutes( + routes: RouteConfig[], + fallback: FunctionType, + ) { + routes && + routes.forEach((route) => { + const hierarchy = route.hierarchy + if (typeof route.element === "string") { + const elementName = route.element + const Component: JSX.ElementType = lazy( + () => + import( + `../components/${hierarchy}/${elementName}/${elementName}.tsx` + ).catch(fallback), + // () => import(`../components/atoms/Component_404`) + ) + route.element = + if (route.children) { + Helper.populateComponentsInRoutes(route.children, fallback) + } + } + }) + } + + static recrusiveFilter( + data: Array, + filter: string, + matchCase?: boolean, + searchKeys?: false | Array, + ) { + if (!filter || !data || data.length === 0) { + return data + } + if (!searchKeys || searchKeys.length === 0) { + searchKeys = ["label", "name", "id", "value"] + } + const dataClone = JSON.parse(JSON.stringify(data)) + const filteredItems = dataClone.filter((obj: any) => { + return ( + searchKeys && + searchKeys.reduce((acc, key) => { + let isMatch = + obj[key] && + (matchCase + ? obj[key].indexOf(filter) > -1 + : obj[key] && + obj[key] + .toString() + .toLowerCase() + .indexOf(filter.toLowerCase()) > -1) + if (obj.children) { + obj.children = Helper.recrusiveFilter( + obj.children, + filter, + matchCase, + ) + isMatch = isMatch || obj.children.length > 0 + } + return acc || isMatch + }, false) + ) + }) + return filteredItems + } + + // static search(args: SearchArgs): ObjectType | undefined { + // const { obj, key, value } = args + // if (Array.isArray(obj)) { + // return obj.find((item) => { + // args.obj = item + // return Helper.search(args) + // }) + // } else { + // if (obj) { + // if (key in obj && (value === undefined || obj[key] === value)) { + // return obj + // } + // return Object.values(obj).find((item) => { + // if (typeof item === "object" && !(item instanceof Date)) { + // args.obj = item as ObjectType + // return Helper.search(args) + // } + // }) as ObjectType + // } + // } + // } + static search({ obj, key, value }: SearchArgs): ObjectType | undefined { + if (Array.isArray(obj)) { + for (const item of obj) { + const result = Helper.search({ obj: item, key, value }) + if (result) return result + } + } else if (obj && typeof obj === "object" && !(obj instanceof Date)) { + if (key in obj && (value === undefined || obj[key] === value)) { + return obj + } + for (const item of Object.values(obj)) { + if (typeof item === "object" && !(item instanceof Date)) { + const result = Helper.search({ obj: item as ObjectType, key, value }) + if (result) return result + } + } + } + return undefined + } + + static filterTreeStructure( + args: SearchArgs, + ): boolean | Array | 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 = Helper.filterTreeStructure(args) + if (Array.isArray(match) && match.length > 0) { + ;(obj as unknown as Array>).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 = Helper.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 + } + } + + static generateSlices( + count: number, + sliceLength: number, + labelFormat: string, + ) { + let i = 0, + j = 0 + const slices: Array = [] + while (i < count) { + let addUp = sliceLength + if (i + sliceLength > count) { + addUp = count % sliceLength + } + const label = + labelFormat === "range" ? i + 1 + " - " + (i + addUp) : "" + (j + 1) + slices.push({ + label, + name: label.replace(/ /g, "").replace(/-/g, "to"), + sliceIndex: j, + startIndex: i, + endIndex: i + addUp - 1, + }) + i += sliceLength + j++ + } + return slices + } + + static aggregate( + data: any, + aggregator: string, + ): { [key: string]: Array } { + let aggregated: { [key: string]: Array } = {} + data.forEach((item: any) => { + const key = item[aggregator] + let aggregatedArray: Array | undefined = aggregated[key] + if (!aggregatedArray) { + aggregatedArray = [] + aggregated[key] = aggregatedArray + } + aggregatedArray.push(item) + }) + return aggregated + } + + static generateCategories( + data: any, + categories: Array | undefined, + ): { [key: string]: { [key: string]: Array } } { + const groups: { [key: string]: { [key: string]: Array } } = {} + if (categories && data) { + categories.forEach((key) => { + let group: { [key: string]: Array } = Helper.aggregate( + data, + key, + ) + groups[key] = group + }) + } + return groups + } + + static toCamelCase(str: string, separater: string, includeFirst: boolean) { + const strParts = str.split(separater || " ") + return strParts + .map((part, i) => + i === 0 && includeFirst + ? part.charAt(0).toUpperCase() + part.slice(1) + : part, + ) + .join("") + } + + static generateRandomId(length: number = 8) { + return Math.random() + .toString(36) + .substring(2, length + 2) + } + + static copyOrPrompt(text?: string, cb?: FunctionType) { + if (Helper.copyToClipboard(text)) { + cb && cb() + } else { + prompt("Clipboard (Select: ⌘+a > Copy: ⌘+c)", text) + } + } + + static copyToClipboard(text?: string) { + const isNotSafari = typeof (window as any).safari === "undefined" + if (isNotSafari) { + text && navigator.clipboard.writeText(text) + return true + } else { + return false + } + } + + static findComponentDescription( + componentName: string, + components: ComponentList, + ) { + let hierarchy + let selectedItem = components.ATOMS.components.find( + (item: ComponentDescription) => + (typeof item === "string" ? item : item.name) === componentName, + ) + if (!selectedItem) { + selectedItem = components.MOLECULES.components.find( + (item: ComponentDescription) => + (typeof item === "string" ? item : item.name) === componentName, + ) + if (selectedItem) { + hierarchy = "MOLECULES" + } + } else { + hierarchy = "ATOMS" + } + return { + selectedItem: JSON.parse( + JSON.stringify(selectedItem), + ) as ComponentDescription, + hierarchy, + component: componentName, + } + } + + static matchArrayFilters( + searchList: Array | undefined, + filters: Array | 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 + } + + static importComponent( + name: string, + hierarchy: string, + fallback: FunctionType, + ) { + return lazy( + () => + import(`../components/${hierarchy}/${name}/${name}.tsx`).catch( + fallback, + ), + // () => import(`../components/atoms/Component_404`) + ) + } + + static getStylesFromClass(className: string): Record { + const styleSheet = Array.from(document.styleSheets).find((sheet) => + Array.from(sheet.cssRules).some( + (rule) => + rule instanceof CSSStyleRule && rule.selectorText === `.${className}`, + ), + ) + + if (styleSheet) { + const styleRule = Array.from(styleSheet.cssRules).find( + (rule) => + rule instanceof CSSStyleRule && rule.selectorText === `.${className}`, + ) as CSSStyleRule + + if (styleRule) { + const styles: Record = {} + for (let i = 0; i < styleRule.style.length; i++) { + const prop = styleRule.style[i] + styles[prop] = styleRule.style.getPropertyValue(prop) + } + return styles + } + } + + return {} + } + + static debounce void>( + func: T, + wait?: number, + immediate: boolean = false, + ): (...args: Parameters) => void { + let timeout: NodeJS.Timeout + + return function (...args: Parameters): void { + const later = () => { + timeout = undefined! + + if (!immediate) { + func(...args) + } + } + + clearTimeout(timeout) + + if (immediate && !timeout) { + func(...args) + } + + timeout = setTimeout(later, wait || 1000) + } + } + + static isMobile(): boolean { + let check = false + ;(function (a: string) { + if ( + /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test( + a, + ) || + /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test( + a.substr(0, 4), + ) + ) + check = true + })( + (navigator.userAgent || + navigator.vendor || + ("opera" in window && window.opera)) as string, + ) + return check + } + + static download(blob: Blob, filename: string) { + const link = document.createElement("a") + link.href = window.URL.createObjectURL(blob) + link.download = filename + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + } + + static pad(num: number, separator?: string) { + return ("" + num).padStart(2, "0") + (separator ? separator : "") + } + + static toReadable(str?: string) { + return str + ?.replace(/([a-z])([A-Z])/g, "$1 $2") + .split(" ") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" ") + } + + static generateFileKey(file: File | { name: string }) { + return "size" in file + ? `${file.name}_${file.size}_${file.type}_${file.lastModified}` + : file.name + "-preexisting-" + Date.now() + } + + static 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 + } + } + + static isImage(type: string) { + return !!type && validImageMimeTypes.indexOf(type) > -1 + } + + static 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) + } +} + +export default Helper diff --git a/src/hooks.ts b/src/hooks.ts new file mode 100644 index 0000000..e2f8219 --- /dev/null +++ b/src/hooks.ts @@ -0,0 +1,170 @@ +import { useContext, useEffect, useState } from "react" +import { + AlertProps, + ArThemes, + DrawerProps, + FunctionType, + ModalProps, + PanelContent, + User, +} from "@armco/types" +import { ArContext, SlotterContext } from "./contexts" + +export function useSlotted(componentName: string) { + const { slotted, setSlotted } = useContext(SlotterContext) + slotted?.indexOf(componentName) === -1 && + setSlotted && + setSlotted([...slotted, componentName]) +} + +export function useStateWithHistory( + initialState?: T, +): [T | undefined, FunctionType, FunctionType, FunctionType, boolean, boolean] { + const [past, setPast] = useState([]) + const [present, setPresent] = useState() + const [future, setFuture] = useState([]) + + useEffect(() => { + !present && setPresent(initialState) + // eslint-disable-next-line + }, [initialState]) + + const undo = () => { + if (past.length === 0) return + + const newPast = [...past] + const newPresent = newPast.pop() + + setPast(newPast) + present && setFuture([present, ...future]) + setPresent(newPresent) + } + + const redo = () => { + if (future.length === 0) return + + const newFuture = [...future] + const newPresent = newFuture.shift() + + past && present && setPast([...past, present]) + setFuture(newFuture) + setPresent(newPresent) + } + + const updatePresent = (newState: any, skipHistory?: boolean) => { + if (!newState) { + debugger + } + !skipHistory && past && present && setPast([...past, present]) + setPresent(newState) + !skipHistory && setFuture([]) + } + + return [ + present, + updatePresent, + undo, + redo, + past.length > 0, + future.length > 0, + ] +} + +export const useTheme = (): { + theme: ArThemes + setTheme: (theme: ArThemes) => void +} => { + const context = useContext(ArContext) + if (!context) { + throw new Error("useTheme must be used within an ArProvider") + } + return { theme: context.theme, setTheme: context.setTheme } +} + +export const useModalState = (): { + modalState?: ModalProps + setModalState: (modalState: ModalProps | undefined) => void +} => { + const context = useContext(ArContext) + if (!context) { + throw new Error("useModalState must be used within an ArProvider") + } + return { + modalState: context.modalState, + setModalState: context.setModalState, + } +} + +export const useNotification = (): { + notification?: AlertProps + notify: (notification: AlertProps | undefined) => void +} => { + const context = useContext(ArContext) + if (!context) { + throw new Error("useNotification must be used within an ArProvider") + } + return { + notification: context.notification, + notify: context.notify, + } +} + +export const useDrawerState = (): { + drawerState: DrawerProps + setDrawerState: (drawerState: DrawerProps) => void +} => { + const context = useContext(ArContext) + if (!context) { + throw new Error("useDrawerState must be used within an ArProvider") + } + return { + drawerState: context.drawerState, + setDrawerState: context.setDrawerState, + } +} + +export const useLoggedIn = (): { + isLoggedIn?: boolean + setLoggedIn: (isLoggedIn: boolean) => void +} => { + const context = useContext(ArContext) + if (!context) { + throw new Error("useLoggedIn must be used within an ArProvider") + } + return { + isLoggedIn: context.isLoggedIn, + setLoggedIn: context.setLoggedIn, + } +} + +export const useUser = (): { + user?: User + setUser: (user: User) => void +} => { + const context = useContext(ArContext) + if (!context) { + throw new Error("useUser must be used within an ArProvider") + } + return { + user: context.user, + setUser: context.setUser, + } +} + +export const usePanelContent = ( + isLeft: boolean, +): { + panelContent?: PanelContent + setPanelContent: (panelContent: PanelContent) => void +} => { + const context = useContext(ArContext) + if (!context) { + throw new Error("usePanelContent must be used within an ArProvider") + } + return { + panelContent: isLeft ? context.leftPanelContent : context.rightPanelContent, + setPanelContent: isLeft + ? context.setLeftPanelContent + : context.setRightPanelContent, + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..61e6a74 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,14 @@ +export { dateFormat as DateFormatter } from "./dateformat" +export { default as DomHelper } from "./domHelper" +export { default as Adapter } from "./adapters" +export { default as Helper } from "./helper" +export { default as Network } from "./network" +export { default as Validator } from "./validators" +export { default as GridHelper } from "./gridHelper" +export { default as RecursionHelper } from "./recursionHelper" +export * from "./dateHelper" +export * from "./chartGenerators" +export * from "./hooks" +export * from "./contexts" +export * from "./providers" +export * from "./HOC" diff --git a/src/network.ts b/src/network.ts new file mode 100644 index 0000000..dfb3679 --- /dev/null +++ b/src/network.ts @@ -0,0 +1,247 @@ +import { FunctionType, ObjectType } from "@armco/types" +import { API_CONFIG } from "@armco/configs" + +interface Options extends RequestInit { + // headers?: { + // body?: object + // Origin?: string + // "Content-Type"?: string + // } + // method?: string + // mode?: string + // credentials?: string + headers?: any + toggleLoader?: Function + extras?: ObjectType +} + +interface RetryOptions { + retryDescriptor?: number[] + maxAttempts?: number + onRetry?: (attempt: number, waitTime: number) => void + onSuccess?: () => void + onFailure?: (error: Error) => void +} + +class Error { + error?: string + message?: string + status: number + extras?: ObjectType + + constructor(message: string, status: number, extras?: ObjectType) { + this.message = message + this.status = status + this.extras = extras + } +} + +export default class Network { + static MAX_ATTEMPTS_LIMIT = 10 + + static async get(url: string, queryParams?: object, options?: Options) { + const urlString = Network.stringifyUrl(url, queryParams) + options = { + method: "GET", + mode: "cors", + credentials: "include", + ...options, + } + return Network.crud(urlString, options) + } + + static async getStatic( + url: string, + queryParams?: object | null, + options?: Options | null, + ) { + let urlString = Network.stringifyUrl(url, queryParams) + const environment = process.env.NODE_ENV || "development" + const host = urlString.startsWith("http") + ? "" + : API_CONFIG.STATIC_HOST[environment as keyof object] + urlString = host ? host + urlString : urlString + options = { + method: "GET", + mode: "cors", + credentials: "include", + ...options, + } + return Network.crud(urlString, options) + } + + static async post( + url: string, + data: object, + queryParams?: object, + options?: Options, + noStringify?: boolean, + noHeaders?: boolean, + ) { + const urlString = Network.stringifyUrl(url, queryParams) + options = { + method: "POST", + // @ts-ignore + body: noStringify ? data : JSON.stringify(data), + mode: "cors", + credentials: "include", + } + !noHeaders && + options && + (options.headers = { "Content-Type": "application/json" }) + // @ts-ignore + return Network.crud(urlString, options) + } + + static async upload( + url: string, + file: File, + queryParams?: object, + options?: Options, + ) { + let urlString = Network.stringifyUrl(url, queryParams) + const environment = process.env.NODE_ENV || "development" + const host = urlString.startsWith("http") + ? "" + : API_CONFIG.STATIC_HOST[environment as keyof object] + urlString = host ? host + urlString : urlString + const formData = new FormData() + formData.append("file", file) + options = { + method: "POST", + mode: "cors", + body: formData, + credentials: "include", + ...options, + } + return Network.crud(urlString, options) + } + + static async put(url: string, data: object, queryParams?: object) { + const urlString = Network.stringifyUrl(url, queryParams) + const options: Options = { + method: "PUT", + body: JSON.stringify(data), + mode: "cors", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + } + return Network.crud(urlString, options) + } + + static async delete(url: string, queryParams: object) { + const urlString = Network.stringifyUrl(url, queryParams) + const options = { + method: "DELETE", + noInject: true, + } + return Network.crud(urlString, options) + } + + static async crud(urlString: string, options: Options) { + const environment = process.env.NODE_ENV || "development" + const host = urlString.startsWith("http") + ? "" + : API_CONFIG.HOST[environment as keyof object] + const extras = options?.extras + urlString = host ? host + urlString : urlString + if (!options.headers) { + options.headers = {} + } + options.toggleLoader && options.toggleLoader({ [urlString]: true }) + options.headers["Origin"] = + window.location.protocol + "//" + window.location.host + const response = await fetch(urlString, options) + const { ok, status, headers } = response + if (ok) { + if (headers.get("content-type")) { + if (headers.get("content-type")!.indexOf("application/json") !== -1) { + const body = await response.json() + options.toggleLoader && options.toggleLoader({ [urlString]: false }) + return { status, body, headers } + } else if ( + headers.get("content-type")!.indexOf("application/zip") !== -1 || + headers.get("content-type")!.indexOf("image/png") !== -1 || + headers.get("content-type")!.indexOf("application/pdf") !== -1 || + headers.get("content-type")!.indexOf("video/mp4") !== -1 || + headers.get("content-type")!.indexOf("image/gif") !== -1 + ) { + const body = await response.blob() + options.toggleLoader && options.toggleLoader({ [urlString]: false }) + return { status, body, headers } + } + } + const body = await response.text() + options.toggleLoader && options.toggleLoader({ [urlString]: false }) + return { status, body, headers } + } + const err = await response.json() + options.toggleLoader && options.toggleLoader({ [urlString]: false }) + console.log(err) + throw new Error(err.error || err.message, status, extras) + } + + static stringifyUrl(url: string, queryParams?: any) { + if (!queryParams) { + return url || "" + } + const arrLength = Object.keys(queryParams).length + return url && arrLength + ? Object.keys(queryParams) + .filter((k) => queryParams[k] !== undefined) + .reduce( + (acc, key, index) => + acc.concat( + `${encodeURIComponent(key)}=${encodeURIComponent( + queryParams[key], + ).replace(/'/g, "%27")}` + (index < arrLength - 1 ? "&" : ""), + ), + url + "?", + ) + : "" + } + + static async retry( + fn: FunctionType, + retryOptions: RetryOptions | undefined = {}, + ) { + let { + retryDescriptor = [1, 3, 5], + maxAttempts = 5, + onRetry, + onSuccess, + onFailure, + } = retryOptions + maxAttempts = Math.min(maxAttempts, Network.MAX_ATTEMPTS_LIMIT) + let attempts = 0 + + const attempt = async () => { + try { + const result = await fn() + onSuccess && onSuccess() + return result + } catch (error) { + if (attempts < Math.min(retryDescriptor.length, maxAttempts)) { + const waitTime = retryDescriptor[attempts] * 1000 + attempts++ + onRetry && onRetry(attempts, waitTime) + console.warn(`Retrying in ${waitTime / 1000} seconds...`) + await new Promise((resolve) => setTimeout(resolve, waitTime)) + return attempt() + } else { + const errorObj = new Error( + "Max retries reached", + 500, + error as ObjectType, + ) + onFailure && onFailure(errorObj) + throw errorObj + } + } + } + + return attempt() + } +} diff --git a/src/providers.tsx b/src/providers.tsx new file mode 100644 index 0000000..df1094c --- /dev/null +++ b/src/providers.tsx @@ -0,0 +1,53 @@ +import { ReactNode, useState } from "react" +import { + AlertProps, + ArThemes, + DrawerProps, + ModalProps, + PanelContent, + User, +} from "@armco/types" +import { ArContext, Helper } from "./index" +export const ArProvider = ({ children }: { children: ReactNode }) => { + const [theme, setTheme] = useState(ArThemes.DARK1) + const [isLoggedIn, setLoggedIn] = useState() + const [user, setUser] = useState() + const [modalState, setModalState] = useState( + undefined, + ) + const [notification, notify] = useState(undefined) + const [drawerState, setDrawerState] = useState({ + collapsed: Helper.isMobile(), + }) + const [leftPanelContent, setLeftPanelContent] = useState< + PanelContent | undefined + >() + const [rightPanelContent, setRightPanelContent] = useState< + PanelContent | undefined + >() + + return ( + + {children} + + ) +} diff --git a/src/recursionHelper.tsx b/src/recursionHelper.tsx new file mode 100644 index 0000000..dd185e7 --- /dev/null +++ b/src/recursionHelper.tsx @@ -0,0 +1,66 @@ +import { v4 as uuid } from "uuid" +import { RecursionArgs, ObjectType, RecusionConditionTypes } from "@armco/types" + +class RecursionHelper { + static recur(args: RecursionArgs) { + const { condition, data, isBrute, preop, postop, iterateOn } = args + if (isBrute) { + if (Array.isArray(data)) { + data.forEach((item) => { + RecursionHelper.recur({ ...args, data: item }) + }) + } else if (typeof data === "object") { + const preopResult = preop && preop(data) + Object.values(data).forEach((item: any) => { + RecursionHelper.recur({ ...args, data: item }) + }) + return (postop && postop(data)) || preopResult + } + } else { + if (Array.isArray(data)) { + data.forEach((item) => { + RecursionHelper.recur({ ...args, data: item }) + }) + } else if ( + typeof data === "object" && + (!condition || RecursionHelper.evaluateCondition(data, condition)) + ) { + const preopResult = preop && preop(data) + if (iterateOn) { + if (data[iterateOn]) { + RecursionHelper.recur({ ...args, data: data[iterateOn] }) + } + } else { + Object.values(data).forEach((item) => { + RecursionHelper.recur({ ...args, data: item }) + }) + } + return (postop && postop(data)) || preopResult + } + } + return null + } + + static injectIds(args: RecursionArgs) { + args.postop = (item) => !item.id && (item.id = uuid()) + RecursionHelper.recur(args) + } + + static evaluateCondition(data: ObjectType, condition: ObjectType): boolean { + if (!data || !condition) { + return false + } + if (condition.type === RecusionConditionTypes.KEY_EXISTS) { + return !!(condition.key && (condition.key as string) in data) + } else if (condition.type === RecusionConditionTypes.KEY_VALUE) { + return !!( + condition.key && + condition.value && + data[condition.key as string] === condition.value + ) + } + return false + } +} + +export default RecursionHelper diff --git a/src/validators.ts b/src/validators.ts new file mode 100644 index 0000000..560e574 --- /dev/null +++ b/src/validators.ts @@ -0,0 +1,17 @@ +import { ObjectType } from "@armco/types" + +class Validator { + static validateTask(taskObj: ObjectType) { + return ( + taskObj && + taskObj.name && + taskObj.client && + taskObj.target && + (taskObj.target as ObjectType).endpoint && + taskObj.schedule && + (taskObj.schedule as ObjectType).cron + ) + } +} + +export default Validator diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..54d05ab --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src"], +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..3018e48 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,34 @@ +import { resolve } from "node:path" +import glob from "glob" +import { defineConfig } from "vitest/config" +import react from "@vitejs/plugin-react" +import dts from "vite-plugin-dts" + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react(), dts({ outDir: "build/types" })], + build: { + outDir: "build", + lib: { + entry: glob.sync(resolve(__dirname, "src/**/*.ts")), + name: "@armco/utils", + formats: ["es", "cjs"], + }, + rollupOptions: { + treeshake: true, + external: ["react", "react/jsx-runtime", "react-dom", "moment"], + output: [ + { + format: "es", + dir: "build/es", + entryFileNames: "[name].js", + }, + { + format: "cjs", + dir: "build/cjs", + entryFileNames: "[name].js", + }, + ], + }, + }, +}) From b608b9a07051590887b8cb9c9f14c538d8fa0c0d Mon Sep 17 00:00:00 2001 From: mohiit1502 Date: Wed, 18 Sep 2024 16:40:35 +0530 Subject: [PATCH 3/6] added gitingore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2d77136 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +dist +node_modules +build \ No newline at end of file From 5bb0e14670a81be0205f9b6fdd1315101daf141d Mon Sep 17 00:00:00 2001 From: mohiit1502 Date: Wed, 18 Sep 2024 23:24:23 +0530 Subject: [PATCH 4/6] Enabled project to run in islation --- package-lock.json | 4317 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 18 +- src/HOC.tsx | 2 +- src/contexts.ts | 2 +- src/dateHelper.ts | 2 +- src/domHelper.ts | 2 +- src/gridHelper.ts | 2 +- src/providers.tsx | 4 +- tsconfig.json | 27 +- vite.config.ts | 10 +- 10 files changed, 4370 insertions(+), 16 deletions(-) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ab6c440 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4317 @@ +{ + "name": "@armco/utils", + "version": "0.0.6", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@armco/utils", + "version": "0.0.6", + "license": "ISC", + "dependencies": { + "@armco/configs": "^0.0.3", + "@armco/types": "^0.0.9", + "d3": "^7.9.0", + "uuid": "^10.0.0" + }, + "devDependencies": { + "@types/d3": "^7.4.3", + "@types/node": "^22.5.5", + "@types/react": "^18.3.7", + "@types/uuid": "^10.0.0", + "@vitejs/plugin-react": "^4.3.1", + "glob": "^11.0.0", + "react": "^18.3.1", + "typescript": "^5.0.2", + "vite": "^5.4.6", + "vite-plugin-dts": "^4.2.1", + "vitest": "^2.1.1" + }, + "peerDependencies": { + "react": ">16.8.1" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@armco/configs": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@armco/configs/-/configs-0.0.3.tgz", + "integrity": "sha512-1gdJmMIkAVttA8D2gZmKlVEs5mEnSkr94BjZfyQa0/A6oQgVt2EMEgJmCUkHy2YzitFm8a8ZtOfM3cvdKTQoWg==", + "license": "ISC", + "dependencies": { + "@armco/types": "^0.0.9", + "uuid": "^10.0.0" + } + }, + "node_modules/@armco/types": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@armco/types/-/types-0.0.9.tgz", + "integrity": "sha512-RLCt0Q20Nm52sTUcKVhjzeq7sbojgryEeqqChATOW6yAkWCL5NrQ2RLXpK4veePz1RLdu1eTsWVy5fMGe85qQw==", + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.6", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", + "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz", + "integrity": "sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz", + "integrity": "sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@microsoft/api-extractor": { + "version": "7.47.7", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.47.7.tgz", + "integrity": "sha512-fNiD3G55ZJGhPOBPMKD/enozj8yxJSYyVJWxRWdcUtw842rvthDHJgUWq9gXQTensFlMHv2wGuCjjivPv53j0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor-model": "7.29.6", + "@microsoft/tsdoc": "~0.15.0", + "@microsoft/tsdoc-config": "~0.17.0", + "@rushstack/node-core-library": "5.7.0", + "@rushstack/rig-package": "0.5.3", + "@rushstack/terminal": "0.14.0", + "@rushstack/ts-command-line": "4.22.6", + "lodash": "~4.17.15", + "minimatch": "~3.0.3", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "source-map": "~0.6.1", + "typescript": "5.4.2" + }, + "bin": { + "api-extractor": "bin/api-extractor" + } + }, + "node_modules/@microsoft/api-extractor-model": { + "version": "7.29.6", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.29.6.tgz", + "integrity": "sha512-gC0KGtrZvxzf/Rt9oMYD2dHvtN/1KPEYsrQPyMKhLHnlVuO/f4AFN3E4toqZzD2pt4LhkKoYmL2H9tX3yCOyRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "~0.15.0", + "@microsoft/tsdoc-config": "~0.17.0", + "@rushstack/node-core-library": "5.7.0" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz", + "integrity": "sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.0.tgz", + "integrity": "sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.15.0", + "ajv": "~8.12.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz", + "integrity": "sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz", + "integrity": "sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz", + "integrity": "sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz", + "integrity": "sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz", + "integrity": "sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz", + "integrity": "sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz", + "integrity": "sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz", + "integrity": "sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz", + "integrity": "sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz", + "integrity": "sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz", + "integrity": "sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz", + "integrity": "sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz", + "integrity": "sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz", + "integrity": "sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz", + "integrity": "sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz", + "integrity": "sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rushstack/node-core-library": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.7.0.tgz", + "integrity": "sha512-Ff9Cz/YlWu9ce4dmqNBZpA45AEya04XaBFIjV7xTVeEf+y/kTjEasmozqFELXlNG4ROdevss75JrrZ5WgufDkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/@rushstack/rig-package": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.3.tgz", + "integrity": "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "~1.22.1", + "strip-json-comments": "~3.1.1" + } + }, + "node_modules/@rushstack/terminal": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.14.0.tgz", + "integrity": "sha512-juTKMAMpTIJKudeFkG5slD8Z/LHwNwGZLtU441l/u82XdTBfsP+LbGKJLCNwP5se+DMCT55GB8x9p6+C4UL7jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.7.0", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/terminal/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@rushstack/terminal/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "4.22.6", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.22.6.tgz", + "integrity": "sha512-QSRqHT/IfoC5nk9zn6+fgyqOPXHME0BfchII9EUPR19pocsNp/xSbeBCbD3PIR2Lg+Q5qk7OFqk1VhWPMdKHJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/terminal": "0.14.0", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" + } + }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz", + "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz", + "integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.5.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz", + "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.7.tgz", + "integrity": "sha512-KUnDCJF5+AiZd8owLIeVHqmW9yM4sqmDVf2JRJiBMFkGvkoZ4/WyV2lL4zVsoinmRS/W3FeEdZLEWFRofnT2FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz", + "integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.5", + "@babel/plugin-transform-react-jsx-self": "^7.24.5", + "@babel/plugin-transform-react-jsx-source": "^7.24.1", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.1.tgz", + "integrity": "sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", + "chai": "^5.1.1", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.1.tgz", + "integrity": "sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "^2.1.0-beta.1", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.11" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/spy": "2.1.1", + "msw": "^2.3.5", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.1.tgz", + "integrity": "sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.1.tgz", + "integrity": "sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.1", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.1.tgz", + "integrity": "sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.1", + "magic-string": "^0.30.11", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.1.tgz", + "integrity": "sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.1.tgz", + "integrity": "sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.1", + "loupe": "^3.1.1", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.5.tgz", + "integrity": "sha512-F4tA0DCO5Q1F5mScHmca0umsi2ufKULAnMOVBfMsZdT4myhVl4WdKRwCaKcfOkIEuyrAVvtq1ESBdZ+rSyLVww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.5" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.5.tgz", + "integrity": "sha512-varwD7RaKE2J/Z+Zu6j3mNNJbNT394qIxXwdvz/4ao/vxOfyClZpSDtLKkwWmecinkOVos5+PWkWraelfMLfpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.5.tgz", + "integrity": "sha512-mcT1mHvLljAEtHviVcBuOyAwwMKz1ibXTi5uYtP/pf4XxoAzpdkQ+Br2IC0NPCvLCbjPZmbf3I0udndkfB1CDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.5", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.6.tgz", + "integrity": "sha512-r+gNu6K4lrvaQLQGmf+1gc41p3FO2OUJyWmNqaIITaJU6YFiV5PtQSFZt8jfztYyARwqhoCayjprC7KMvT3nRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.6", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.6.tgz", + "integrity": "sha512-xRXqxDrIqK8v8sSScpistyYH0qYqxakpsIvqMD2e5sV/PXQ1mTwtXp4k42yHK06KXxKSmitop9e45Ui/3BrTEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.6", + "@vue/shared": "3.5.6" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/language-core": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.6.tgz", + "integrity": "sha512-MW569cSky9R/ooKMh6xa2g1D0AtRKbL56k83dzus/bx//RDJk24RHWkMzbAlXjMdDNyxAaagKPRquBIxkxlCkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "~2.4.1", + "@vue/compiler-dom": "^3.4.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.4.0", + "computeds": "^0.0.1", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.6.tgz", + "integrity": "sha512-eidH0HInnL39z6wAt6SFIwBrvGOpDWsDxlw3rCgo1B+CQ1781WzQUSU3YjxgdkcJo9Q8S6LmXTkvI+cLHGkQfA==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001660", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", + "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", + "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "dev": true, + "license": "MIT" + }, + "node_modules/computeds": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.25", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.25.tgz", + "integrity": "sha512-kMb204zvK3PsSlgvvwzI3wBIcAw15tRkYk+NQdsjdDtcQWTp2RABbMQ9rUBy8KNEOM+/E6ep+XC3AykiWZld4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz", + "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", + "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", + "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.1", + "ufo": "^1.5.3" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "license": "MIT" + }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", + "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz", + "integrity": "sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.7.1", + "pathe": "^1.1.2" + } + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" + }, + "node_modules/rollup": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.3.tgz", + "integrity": "sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.21.3", + "@rollup/rollup-android-arm64": "4.21.3", + "@rollup/rollup-darwin-arm64": "4.21.3", + "@rollup/rollup-darwin-x64": "4.21.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.3", + "@rollup/rollup-linux-arm-musleabihf": "4.21.3", + "@rollup/rollup-linux-arm64-gnu": "4.21.3", + "@rollup/rollup-linux-arm64-musl": "4.21.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.3", + "@rollup/rollup-linux-riscv64-gnu": "4.21.3", + "@rollup/rollup-linux-s390x-gnu": "4.21.3", + "@rollup/rollup-linux-x64-gnu": "4.21.3", + "@rollup/rollup-linux-x64-musl": "4.21.3", + "@rollup/rollup-win32-arm64-msvc": "4.21.3", + "@rollup/rollup-win32-ia32-msvc": "4.21.3", + "@rollup/rollup-win32-x64-msvc": "4.21.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", + "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", + "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/typescript": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vite": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", + "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.1.tgz", + "integrity": "sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.6", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-plugin-dts": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-4.2.1.tgz", + "integrity": "sha512-/QlYvgUMiv8+ZTEerhNCYnYaZMM07cdlX6hQCR/w/g/nTh0tUXPoYwbT6SitizLJ9BybT1lnrcZgqheI6wromQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor": "7.47.7", + "@rollup/pluginutils": "^5.1.0", + "@volar/typescript": "^2.4.4", + "@vue/language-core": "2.1.6", + "compare-versions": "^6.1.1", + "debug": "^4.3.6", + "kolorist": "^1.8.0", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.11" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "typescript": "*", + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.1.tgz", + "integrity": "sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.1", + "@vitest/mocker": "2.1.1", + "@vitest/pretty-format": "^2.1.1", + "@vitest/runner": "2.1.1", + "@vitest/snapshot": "2.1.1", + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", + "chai": "^5.1.1", + "debug": "^4.3.6", + "magic-string": "^0.30.11", + "pathe": "^1.1.2", + "std-env": "^3.7.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.0", + "tinypool": "^1.0.0", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.1", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.1", + "@vitest/ui": "2.1.1", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/package.json b/package.json index 7919338..def303d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@armco/utils", - "version": "0.0.4", + "version": "0.0.6", "type": "module", "scripts": { "build": "rm -rf build && tsc && vite build", @@ -9,11 +9,23 @@ "publish:sh": "./publish.sh" }, "devDependencies": { + "@types/d3": "^7.4.3", + "@types/node": "^22.5.5", + "@types/react": "^18.3.7", + "@types/uuid": "^10.0.0", + "@vitejs/plugin-react": "^4.3.1", + "glob": "^11.0.0", "react": "^18.3.1", - "typescript": "^5.0.2" + "typescript": "^5.0.2", + "vite": "^5.4.6", + "vite-plugin-dts": "^4.2.1", + "vitest": "^2.1.1" }, "dependencies": { - "@armco/configs": "0.0.2" + "@armco/configs": "^0.0.3", + "@armco/types": "^0.0.9", + "d3": "^7.9.0", + "uuid": "^10.0.0" }, "peerDependencies": { "react": ">16.8.1" diff --git a/src/HOC.tsx b/src/HOC.tsx index f8f0cef..127e12e 100644 --- a/src/HOC.tsx +++ b/src/HOC.tsx @@ -1,5 +1,5 @@ import { ComponentType } from "react" -import { useTheme } from "." // Adjust the import path as needed +import { useTheme } from "./hooks" // Adjust the import path as needed export const withTheme =

( Component: ComponentType

, diff --git a/src/contexts.ts b/src/contexts.ts index ca75068..8a27bc8 100644 --- a/src/contexts.ts +++ b/src/contexts.ts @@ -1,6 +1,6 @@ import { createContext } from "react" import { ArContextType, ArThemes, FunctionType } from "@armco/types" -import { Helper } from "." +import Helper from "./helper" export const SlotterContext = createContext<{ slotted: Array diff --git a/src/dateHelper.ts b/src/dateHelper.ts index edf1e5b..17aca73 100644 --- a/src/dateHelper.ts +++ b/src/dateHelper.ts @@ -1,6 +1,6 @@ import { CalendarDate, Event, ArDateFormats } from "@armco/types" import { MONTH_INDEX } from "@armco/configs" -import { Helper } from "." +import Helper from "./helper" export interface CalendarOptions { /** diff --git a/src/domHelper.ts b/src/domHelper.ts index 31c5feb..343c893 100644 --- a/src/domHelper.ts +++ b/src/domHelper.ts @@ -6,7 +6,7 @@ import { ObjectType, Position, } from "@armco/types" -import { Helper } from "." +import Helper from "./helper" class DomHelper { static style = window.getComputedStyle(document.documentElement) diff --git a/src/gridHelper.ts b/src/gridHelper.ts index 397b42c..56bf8ad 100644 --- a/src/gridHelper.ts +++ b/src/gridHelper.ts @@ -1,6 +1,6 @@ import { v4 as uuid } from "uuid" import { FunctionType, GridToolbarSpecs, SlotDescriptor } from "@armco/types" -import { DomHelper } from "." +import DomHelper from "./domHelper" class GridHelper { // For x and y toolbars only diff --git a/src/providers.tsx b/src/providers.tsx index df1094c..3714569 100644 --- a/src/providers.tsx +++ b/src/providers.tsx @@ -7,7 +7,9 @@ import { PanelContent, User, } from "@armco/types" -import { ArContext, Helper } from "./index" +import { ArContext } from "./contexts" +import Helper from "./helper" + export const ArProvider = ({ children }: { children: ReactNode }) => { const [theme, setTheme] = useState(ArThemes.DARK1) const [isLoggedIn, setLoggedIn] = useState() diff --git a/tsconfig.json b/tsconfig.json index 54d05ab..a951d6b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,27 @@ { - "extends": "../../tsconfig.json", - "include": ["src"], + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "outDir": "build", + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": [ + "src" + ] } diff --git a/vite.config.ts b/vite.config.ts index 3018e48..93bd73b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,5 +1,5 @@ import { resolve } from "node:path" -import glob from "glob" +import {glob} from "glob" import { defineConfig } from "vitest/config" import react from "@vitejs/plugin-react" import dts from "vite-plugin-dts" @@ -10,23 +10,23 @@ export default defineConfig({ build: { outDir: "build", lib: { - entry: glob.sync(resolve(__dirname, "src/**/*.ts")), - name: "@armco/utils", - formats: ["es", "cjs"], + entry: glob.sync(resolve(__dirname, "src/**/*.{ts,tsx}")), }, rollupOptions: { treeshake: true, - external: ["react", "react/jsx-runtime", "react-dom", "moment"], + external: ["react", "react/jsx-runtime", "react-dom"], output: [ { format: "es", dir: "build/es", entryFileNames: "[name].js", + chunkFileNames: "[name].js", }, { format: "cjs", dir: "build/cjs", entryFileNames: "[name].js", + chunkFileNames: "[name].js", }, ], }, From eb12f43379f5713159cfd8cc620e6b1a6f111719 Mon Sep 17 00:00:00 2001 From: mohiit1502 Date: Fri, 20 Sep 2024 10:29:43 +0530 Subject: [PATCH 5/6] Updated build pipeline --- build-tools/build.sh | 29 +++++++++++++++++++++++++++++ build-tools/generate-module.js | 33 +++++++++++++++++++++++++++++++++ build-tools/post-processor.js | 23 +++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 4 ++-- 5 files changed, 89 insertions(+), 4 deletions(-) create mode 100755 build-tools/build.sh create mode 100644 build-tools/generate-module.js create mode 100644 build-tools/post-processor.js diff --git a/build-tools/build.sh b/build-tools/build.sh new file mode 100755 index 0000000..43bf4ce --- /dev/null +++ b/build-tools/build.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Get the directory of the current script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +rm -rf build + +# Quality check +npx tsc + +# Generate Styles (with unneeded js) +# vite build --config vite-css.config.ts + +vite build + +# Generated source bundle +# tsc -p tsconfig.cjs.build.json +# tsc -p tsconfig.es.build.json + +# Cleanup unneeded js +# rm -f build/*.js + +# # Copy image files +# find src/static -type f \( -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o -name "*.gif" -o -name "*.svg" \) -exec cp {} build/cjs/static/ \; +# find src/static -type f \( -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o -name "*.gif" -o -name "*.svg" \) -exec cp {} build/es/static/ \; + +# Run Post processors: Update style imports in .js files, create component modules +node "$SCRIPT_DIR/post-processor.js" build/cjs +node "$SCRIPT_DIR/post-processor.js" build/es diff --git a/build-tools/generate-module.js b/build-tools/generate-module.js new file mode 100644 index 0000000..699fa72 --- /dev/null +++ b/build-tools/generate-module.js @@ -0,0 +1,33 @@ +import { promises as fs } from "fs" +import { dirname, resolve } from "path" +import { fileURLToPath } from "url" + +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +const exclusions = ["enums.js", "navigator.js", "v4.js", "index.js"] + +async function generateModule(fileName) { + if (!exclusions.includes(fileName)) { + const dir = fileName.slice(0, -3) + const name = `@armco/utils/${dir}` + const packageJsonContent = { + name, + main: `../cjs/${dir}.js`, + module: `../es/${dir}.js`, + types: `../types/${dir}.d.ts`, + } + const dirPath = resolve(__dirname, `../build/${dir}`) + try { + await fs.mkdir(dirPath, { recursive: true }) + await fs.writeFile( + resolve(dirPath, "package.json"), + JSON.stringify(packageJsonContent, null, 2), + ) + } catch (error) { + console.error(`Error processing directory ${dirPath}:`, error) + } + } +} + +export default generateModule diff --git a/build-tools/post-processor.js b/build-tools/post-processor.js new file mode 100644 index 0000000..53c993f --- /dev/null +++ b/build-tools/post-processor.js @@ -0,0 +1,23 @@ +import { readdir } from "fs/promises" +import generateModule from "./generate-module.js" + +async function postProcessor(dir) { + try { + const files = await readdir(dir) + await Promise.all( + files.map(async (file) => { + await generateModule(file, dir) + }), + ) + } catch (error) { + console.error(`Error processing directory ${dir}:`, error) + } +} + +const targetDir = process.argv[2] +if (targetDir) { + postProcessor(targetDir) +} else { + console.error("Please provide the build directory to run post processor on.") + process.exit(1) +} diff --git a/package-lock.json b/package-lock.json index ab6c440..d837560 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@armco/utils", - "version": "0.0.6", + "version": "0.0.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@armco/utils", - "version": "0.0.6", + "version": "0.0.7", "license": "ISC", "dependencies": { "@armco/configs": "^0.0.3", diff --git a/package.json b/package.json index def303d..cf767d3 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "@armco/utils", - "version": "0.0.6", + "version": "0.0.7", "type": "module", "scripts": { - "build": "rm -rf build && tsc && vite build", + "build": "./build-tools/build.sh", "format": "prettier --write .", "lint": "eslint .", "publish:sh": "./publish.sh" From a49a6fee65669db18760d06fb83cc6be79411f4f Mon Sep 17 00:00:00 2001 From: mohiit1502 Date: Sat, 5 Oct 2024 23:09:35 +0530 Subject: [PATCH 6/6] Fixed compile errors, added publish scripts --- build-tools/build.sh | 17 - build-tools/generate-module.js | 2 +- package-lock.json | 34 +- package.json | 8 +- publish-local.sh | 16 + publish.sh | 10 +- src/adapters.ts | 182 ++-- src/contexts.ts | 4 +- src/dateHelper.ts | 8 +- src/dateformat.ts | 5 +- src/domHelper.ts | 810 +++++++++--------- src/gridHelper.ts | 1444 ++++++++++++++++---------------- src/helper.tsx | 978 +++++++++++---------- src/index.ts | 16 +- src/network.ts | 56 +- src/providers.tsx | 4 +- src/recursionHelper.tsx | 110 ++- src/validators.ts | 24 +- vite.config.ts | 4 +- 19 files changed, 1860 insertions(+), 1872 deletions(-) create mode 100755 publish-local.sh diff --git a/build-tools/build.sh b/build-tools/build.sh index 43bf4ce..0daf942 100755 --- a/build-tools/build.sh +++ b/build-tools/build.sh @@ -4,26 +4,9 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" rm -rf build - -# Quality check npx tsc - -# Generate Styles (with unneeded js) -# vite build --config vite-css.config.ts - vite build -# Generated source bundle -# tsc -p tsconfig.cjs.build.json -# tsc -p tsconfig.es.build.json - -# Cleanup unneeded js -# rm -f build/*.js - -# # Copy image files -# find src/static -type f \( -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o -name "*.gif" -o -name "*.svg" \) -exec cp {} build/cjs/static/ \; -# find src/static -type f \( -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o -name "*.gif" -o -name "*.svg" \) -exec cp {} build/es/static/ \; - # Run Post processors: Update style imports in .js files, create component modules node "$SCRIPT_DIR/post-processor.js" build/cjs node "$SCRIPT_DIR/post-processor.js" build/es diff --git a/build-tools/generate-module.js b/build-tools/generate-module.js index 699fa72..802cda7 100644 --- a/build-tools/generate-module.js +++ b/build-tools/generate-module.js @@ -5,7 +5,7 @@ import { fileURLToPath } from "url" const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) -const exclusions = ["enums.js", "navigator.js", "v4.js", "index.js"] +const exclusions = ["enums.js", "v4.js", "index.js"] async function generateModule(fileName) { if (!exclusions.includes(fileName)) { diff --git a/package-lock.json b/package-lock.json index d837560..884ac06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@armco/utils", - "version": "0.0.7", + "version": "0.0.18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@armco/utils", - "version": "0.0.7", + "version": "0.0.18", "license": "ISC", "dependencies": { - "@armco/configs": "^0.0.3", + "@armco/configs": "^0.0.7", "@armco/types": "^0.0.9", "d3": "^7.9.0", "uuid": "^10.0.0" @@ -25,6 +25,7 @@ "typescript": "^5.0.2", "vite": "^5.4.6", "vite-plugin-dts": "^4.2.1", + "vite-plugin-externalize-deps": "^0.8.0", "vitest": "^2.1.1" }, "peerDependencies": { @@ -46,15 +47,21 @@ } }, "node_modules/@armco/configs": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@armco/configs/-/configs-0.0.3.tgz", - "integrity": "sha512-1gdJmMIkAVttA8D2gZmKlVEs5mEnSkr94BjZfyQa0/A6oQgVt2EMEgJmCUkHy2YzitFm8a8ZtOfM3cvdKTQoWg==", + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@armco/configs/-/configs-0.0.7.tgz", + "integrity": "sha512-iniTmpR0kaOmRXAKw7cmXMyVnd1M+gOG/gNEOvmNAxRt8AuvW8WBra96o6oHnk0LFPgZhzmKSHvWzO1ADErxZQ==", "license": "ISC", "dependencies": { - "@armco/types": "^0.0.9", + "@armco/types": "^0.0.10", "uuid": "^10.0.0" } }, + "node_modules/@armco/configs/node_modules/@armco/types": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@armco/types/-/types-0.0.10.tgz", + "integrity": "sha512-PKmehb5PsX6o6b3yaItCyDJxYy5nyXBduONnWv6slQwBMareFquGUcrCORPQhvnVLprCTs5aIZyPerZdjVuuxg==", + "license": "MIT" + }, "node_modules/@armco/types": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/@armco/types/-/types-0.0.9.tgz", @@ -4070,6 +4077,19 @@ } } }, + "node_modules/vite-plugin-externalize-deps": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/vite-plugin-externalize-deps/-/vite-plugin-externalize-deps-0.8.0.tgz", + "integrity": "sha512-MdC8kRNQ1ZjhUicU2HcqGVhL0UUFqv83Zp1JZdHjE82PoPR8wsSWZ3axpot7B6img3sW6g8shYJikE0CKA0chA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/voracious" + }, + "peerDependencies": { + "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, "node_modules/vitest": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.1.tgz", diff --git a/package.json b/package.json index cf767d3..e1d7f92 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,13 @@ { "name": "@armco/utils", - "version": "0.0.7", + "version": "0.0.18", "type": "module", "scripts": { "build": "./build-tools/build.sh", "format": "prettier --write .", "lint": "eslint .", - "publish:sh": "./publish.sh" + "publish:sh": "./publish.sh", + "publish:local": "./publish-local.sh" }, "devDependencies": { "@types/d3": "^7.4.3", @@ -19,10 +20,11 @@ "typescript": "^5.0.2", "vite": "^5.4.6", "vite-plugin-dts": "^4.2.1", + "vite-plugin-externalize-deps": "^0.8.0", "vitest": "^2.1.1" }, "dependencies": { - "@armco/configs": "^0.0.3", + "@armco/configs": "^0.0.7", "@armco/types": "^0.0.9", "d3": "^7.9.0", "uuid": "^10.0.0" diff --git a/publish-local.sh b/publish-local.sh new file mode 100755 index 0000000..46b66a6 --- /dev/null +++ b/publish-local.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +semver=${1:-patch} + +set -e + +npm run build +cp package.json build/ +sed -i '' -E 's/"build"/"*"/' build/package.json + +sed -i '' 's#"build/cjs/index.js"#"cjs/index.js"#' build/package.json +sed -i '' 's#"build/es/index.js"#"es/index.js"#' build/package.json +sed -i '' 's#"build/types/index.d.ts"#"types/index.d.ts"#' build/package.json + +cd build +npm pack --pack-destination ~/__Projects__/Common \ No newline at end of file diff --git a/publish.sh b/publish.sh index a0d2fb4..94621ea 100755 --- a/publish.sh +++ b/publish.sh @@ -2,7 +2,15 @@ semver=${1:-patch} -npm run build set -e npm --no-git-tag-version version ${semver} +npm run build +cp package.json build/ +sed -i '' -E 's/"build"/"*"/' build/package.json + +sed -i '' 's#"build/cjs/index.js"#"cjs/index.js"#' build/package.json +sed -i '' 's#"build/es/index.js"#"es/index.js"#' build/package.json +sed -i '' 's#"build/types/index.d.ts"#"types/index.d.ts"#' build/package.json + +cd build npm publish --access public --loglevel verbose diff --git a/src/adapters.ts b/src/adapters.ts index fa4fb7d..3d81edf 100644 --- a/src/adapters.ts +++ b/src/adapters.ts @@ -6,7 +6,7 @@ import { TreeListData, RecusionConditionTypes, } from "@armco/types" -import RecursionHelper from "./recursionHelper" +import { injectIds } from "./recursionHelper" const years = Array.from({ length: 60 }, (_, i) => 1964 + i) const countries = ["IN", "US", "RU", "CN", "UK", "JP", "FR", "IT", "SP"] @@ -29,100 +29,96 @@ const dummyProgressiveChartData: ThreeDChartDataArrayFormat = countries.flatMap( }, ) -class Adapter { - static adaptToTreeFromComponentConfig(data: any): Array { - const returnTreeList: Array = [] - Object.keys(data).forEach((key) => { - const groupConfig = data[key as keyof any] - const components = groupConfig.components - components.sort((c1: ComponentDescription, c2: ComponentDescription) => - (c1.name as any) > (c2.name as any) ? 1 : -1, - ) - const obj: TreeListData = { - label: groupConfig.label, - children: [], - data: { ...groupConfig }, - } - components.forEach((item: any) => { - const treeItem: TreeListData = { - label: typeof item === "string" ? item : item.name, - data: { - component: typeof item === "string" ? item : item.name, - hierarchy: key, - }, - } - const children: Array = [] - treeItem.children = children - if (item.variants && Object.keys(item.variants.length > 0)) { - Object.keys(item.variants).forEach((variantKey) => { - const variants = item.variants[variantKey] - treeItem.children && - treeItem.children.push({ - label: variantKey, - data: { name: typeof item === "string" ? item : item.name }, - children: variants.map((v: string) => ({ - label: v, - data: { - component: typeof item === "string" ? item : item.name, - props: { [variantKey]: v }, - hierarchy: key, - }, - })), - }) - }) - } - obj.children && obj.children.push(treeItem) - }) - returnTreeList.push(obj) - }) - RecursionHelper.injectIds({ - data: returnTreeList, - condition: { type: RecusionConditionTypes.KEY_EXISTS, key: "label" }, - iterateOn: "children", - }) - return returnTreeList - } - - static adaptToProgressiveChart( - inputData: ProgressiveChartData, - demo?: boolean, - ): ThreeDChartDataObjectFormat { - if (demo && !inputData) { - inputData = dummyProgressiveChartData +export function adaptToTreeFromComponentConfig(data: any): Array { + const returnTreeList: Array = [] + Object.keys(data).forEach((key) => { + const groupConfig = data[key as keyof any] + const components = groupConfig.components + components.sort((c1: ComponentDescription, c2: ComponentDescription) => + (c1.name as any) > (c2.name as any) ? 1 : -1, + ) + const obj: TreeListData = { + label: groupConfig.label, + children: [], + data: { ...groupConfig }, } - let unsortedData: ThreeDChartDataObjectFormat = {} - - // Transform the data into the desired format - if (Array.isArray(inputData)) { - inputData.forEach(([key, xValue, yValue]) => { - if (!unsortedData[key]) { - unsortedData[key] = [[xValue, yValue]] - } else { - unsortedData[key].push([xValue, yValue]) - } - }) - } else { - unsortedData = inputData - } - - // Sort the keys - const keys = Object.keys(unsortedData) - keys.sort((a, b) => { - if (typeof a === "number" && typeof b === "number") { - return a - b - } else { - return a.localeCompare(b) + components.forEach((item: any) => { + const treeItem: TreeListData = { + label: typeof item === "string" ? item : item.name, + data: { + component: typeof item === "string" ? item : item.name, + hierarchy: key, + }, } + const children: Array = [] + treeItem.children = children + if (item.variants && Object.keys(item.variants.length > 0)) { + Object.keys(item.variants).forEach((variantKey) => { + const variants = item.variants[variantKey] + treeItem.children && + treeItem.children.push({ + label: variantKey, + data: { name: typeof item === "string" ? item : item.name }, + children: variants.map((v: string) => ({ + label: v, + data: { + component: typeof item === "string" ? item : item.name, + props: { [variantKey]: v }, + hierarchy: key, + }, + })), + }) + }) + } + obj.children && obj.children.push(treeItem) }) - - // Create a new object with the sorted keys - let sortedData: ThreeDChartDataObjectFormat = {} - keys.forEach((key) => { - sortedData[key] = unsortedData[key] - }) - - return sortedData - } + returnTreeList.push(obj) + }) + injectIds({ + data: returnTreeList, + condition: { type: RecusionConditionTypes.KEY_EXISTS, key: "label" }, + iterateOn: "children", + }) + return returnTreeList } -export default Adapter +export function adaptToProgressiveChart( + inputData: ProgressiveChartData, + demo?: boolean, +): ThreeDChartDataObjectFormat { + if (demo && !inputData) { + inputData = dummyProgressiveChartData + } + let unsortedData: ThreeDChartDataObjectFormat = {} + + // Transform the data into the desired format + if (Array.isArray(inputData)) { + inputData.forEach(([key, xValue, yValue]) => { + if (!unsortedData[key]) { + unsortedData[key] = [[xValue, yValue]] + } else { + unsortedData[key].push([xValue, yValue]) + } + }) + } else { + unsortedData = inputData + } + + // Sort the keys + const keys = Object.keys(unsortedData) + keys.sort((a, b) => { + if (typeof a === "number" && typeof b === "number") { + return a - b + } else { + return a.localeCompare(b) + } + }) + + // Create a new object with the sorted keys + let sortedData: ThreeDChartDataObjectFormat = {} + keys.forEach((key) => { + sortedData[key] = unsortedData[key] + }) + + return sortedData +} diff --git a/src/contexts.ts b/src/contexts.ts index 8a27bc8..fa50951 100644 --- a/src/contexts.ts +++ b/src/contexts.ts @@ -1,6 +1,6 @@ import { createContext } from "react" import { ArContextType, ArThemes, FunctionType } from "@armco/types" -import Helper from "./helper" +import { isMobile } from "./helper" export const SlotterContext = createContext<{ slotted: Array @@ -9,7 +9,7 @@ export const SlotterContext = createContext<{ export const ArContext = createContext({ theme: ArThemes.DARK1, - drawerState: { collapsed: Helper.isMobile() }, + drawerState: { collapsed: isMobile() }, notify: () => {}, setDrawerState: () => {}, setLeftPanelContent: () => {}, diff --git a/src/dateHelper.ts b/src/dateHelper.ts index 17aca73..7c529f0 100644 --- a/src/dateHelper.ts +++ b/src/dateHelper.ts @@ -1,6 +1,6 @@ import { CalendarDate, Event, ArDateFormats } from "@armco/types" -import { MONTH_INDEX } from "@armco/configs" -import Helper from "./helper" +import { MONTH_INDEX } from "@armco/configs/constants" +import { pad } from "./helper" export interface CalendarOptions { /** @@ -402,8 +402,8 @@ class Calendar { separator: string = "/", format: string = "DDMMYYYY", ) { - const day = Helper.pad(date.day, separator) - const month = Helper.pad(date.month + 1, separator) + const day = pad(date.day, separator) + const month = pad(date.month + 1, separator) const year = date.year switch (format) { diff --git a/src/dateformat.ts b/src/dateformat.ts index e7b31cb..0a633dd 100644 --- a/src/dateformat.ts +++ b/src/dateformat.ts @@ -1,5 +1,4 @@ -import { FunctionType } from "@armco/types" -import { ArDateMasks } from "@armco/types" +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 @@ -334,3 +333,5 @@ export const formatTimezone = (date: Date) => { ?.replace(timezoneClip, "") .replace(/GMT\+0000/g, "UTC") } + +export { dateFormat as DateFormatter } \ No newline at end of file diff --git a/src/domHelper.ts b/src/domHelper.ts index 343c893..4079032 100644 --- a/src/domHelper.ts +++ b/src/domHelper.ts @@ -6,451 +6,447 @@ import { ObjectType, Position, } from "@armco/types" -import Helper from "./helper" +import { getStylesFromClass } from "./helper" -class DomHelper { - static style = window.getComputedStyle(document.documentElement) - static fontSize = parseFloat(DomHelper.style.fontSize) - static markerSize = 7 +const style = window.getComputedStyle(document.documentElement) +const fontSize = parseFloat(style.fontSize) +const markerSize = 7 - static outerWidth(el: HTMLElement) { - let width = el.offsetWidth - const style = getComputedStyle(el) +export function outerWidth(el: HTMLElement) { + let width = el.offsetWidth + const style = getComputedStyle(el) - width += parseInt(style.marginLeft) + parseInt(style.marginRight) - return width - } + width += parseInt(style.marginLeft) + parseInt(style.marginRight) + return width +} - static 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" +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(",") + ")" + const translatedPosition = "(" + positionCss.join(",") + ")" - return transitionProp + translatedPosition - } + return transitionProp + translatedPosition +} - static hexToHsl(color: string, returnRaw?: boolean) { - const [r, g, b] = DomHelper.hexToRgb(color) - return DomHelper.rgbToHsl(r, g, b, returnRaw) - } +export function hexToHsl(color: string, returnRaw?: boolean) { + const [r, g, b] = hexToRgb(color) + return rgbToHsl(r, g, b, returnRaw) +} - static 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] - } +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 - */ - static rgbToHsl(r: number, g: number, b: number, returnRaw?: boolean) { - r /= 255 - g /= 255 - b /= 255 +/** + * 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 + 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) + 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 - } - - static 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) + 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 } - return [r * 255, g * 255, b * 255] + // @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) } - static 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) + 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)) +} + +export function implementSvgStyles( + svg: SVGSVGElement, + svgProps?: IconProps["attributes"], + flags?: Array, +) { + const { size, classes, colors, strokeWidth } = svgProps || {} + const { fillColor, strokeColor } = colors || {} + const [skipPathColorFill] = flags || [] + let styles = null + if (classes) { + styles = getStylesFromClass(classes) + styles = Object.entries(styles || {}) + .map(([property, value]) => `${property}: ${value}`) + .join("; ") + } + svg.setAttribute("height", size || "1rem") + svg.setAttribute("width", size || "1rem") + svg.setAttribute("class", classes || "") + svg.setAttribute("style", styles || "") + svg.setAttribute("stroke", strokeColor || "black") + strokeWidth + ? svg.setAttribute("stroke-width", strokeWidth) + : strokeColor && svg.setAttribute("stroke-width", "1") + let path: SVGPathElement | NodeListOf = + svg.querySelectorAll("path") + if (path.length === 1 && !skipPathColorFill) { + path = path[0] + // Override stroke and color of "path" node inside SVG at below line. + path && + strokeColor && + !!path.getAttribute("stroke") && + path.setAttribute("stroke", strokeColor) + path && + strokeWidth && + !!path.getAttribute("stroke-width") && + path.setAttribute("stroke-width", strokeWidth) + path && + fillColor && + !!path.getAttribute("fill") && + path.setAttribute("fill", fillColor) + } + svg.style.color = fillColor || "black" + return svg +} + +/** + * 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 } - static hslToHex(h: number, s: number, l: number) { - const [r, g, b] = DomHelper.hslToRgb(h, s, l) - return DomHelper.rgbToHex(Math.round(r), Math.round(g), Math.round(b)) + if (index === 0) { + return 0 } - static implementSvgStyles( - svg: SVGSVGElement, - svgProps?: IconProps["attributes"], - flags?: Array, - ) { - const { size, classes, colors, strokeWidth } = svgProps || {} - const { fillColor, strokeColor } = colors || {} - const [skipPathColorFill] = flags || [] - let styles = null - if (classes) { - styles = Helper.getStylesFromClass(classes) - styles = Object.entries(styles || {}) - .map(([property, value]) => `${property}: ${value}`) - .join("; ") + 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 } - svg.setAttribute("height", size || "1rem") - svg.setAttribute("width", size || "1rem") - svg.setAttribute("class", classes || "") - svg.setAttribute("style", styles || "") - svg.setAttribute("stroke", strokeColor || "black") - strokeWidth - ? svg.setAttribute("stroke-width", strokeWidth) - : strokeColor && svg.setAttribute("stroke-width", "1") - let path: SVGPathElement | NodeListOf = - svg.querySelectorAll("path") - if (path.length === 1 && !skipPathColorFill) { - path = path[0] - // Override stroke and color of "path" node inside SVG at below line. - path && - strokeColor && - !!path.getAttribute("stroke") && - path.setAttribute("stroke", strokeColor) - path && - strokeWidth && - !!path.getAttribute("stroke-width") && - path.setAttribute("stroke-width", strokeWidth) - path && - fillColor && - !!path.getAttribute("fill") && - path.setAttribute("fill", fillColor) - } - svg.style.color = fillColor || "black" - return svg + + return currentPosition } - /** - * Gets the list 'position' relative to a current index - * @param index - */ - static getPosition(index: number, props: CarouselProps): number { - if (props.infiniteLoop) { - // index has to be added by 1 because of the first cloned slide - ++index + 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 getDocumentElement(demo?: boolean) { + // Check if the component is running inside an iframe + if (demo) { + const iframeElement = + document.querySelector(".ar-Editor__frame") + if (iframeElement) { + return iframeElement.contentDocument } - - 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 } + // Return the parent document element by default + return document +} - /** - * Sets the 'position' transform for sliding animations - * @param position - * @param forceReflow - */ - static 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) - }) +export function getWindowElement(demo?: boolean) { + // Check if the component is running inside an iframe + if (demo) { + const iframeElement = + document.querySelector(".ar-Editor__frame") - return style - } - - static isKeyboardEvent( - e?: React.MouseEvent | React.KeyboardEvent, - ): e is React.KeyboardEvent { - return e ? e.hasOwnProperty("key") : false - } - - static getDocumentElement(demo?: boolean) { - // Check if the component is running inside an iframe - if (demo) { - const iframeElement = - document.querySelector(".ar-Editor__frame") - if (iframeElement) { - return iframeElement.contentDocument - } + if (iframeElement) { + return iframeElement.contentWindow } - // Return the parent document element by default - return document } + // Return the parent document element by default + return window +} - static getWindowElement(demo?: boolean) { - // Check if the component is running inside an iframe - if (demo) { - const iframeElement = - document.querySelector(".ar-Editor__frame") - - if (iframeElement) { - return iframeElement.contentWindow - } - } - // Return the parent document element by default - return window +export function shouldUsePosition( + position: ArPopoverPositions, + anchorRect: DOMRect, + popoverRect: DOMRect, + demo?: boolean, +) { + const windowObj = getWindowElement(demo) || window + const exceeds: ObjectType = { + bottom: + anchorRect.top + anchorRect.height + popoverRect.height > + windowObj.innerHeight, + right: + anchorRect.left + anchorRect.width + popoverRect.width > + windowObj.innerWidth, + left: anchorRect.left - popoverRect.width < 0, + top: anchorRect.top - popoverRect.height < 0, } - - static shouldUsePosition( - position: ArPopoverPositions, - anchorRect: DOMRect, - popoverRect: DOMRect, - demo?: boolean, - ) { - const windowObj = DomHelper.getWindowElement(demo) || window - const exceeds: ObjectType = { - bottom: - anchorRect.top + anchorRect.height + popoverRect.height > - windowObj.innerHeight, - right: - anchorRect.left + anchorRect.width + popoverRect.width > - windowObj.innerWidth, - left: anchorRect.left - popoverRect.width < 0, - top: anchorRect.top - popoverRect.height < 0, - } - if (exceeds[position]) return false - if (position === "left" || position === "right") { - if ( - DomHelper.fontSize > anchorRect.top || - DomHelper.fontSize > anchorRect.bottom - ) - return false - } else { - if ( - DomHelper.fontSize > anchorRect.left || - DomHelper.fontSize > anchorRect.right - ) - return false - } - return position - } - - static 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 : DomHelper.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 : DomHelper.markerSize), - }, - [ArPopoverPositions.LEFT]: { - top: anchorRect.top + (anchorRect.height - popoverRect.height) / 2, - left: - anchorRect.left - - popoverRect.width - - (hideMarker ? 0 : DomHelper.markerSize), - }, - [ArPopoverPositions.TOP]: { - top: - anchorRect.top - - popoverRect.height - - (hideMarker ? 0 : DomHelper.markerSize) - - (topOffset || 0), - left: anchorRect.left + (anchorRect.width - popoverRect.width) / 2, - }, - } - let { left, top } = popoverPositions[position] + if (exceeds[position]) return false + if (position === "left" || position === "right") { if ( - position === ArPopoverPositions.BOTTOM || - position === ArPopoverPositions.TOP + 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, + demo?: boolean, +) { + 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, demo) ) { - if ((left as number) < 0) { - left = DomHelper.fontSize - } - if ((left as number) + popoverRect.width > window.innerWidth) { - left = `calc(100% - ${popoverRect.width}px - ${DomHelper.fontSize}px)` - } - } else { - if ((top as number) < 0) { - top = DomHelper.fontSize - } - if ((top as number) + popoverRect.height > window.innerHeight) { - top = `calc(100% - ${popoverRect.height}px - ${DomHelper.fontSize}px)` - } + return position } - return { left, top } + continue } - static getPositionToUse( - anchorRect: DOMRect, - popoverRect: DOMRect, - requestedPosition?: ArPopoverPositions, - demo?: boolean, - ) { - if (requestedPosition && requestedPosition !== ArPopoverPositions.AUTO) { - return requestedPosition - } - const positionsPriority = [ - ArPopoverPositions.BOTTOM, - ArPopoverPositions.RIGHT, - ArPopoverPositions.LEFT, - ArPopoverPositions.TOP, - ] + return ArPopoverPositions.BOTTOM +} - for (const position of positionsPriority) { - if ( - DomHelper.shouldUsePosition(position, anchorRect, popoverRect, demo) - ) { - return position +export function calculatePopoverPosition( + anchorRect: DOMRect, + popoverRect: DOMRect, + requestedPosition?: ArPopoverPositions, + hideMarker?: boolean, + clickCoordinates?: Array | null, + demo?: boolean, + topOffset?: number, +) { + const windowObj = getWindowElement(demo) || window + anchorRect = clickCoordinates + ? { + left: clickCoordinates[0], + x: clickCoordinates[0], + top: clickCoordinates[1], + y: clickCoordinates[1], + height: 0, + width: 0, + right: windowObj.screen.width - clickCoordinates[0], + bottom: windowObj.screen.height - clickCoordinates[1], + toJSON: () => {}, } - continue - } - - return ArPopoverPositions.BOTTOM - } - - static calculatePopoverPosition( - anchorRect: DOMRect, - popoverRect: DOMRect, - requestedPosition?: ArPopoverPositions, - hideMarker?: boolean, - clickCoordinates?: Array | null, - demo?: boolean, - topOffset?: number, - ) { - const windowObj = DomHelper.getWindowElement(demo) || window - anchorRect = clickCoordinates - ? { - left: clickCoordinates[0], - x: clickCoordinates[0], - top: clickCoordinates[1], - y: clickCoordinates[1], - height: 0, - width: 0, - right: windowObj.screen.width - clickCoordinates[0], - bottom: windowObj.screen.height - clickCoordinates[1], - toJSON: () => {}, - } - : anchorRect - const position = DomHelper.getPositionToUse( - anchorRect, - popoverRect, - requestedPosition, - demo, - ) - const { left, top } = DomHelper.adjustPosition( - position, - anchorRect, - popoverRect, - hideMarker, - topOffset, - ) - return { - leftTop: { - top: ("" + top).startsWith("calc") ? top : top + "px", - left: ("" + left).startsWith("calc") ? left : left + "px", - }, - position, - } - } - - static openInNewTab(url: string) { - const win = window.open(url, "_blank") - if (win != null) { - win.focus() - } - } - - static download(dataURL: any) { - const anchor = document.createElement("a") - anchor.href = dataURL - anchor.download = "edited-image.png" - document.body.appendChild(anchor) - anchor.click() - document.body.removeChild(anchor) + : anchorRect + const position = getPositionToUse( + anchorRect, + popoverRect, + requestedPosition, + demo, + ) + 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 default DomHelper +export function openInNewTab(url: string) { + const win = window.open(url, "_blank") + if (win != null) { + win.focus() + } +} + +export function download(data: string | Blob, filename?: string) { + const anchor = document.createElement("a") + anchor.href = typeof data === "string" ? data : window.URL.createObjectURL(data) + anchor.download = filename || "edited-image.png" + document.body.appendChild(anchor) + anchor.click() + document.body.removeChild(anchor) +} diff --git a/src/gridHelper.ts b/src/gridHelper.ts index 56bf8ad..28d3d88 100644 --- a/src/gridHelper.ts +++ b/src/gridHelper.ts @@ -1,737 +1,733 @@ import { v4 as uuid } from "uuid" import { FunctionType, GridToolbarSpecs, SlotDescriptor } from "@armco/types" -import DomHelper from "./domHelper" +import { getDocumentElement } from "./domHelper" -class GridHelper { // For x and y toolbars only - static generateGridToolbarSpecs( - gridArea: Array>, - rowHeight?: Array | string, - colWidth?: Array | string, - ) { - const rowToolsGridArea = gridArea.map((row) => [ - row.every((cell) => cell === row[0]) ? row[0] : "ga-" + uuid(), - ]) - const colToolsGridArea = gridArea[0].map((_, i) => - gridArea.every((row) => row[i] === gridArea[0][i]) - ? gridArea[0][i] - : "ga-" + uuid(), - ) - const rowSlots = GridHelper.countOccurrences( - rowToolsGridArea.map((ga) => ga[0]), - ).map((slotConfig) => { +export function generateGridToolbarSpecs( + gridArea: Array>, + rowHeight?: Array | string, + colWidth?: Array | string, +) { + const rowToolsGridArea = gridArea.map((row) => [ + row.every((cell) => cell === row[0]) ? row[0] : "ga-" + uuid(), + ]) + const colToolsGridArea = gridArea[0].map((_, i) => + gridArea.every((row) => row[i] === gridArea[0][i]) + ? gridArea[0][i] + : "ga-" + uuid(), + ) + const rowSlots = countOccurrences( + rowToolsGridArea.map((ga) => ga[0]), + ).map((slotConfig) => { + return { + slot: slotConfig.slotId, + gridArea: slotConfig.slotId, + row: slotConfig.location, + column: 0, + rowSpan: slotConfig.span, + colSpan: 1, + style: { gridArea: slotConfig.slotId }, + } + }) + const colSlots = countOccurrences(colToolsGridArea).map( + (slotConfig) => { return { slot: slotConfig.slotId, gridArea: slotConfig.slotId, - row: slotConfig.location, - column: 0, - rowSpan: slotConfig.span, - colSpan: 1, + row: 0, + column: slotConfig.location, + rowSpan: 1, + colSpan: slotConfig.span, style: { gridArea: slotConfig.slotId }, } - }) - const colSlots = GridHelper.countOccurrences(colToolsGridArea).map( - (slotConfig) => { - return { - slot: slotConfig.slotId, - gridArea: slotConfig.slotId, - row: 0, - column: slotConfig.location, - rowSpan: 1, - colSpan: slotConfig.span, - style: { gridArea: slotConfig.slotId }, - } - }, - ) - return { - rowTools: { - slots: rowSlots, - gridTemplate: this.generateGridTemplate( - rowToolsGridArea, - rowHeight || "minmax(auto, 1fr)", - colWidth || "1fr", - ), - }, - colTools: { - slots: colSlots, - gridTemplate: this.generateGridTemplate( - [colToolsGridArea], - "1fr", - "1fr", - ), - }, - } - } - - static calculateRowHeights( - slots: Array, - rowHeights?: Array | string, - demo?: boolean, - ): Array { - const totalRows = Math.max(...slots.map((slot) => slot.row + slot.rowSpan)) - const allRowHeights: Array = [] - const doc = DomHelper.getDocumentElement(demo) - rowHeights = Array.isArray(rowHeights) - ? rowHeights - : (Array.from( - { length: totalRows }, - () => rowHeights || "minmax(2rem, auto)", - ) as Array) - - for (let rowIndex = 0; rowIndex < totalRows; rowIndex++) { - if ( - rowHeights[rowIndex] && - rowHeights[rowIndex] !== "" && - rowHeights[rowIndex].indexOf("auto") === -1 - ) { - allRowHeights[rowIndex] = rowHeights[rowIndex] - continue - } - - const rowSlots = slots.filter( - (slot) => slot.row <= rowIndex && rowIndex < slot.row + slot.rowSpan, - ) - const heights = rowSlots.map((slot) => { - const element = doc?.getElementById(slot.slot) - return element - ? Math.floor(element.getBoundingClientRect().height / slot.rowSpan) - : 0 - }) - const maxHeight = Math.max(...heights) - allRowHeights[rowIndex] = - maxHeight === 0 ? "minmax(2rem, auto)" : `${maxHeight}px` - } - - return allRowHeights - } - - static generateGridAreaAndSizes( - slots: Array, - currentRowHeights?: string | Array, - ): { gridArea: Array>; rowHeights: Array } { - let gridArea: Array> = [[]] - const rowHeights: Array = [] - slots.forEach((slot) => { - let endRow = slot.row + slot.rowSpan - let endColumn = slot.column + slot.colSpan - for (let row = slot.row; row < endRow; row++) { - const currentRowHeight = Array.isArray(currentRowHeights) - ? currentRowHeights[row] - : currentRowHeights - if (slot.content) { - rowHeights[row] = currentRowHeight || "auto" - } else { - rowHeights[row] = "minmax(2rem, auto)" - } - if (!gridArea[row]) { - gridArea[row] = [] - } - for (let col = slot.column; col < endColumn; col++) { - gridArea[row][col] = slot.gridArea - } - } - }) - - return { gridArea, rowHeights } - } - - static generateGridTemplate( - gridArea: Array>, - rowHeight?: Array | string, - colWidth?: Array | string, - ): string { - let areas = gridArea - .map( - (row, i) => - `"${row.join(" ")}" ${ - Array.isArray(rowHeight) - ? rowHeight[i] || "auto" - : rowHeight || "minmax(3rem, 1fr)" - }`, - ) - .join(" ") - - const gridTemplateColumns = - " / " + - (Array.isArray(colWidth) - ? colWidth.join(" ") - : Array.from( - { length: gridArea[0].length }, - () => colWidth || "1fr", - ).join(" ")) - areas += gridTemplateColumns - - return areas.trim() - } - - static mergeHandler( - setSlots: FunctionType, - slotConfigs: Array, - minMaxSelections: { - minRow: number - maxRow: number - minColumn: number - maxColumn: number }, - primarySlotConfig?: SlotDescriptor, - ) { - if (slotConfigs) { - slotConfigs = JSON.parse(JSON.stringify(slotConfigs)) - const selectedSlotConfigs = slotConfigs.filter((sc) => sc.isSelected) - selectedSlotConfigs.sort((a, b) => { - if (a.row !== b.row) { - return a.row - b.row - } else { - return a.column - b.column - } - }) - const firstSlotConfig = selectedSlotConfigs[0] - if (primarySlotConfig) { - primarySlotConfig = slotConfigs.find( - (s) => s.slot === primarySlotConfig?.slot, - ) as SlotDescriptor - primarySlotConfig.row = firstSlotConfig.row - primarySlotConfig.column = firstSlotConfig.column - } else { - primarySlotConfig = firstSlotConfig - } - primarySlotConfig.rowSpan = - minMaxSelections.maxRow - minMaxSelections.minRow + 1 - primarySlotConfig.colSpan = - minMaxSelections.maxColumn - minMaxSelections.minColumn + 1 - selectedSlotConfigs.forEach((ssc) => { - const matchedSlotConfigIndex = slotConfigs.findIndex( - (sc) => sc.slot === ssc.slot, - ) - const matchedSlotConfig = slotConfigs[matchedSlotConfigIndex] - if ( - matchedSlotConfig && - matchedSlotConfig.slot !== primarySlotConfig?.slot - ) { - slotConfigs.splice(matchedSlotConfigIndex, 1) - } - }) - // slotConfigs.forEach((slotConfig) => (slotConfig.isSelected = false)) - setSlots(slotConfigs) - } - } - - static splitHandler( - setSlots: FunctionType, - slotConfigs: Array, - orientation: "horizontal" | "vertical", - placement: "before" | "after", - slot?: SlotDescriptor, - ) { - slotConfigs = JSON.parse(JSON.stringify(slotConfigs)) - let selectedSlots = [] - if (slot) { - slot = slotConfigs.find((s) => s.slot === slot?.slot) - slot && selectedSlots.push(slot) - } else { - selectedSlots = slotConfigs.filter((sc) => sc.isSelected) - } - selectedSlots.forEach((selectedSlot) => { - const { - row, - slot: selectedSlotId, - column, - colSpan, - rowSpan, - } = selectedSlot - const isHorizontal = orientation === "horizontal" - const isAfter = placement === "after" - const slotId = uuid() - const gridAreaValue = `ga-${slotId}` - const span = isHorizontal ? "rowSpan" : "colSpan" - const dimension = isHorizontal ? "row" : "column" - let newDimSpan = selectedSlot[span] - if (newDimSpan > 1) { - newDimSpan = selectedSlot[span] - 1 - selectedSlot[span] = newDimSpan - } - const newSlot: SlotDescriptor = { - slot: slotId, - row: isHorizontal ? (isAfter ? newDimSpan + row : row) : row, - column: isHorizontal ? column : isAfter ? newDimSpan + column : column, - rowSpan: isHorizontal ? 1 : rowSpan, - colSpan: isHorizontal ? colSpan : 1, - gridArea: gridAreaValue, - splitFrom: selectedSlotId, - } - - const shouldUpdateOtherSlots = isHorizontal - ? rowSpan === 1 - : colSpan === 1 - - // Update slot configs - shouldUpdateOtherSlots && - slotConfigs.forEach((slot) => { - const { - row: currRow, - column: currColumn, - slot: currSlot, - colSpan: currColSpan, - rowSpan: currRowSpan, - } = slot - if (isHorizontal) { - if ( - row >= currRow && - row < currRow + currRowSpan && - currSlot !== selectedSlotId - ) { - slot.rowSpan += 1 - } else if (currRow > row) { - slot.row += 1 - } - } else { - if ( - column >= currColumn && - column < currColumn + currColSpan && - currSlot !== selectedSlotId - ) { - slot.colSpan += 1 - } else if (currColumn > column) { - slot.column += 1 - } - } - }) - if (!isAfter) { - selectedSlot[dimension] += 1 - } - slotConfigs.push(newSlot) - }) - - setSlots(slotConfigs) - } - - static removeHandler( - slotConfigs: Array, - gridToolbarSpecs: GridToolbarSpecs, - type: string, - isInlineDelete?: boolean, - ): Array { - slotConfigs = JSON.parse(JSON.stringify(slotConfigs)) - const isRow = type === "row" - const toolbarSlots = isRow - ? gridToolbarSpecs.rowTools.slots - : gridToolbarSpecs.colTools.slots - // Find and sort selected slots from toolbarSpecs - const selectedToolbarSlots = toolbarSlots - .filter((slot) => - isInlineDelete ? slot.isSelectedForInlineDelete : slot.isSelected, - ) - .sort((a, b) => (isRow ? a.row - b.row : a.column - b.column)) - - // Iterate over each selected slot - selectedToolbarSlots.forEach((toolbarSlot) => { - // Find intersecting slots from slotConfigs - const intersectingSlots = slotConfigs.filter((slot) => - isRow - ? toolbarSlot.row >= slot.row && - toolbarSlot.row < slot.row + slot.rowSpan - : toolbarSlot.column >= slot.column && - toolbarSlot.column < slot.column + slot.colSpan, - ) - - // Delete slots with colSpan 1, decrement colSpan of rest by 1 - intersectingSlots.forEach((intersectingSlot) => { - if ( - isRow - ? intersectingSlot.rowSpan <= toolbarSlot.rowSpan - : intersectingSlot.colSpan <= toolbarSlot.colSpan - ) { - const index = slotConfigs.indexOf(intersectingSlot) - if (index !== -1) slotConfigs.splice(index, 1) - } else { - isRow - ? (intersectingSlot.rowSpan -= 1) - : (intersectingSlot.colSpan -= 1) - } - }) - - // Find next slots and decrement their row/column value by 1 - let succeedingSlots = slotConfigs.filter((slot) => - isRow ? slot.row > toolbarSlot.row : slot.column > toolbarSlot.column, - ) - succeedingSlots.forEach((succeedingSlot) => - isRow ? (succeedingSlot.row -= 1) : (succeedingSlot.column -= 1), - ) - - // Find succeeding toolbar slot and decrement their row/column value by 1 - succeedingSlots = toolbarSlots.filter((currToolbarSlot) => - isRow - ? currToolbarSlot.row > toolbarSlot.row - : currToolbarSlot.column > toolbarSlot.column, - ) - succeedingSlots.forEach((succeedingSlot) => - isRow ? (succeedingSlot.row -= 1) : (succeedingSlot.column -= 1), - ) - toolbarSlots.splice(toolbarSlots.indexOf(toolbarSlot), 1) - }) - - return slotConfigs - } - static checkIfAdjacent(slotConfigs: Array) { - const selectedSlotConfigs = slotConfigs.filter((sc) => sc.isSelected) - let minRow = Infinity - let maxRow = -Infinity - let minColumn = Infinity - let maxColumn = -Infinity - let totalCells = 0 - selectedSlotConfigs.forEach((config: SlotDescriptor) => { - if (config.row !== undefined && config.column !== undefined) { - minRow = Math.min(minRow, config.row) - maxRow = Math.max(maxRow, config.row + (config.rowSpan - 1)) - minColumn = Math.min(minColumn, config.column) - maxColumn = Math.max(maxColumn, config.column + (config.colSpan - 1)) - totalCells += config.rowSpan * config.colSpan - } - }) - const rectangleArea = (maxRow - minRow + 1) * (maxColumn - minColumn + 1) - - return { - areAdjacent: - selectedSlotConfigs.length > 1 && rectangleArea === totalCells, - minRow, - maxRow, - minColumn, - maxColumn, - } - } - - static isSlotSelected( - slot: SlotDescriptor, - rowSlots: Array, - colSlots: Array, - selectForDelete?: boolean, - ) { - const { row, rowSpan, column, colSpan } = slot - // For cells spanning multiple rows or columns we check if all rows or columns (toolbar cells) - // corresponding to this slot are selected, if either or all rows or all columns intersecting this cell - // are selected, we mark this cell selected. - const allRowsSelected = rowSlots - .filter((ts) => ts.row < row + rowSpan && ts.row + ts.rowSpan > row) - .every((ts) => - selectForDelete !== undefined - ? ts.isSelectedForInlineDelete - : ts.isSelected, - ) - const allColumnSelected = colSlots - .filter( - (ts) => ts.column < column + colSpan && ts.column + ts.colSpan > column, - ) - .every((ts) => - selectForDelete !== undefined - ? ts.isSelectedForInlineDelete - : ts.isSelected, - ) - return allRowsSelected || allColumnSelected - } - - static selectCellsInSelectedRowCol( - gridToolbarSpecs: GridToolbarSpecs, - slots: Array, - selectForDelete?: boolean, - ) { - const rowSlots = gridToolbarSpecs.rowTools.slots - const colSlots = gridToolbarSpecs.colTools.slots - slots.forEach((slot) => { - slot[ - selectForDelete !== undefined - ? "isSelectedForInlineDelete" - : "isSelected" - ] = this.isSlotSelected(slot, rowSlots, colSlots, selectForDelete) - }) - return [...slots] - } - - static countOccurrences(array: Array) { - const slots: Array<{ slotId: string; span: number; location: number }> = [] - for (let i = 0; i < array.length; i++) { - const value = array[i] - const existingObject = slots.find((obj) => obj.slotId === value) - if (existingObject === undefined) { - slots.push({ slotId: value, span: 1, location: i }) - } else { - existingObject.span++ - } - } - return slots - } - - static isIntersecting( - toolSlot: SlotDescriptor, - slot: SlotDescriptor, - isRow: boolean, - ) { - const dimension = isRow ? "row" : "column" - const span = isRow ? "rowSpan" : "colSpan" - const sRow = slot[dimension] - const sSpan = slot[span] - const tRow = toolSlot[dimension] - const tSpan = toolSlot[span] - return sRow <= tRow && sRow + sSpan >= tRow + tSpan - } - - static findIntersectingSlots( - slots: Array, - toolSlot: SlotDescriptor, - isRow: boolean, - ) { - return slots.filter((slot) => this.isIntersecting(toolSlot, slot, isRow)) - } - - static getCreateOrExtend( - slot: SlotDescriptor, - selectedToolSlot: SlotDescriptor, - placement: "before" | "after", - isRow: boolean, - ) { - const dimension = isRow ? "row" : "column" - const span = isRow ? "rowSpan" : "colSpan" - return placement === "before" - ? slot[dimension] === selectedToolSlot[dimension] - : slot[dimension] + slot[span] === - selectedToolSlot[dimension] + selectedToolSlot[span] - } - - static insertDimension( - type: "row" | "column", - gridToolbarSpecs: GridToolbarSpecs, - slots: Array, - setSlots: FunctionType, - placement: "before" | "after", - selection?: number, - ) { - slots = JSON.parse(JSON.stringify(slots)) - const isRow = type === "row" - let referenceSlots = gridToolbarSpecs[isRow ? "colTools" : "rowTools"].slots - let newSlots: Array = [] - if (referenceSlots.length > 0) { - const insertAt: Array = this.generateInsertionIndexes( - gridToolbarSpecs, - isRow, - placement, - selection, - ) - insertAt.sort() - const trackedSlotsForSpanIncrease: { - [key: string]: { d: SlotDescriptor; count: number } - } = {} - insertAt.forEach((insertionIndex, index) => { - // Insertion indexes represent which row/column new item will end up, and not what was actually selected, - // Below line gets the selected slot by adjusting index back - const selectedToolSlot = - gridToolbarSpecs[isRow ? "rowTools" : "colTools"].slots[ - insertionIndex - index - (placement === "after" ? 1 : 0) - ] - const intersectedSlots = this.findIntersectingSlots( - slots, - selectedToolSlot, - isRow, - ) - const newSlotsAtCurrentDimension = referenceSlots - .map((referenceSlot) => { - const lateralRefToolIntersectedSlot = intersectedSlots.find((s) => - this.isIntersecting(referenceSlot, s, !isRow), - ) - if (lateralRefToolIntersectedSlot) { - const shouldCreate = this.getCreateOrExtend( - lateralRefToolIntersectedSlot, - selectedToolSlot, - placement, - isRow, - ) - if (shouldCreate) { - const slotId = uuid() - const gridArea = `ga-${slotId}` - return { - slot: slotId, - row: isRow ? insertionIndex : referenceSlot.row, - column: isRow ? referenceSlot.column : insertionIndex, - rowSpan: isRow ? 1 : referenceSlot.rowSpan, - colSpan: isRow ? referenceSlot.colSpan : 1, - gridArea, - } - } else { - if ( - !trackedSlotsForSpanIncrease[ - lateralRefToolIntersectedSlot.slot - ] - ) { - trackedSlotsForSpanIncrease[ - lateralRefToolIntersectedSlot.slot - ] = { d: lateralRefToolIntersectedSlot, count: 1 } - } else { - trackedSlotsForSpanIncrease[ - lateralRefToolIntersectedSlot.slot - ].count += 1 - } - } - } - }) - .filter((s) => !!s) as Array - newSlots = newSlots.concat(newSlotsAtCurrentDimension) - }) - insertAt.forEach((dimNum) => { - const fixDim = isRow ? "row" : "column" - const correctableSlots = slots.filter((s) => s[fixDim] >= dimNum) - correctableSlots.forEach((s) => s[fixDim]++) - }) - Object.values(trackedSlotsForSpanIncrease).forEach( - (obj) => (obj.d[isRow ? "rowSpan" : "colSpan"] += obj.count), - ) - } else { - const slotId = uuid() - const gridArea = `ga-${slotId}` - newSlots.push({ - slot: uuid(), - row: 0, - column: 0, - rowSpan: 1, - colSpan: 1, - gridArea, - }) - } - slots = slots.concat(newSlots) - setSlots(slots) - } - - static generateInsertionIndexes( - gridToolbarSpecs: GridToolbarSpecs, - isRow: boolean, - placement: "before" | "after", - selection?: number, - ) { - let insertAt: Array = [] - if (selection !== undefined) { - insertAt.push(placement === "after" ? selection + 1 : selection) - } else { - if (isRow) { - insertAt = this.generateInsertionIndexesForDimension( - gridToolbarSpecs, - "rowTools", - "row", - placement, - isRow, - ) - } else { - insertAt = this.generateInsertionIndexesForDimension( - gridToolbarSpecs, - "colTools", - "column", - placement, - isRow, - ) - } - } - insertAt.sort() - // If multiple row/col to be added, indexes of all rows (columns) except the first one will change by a value - // equal to their index value in insertAt array (second should be incremented by 1, third by 2 and so on hence we can use indexes) - insertAt = insertAt.map((dimNum, index) => dimNum + index) - return insertAt - } - - static generateInsertionIndexesForDimension( - gridToolbarSpecs: GridToolbarSpecs, - tools: "rowTools" | "colTools", - dimension: "row" | "column", - placement: "before" | "after", - isRow: boolean, - ) { - let insertAt: Array = [] - const reverseDimSlots = - gridToolbarSpecs[isRow ? "rowTools" : "colTools"].slots - const selections = gridToolbarSpecs[tools].slots - .filter((s) => s.isSelected) - .map((s) => s[dimension] + (placement === "before" ? 0 : 1)) - if (selections.length > 0) { - insertAt = insertAt.concat(selections) - } else { - if (placement === "before") { - insertAt.push(0) - } else { - insertAt.push( - Math.max(...reverseDimSlots.map((rS) => rS[dimension])) + 1, - ) - } - } - return insertAt - } - - static generateSlots(rows: Array): Array { - const slotConfigs: Array = [] - const processedGridAreas = new Set() - - rows.forEach((row, rowIndex) => { - const parts = row.split(" ") - - parts.forEach((part, partIndex) => { - const gridArea = part.trim() - - if (!processedGridAreas.has(gridArea)) { - processedGridAreas.add(gridArea) - - // Calculate rowSpan and colSpan based on the repetitions in the grid template - const rowSpan = rows.filter((row) => row.includes(gridArea)).length - const colSpan = parts.filter((part) => part === gridArea).length - - slotConfigs.push({ - slot: gridArea, - gridArea, - row: rowIndex, - column: partIndex, - rowSpan, - colSpan, - }) - } - }) - }) - - return slotConfigs - } - - static generateSlotConfigs(gridTemplate: string): { - slotConfigs: Array - rowHeights: Array - colWidths: Array - } { - // Split by double quotes to separate row and column definitions - const parts = gridTemplate - .split('"') - .map((p) => p.trim()) - .filter((p) => p) - let colWidths - - const rowsAndHeights: { [key: string]: string } = {} - - // Identify row and column parts - parts.forEach((part, index) => { - if (part.startsWith("ga-")) { - rowsAndHeights[part] = "auto" - } else { - if (part.includes("calc(")) { - const calcEndIndex = part.lastIndexOf(")") - const calcExpression = part.substring(0, calcEndIndex + 1) - rowsAndHeights[parts[index - 1]] = calcExpression - - const remainingPart = part.substring(calcEndIndex + 1).trim() - if (remainingPart.includes("/")) { - const colPart = remainingPart.split("/")[1].trim() - colWidths = colPart.split(" ").map((p) => p.trim()) - } - } else { - rowsAndHeights[parts[index - 1]] = part - } - } - }) - - const rowHeights = Object.values(rowsAndHeights) - const slotConfigs = this.generateSlots(Object.keys(rowsAndHeights)) - - return { slotConfigs, rowHeights, colWidths: colWidths || [] } + ) + return { + rowTools: { + slots: rowSlots, + gridTemplate: generateGridTemplate( + rowToolsGridArea, + rowHeight || "minmax(auto, 1fr)", + colWidth || "1fr", + ), + }, + colTools: { + slots: colSlots, + gridTemplate: generateGridTemplate( + [colToolsGridArea], + "1fr", + "1fr", + ), + }, } } -export default GridHelper +export function calculateRowHeights( + slots: Array, + rowHeights?: Array | string, + demo?: boolean, +): Array { + const totalRows = Math.max(...slots.map((slot) => slot.row + slot.rowSpan)) + const allRowHeights: Array = [] + const doc = getDocumentElement(demo) + rowHeights = Array.isArray(rowHeights) + ? rowHeights + : (Array.from( + { length: totalRows }, + () => rowHeights || "minmax(2rem, auto)", + ) as Array) + + for (let rowIndex = 0; rowIndex < totalRows; rowIndex++) { + if ( + rowHeights[rowIndex] && + rowHeights[rowIndex] !== "" && + rowHeights[rowIndex].indexOf("auto") === -1 + ) { + allRowHeights[rowIndex] = rowHeights[rowIndex] + continue + } + + const rowSlots = slots.filter( + (slot) => slot.row <= rowIndex && rowIndex < slot.row + slot.rowSpan, + ) + const heights = rowSlots.map((slot) => { + const element = doc?.getElementById(slot.slot) + return element + ? Math.floor(element.getBoundingClientRect().height / slot.rowSpan) + : 0 + }) + const maxHeight = Math.max(...heights) + allRowHeights[rowIndex] = + maxHeight === 0 ? "minmax(2rem, auto)" : `${maxHeight}px` + } + + return allRowHeights +} + +export function generateGridAreaAndSizes( + slots: Array, + currentRowHeights?: string | Array, +): { gridArea: Array>; rowHeights: Array } { + let gridArea: Array> = [[]] + const rowHeights: Array = [] + slots.forEach((slot) => { + let endRow = slot.row + slot.rowSpan + let endColumn = slot.column + slot.colSpan + for (let row = slot.row; row < endRow; row++) { + const currentRowHeight = Array.isArray(currentRowHeights) + ? currentRowHeights[row] + : currentRowHeights + if (slot.content) { + rowHeights[row] = currentRowHeight || "auto" + } else { + rowHeights[row] = "minmax(2rem, auto)" + } + if (!gridArea[row]) { + gridArea[row] = [] + } + for (let col = slot.column; col < endColumn; col++) { + gridArea[row][col] = slot.gridArea + } + } + }) + + return { gridArea, rowHeights } +} + +export function generateGridTemplate( + gridArea: Array>, + rowHeight?: Array | string, + colWidth?: Array | string, +): string { + let areas = gridArea + .map( + (row, i) => + `"${row.join(" ")}" ${ + Array.isArray(rowHeight) + ? rowHeight[i] || "auto" + : rowHeight || "minmax(3rem, 1fr)" + }`, + ) + .join(" ") + + const gridTemplateColumns = + " / " + + (Array.isArray(colWidth) + ? colWidth.join(" ") + : Array.from( + { length: gridArea[0].length }, + () => colWidth || "1fr", + ).join(" ")) + areas += gridTemplateColumns + + return areas.trim() +} + +export function mergeHandler( + setSlots: FunctionType, + slotConfigs: Array, + minMaxSelections: { + minRow: number + maxRow: number + minColumn: number + maxColumn: number + }, + primarySlotConfig?: SlotDescriptor, +) { + if (slotConfigs) { + slotConfigs = JSON.parse(JSON.stringify(slotConfigs)) + const selectedSlotConfigs = slotConfigs.filter((sc) => sc.isSelected) + selectedSlotConfigs.sort((a, b) => { + if (a.row !== b.row) { + return a.row - b.row + } else { + return a.column - b.column + } + }) + const firstSlotConfig = selectedSlotConfigs[0] + if (primarySlotConfig) { + primarySlotConfig = slotConfigs.find( + (s) => s.slot === primarySlotConfig?.slot, + ) as SlotDescriptor + primarySlotConfig.row = firstSlotConfig.row + primarySlotConfig.column = firstSlotConfig.column + } else { + primarySlotConfig = firstSlotConfig + } + primarySlotConfig.rowSpan = + minMaxSelections.maxRow - minMaxSelections.minRow + 1 + primarySlotConfig.colSpan = + minMaxSelections.maxColumn - minMaxSelections.minColumn + 1 + selectedSlotConfigs.forEach((ssc) => { + const matchedSlotConfigIndex = slotConfigs.findIndex( + (sc) => sc.slot === ssc.slot, + ) + const matchedSlotConfig = slotConfigs[matchedSlotConfigIndex] + if ( + matchedSlotConfig && + matchedSlotConfig.slot !== primarySlotConfig?.slot + ) { + slotConfigs.splice(matchedSlotConfigIndex, 1) + } + }) + // slotConfigs.forEach((slotConfig) => (slotConfig.isSelected = false)) + setSlots(slotConfigs) + } +} + +export function splitHandler( + setSlots: FunctionType, + slotConfigs: Array, + orientation: "horizontal" | "vertical", + placement: "before" | "after", + slot?: SlotDescriptor, +) { + slotConfigs = JSON.parse(JSON.stringify(slotConfigs)) + let selectedSlots = [] + if (slot) { + slot = slotConfigs.find((s) => s.slot === slot?.slot) + slot && selectedSlots.push(slot) + } else { + selectedSlots = slotConfigs.filter((sc) => sc.isSelected) + } + selectedSlots.forEach((selectedSlot) => { + const { + row, + slot: selectedSlotId, + column, + colSpan, + rowSpan, + } = selectedSlot + const isHorizontal = orientation === "horizontal" + const isAfter = placement === "after" + const slotId = uuid() + const gridAreaValue = `ga-${slotId}` + const span = isHorizontal ? "rowSpan" : "colSpan" + const dimension = isHorizontal ? "row" : "column" + let newDimSpan = selectedSlot[span] + if (newDimSpan > 1) { + newDimSpan = selectedSlot[span] - 1 + selectedSlot[span] = newDimSpan + } + const newSlot: SlotDescriptor = { + slot: slotId, + row: isHorizontal ? (isAfter ? newDimSpan + row : row) : row, + column: isHorizontal ? column : isAfter ? newDimSpan + column : column, + rowSpan: isHorizontal ? 1 : rowSpan, + colSpan: isHorizontal ? colSpan : 1, + gridArea: gridAreaValue, + splitFrom: selectedSlotId, + } + + const shouldUpdateOtherSlots = isHorizontal + ? rowSpan === 1 + : colSpan === 1 + + // Update slot configs + shouldUpdateOtherSlots && + slotConfigs.forEach((slot) => { + const { + row: currRow, + column: currColumn, + slot: currSlot, + colSpan: currColSpan, + rowSpan: currRowSpan, + } = slot + if (isHorizontal) { + if ( + row >= currRow && + row < currRow + currRowSpan && + currSlot !== selectedSlotId + ) { + slot.rowSpan += 1 + } else if (currRow > row) { + slot.row += 1 + } + } else { + if ( + column >= currColumn && + column < currColumn + currColSpan && + currSlot !== selectedSlotId + ) { + slot.colSpan += 1 + } else if (currColumn > column) { + slot.column += 1 + } + } + }) + if (!isAfter) { + selectedSlot[dimension] += 1 + } + slotConfigs.push(newSlot) + }) + + setSlots(slotConfigs) +} + +export function removeHandler( + slotConfigs: Array, + gridToolbarSpecs: GridToolbarSpecs, + type: string, + isInlineDelete?: boolean, +): Array { + slotConfigs = JSON.parse(JSON.stringify(slotConfigs)) + const isRow = type === "row" + const toolbarSlots = isRow + ? gridToolbarSpecs.rowTools.slots + : gridToolbarSpecs.colTools.slots + // Find and sort selected slots from toolbarSpecs + const selectedToolbarSlots = toolbarSlots + .filter((slot) => + isInlineDelete ? slot.isSelectedForInlineDelete : slot.isSelected, + ) + .sort((a, b) => (isRow ? a.row - b.row : a.column - b.column)) + + // Iterate over each selected slot + selectedToolbarSlots.forEach((toolbarSlot) => { + // Find intersecting slots from slotConfigs + const intersectingSlots = slotConfigs.filter((slot) => + isRow + ? toolbarSlot.row >= slot.row && + toolbarSlot.row < slot.row + slot.rowSpan + : toolbarSlot.column >= slot.column && + toolbarSlot.column < slot.column + slot.colSpan, + ) + + // Delete slots with colSpan 1, decrement colSpan of rest by 1 + intersectingSlots.forEach((intersectingSlot) => { + if ( + isRow + ? intersectingSlot.rowSpan <= toolbarSlot.rowSpan + : intersectingSlot.colSpan <= toolbarSlot.colSpan + ) { + const index = slotConfigs.indexOf(intersectingSlot) + if (index !== -1) slotConfigs.splice(index, 1) + } else { + isRow + ? (intersectingSlot.rowSpan -= 1) + : (intersectingSlot.colSpan -= 1) + } + }) + + // Find next slots and decrement their row/column value by 1 + let succeedingSlots = slotConfigs.filter((slot) => + isRow ? slot.row > toolbarSlot.row : slot.column > toolbarSlot.column, + ) + succeedingSlots.forEach((succeedingSlot) => + isRow ? (succeedingSlot.row -= 1) : (succeedingSlot.column -= 1), + ) + + // Find succeeding toolbar slot and decrement their row/column value by 1 + succeedingSlots = toolbarSlots.filter((currToolbarSlot) => + isRow + ? currToolbarSlot.row > toolbarSlot.row + : currToolbarSlot.column > toolbarSlot.column, + ) + succeedingSlots.forEach((succeedingSlot) => + isRow ? (succeedingSlot.row -= 1) : (succeedingSlot.column -= 1), + ) + toolbarSlots.splice(toolbarSlots.indexOf(toolbarSlot), 1) + }) + + return slotConfigs +} +export function checkIfAdjacent(slotConfigs: Array) { + const selectedSlotConfigs = slotConfigs.filter((sc) => sc.isSelected) + let minRow = Infinity + let maxRow = -Infinity + let minColumn = Infinity + let maxColumn = -Infinity + let totalCells = 0 + selectedSlotConfigs.forEach((config: SlotDescriptor) => { + if (config.row !== undefined && config.column !== undefined) { + minRow = Math.min(minRow, config.row) + maxRow = Math.max(maxRow, config.row + (config.rowSpan - 1)) + minColumn = Math.min(minColumn, config.column) + maxColumn = Math.max(maxColumn, config.column + (config.colSpan - 1)) + totalCells += config.rowSpan * config.colSpan + } + }) + const rectangleArea = (maxRow - minRow + 1) * (maxColumn - minColumn + 1) + + return { + areAdjacent: + selectedSlotConfigs.length > 1 && rectangleArea === totalCells, + minRow, + maxRow, + minColumn, + maxColumn, + } +} + +export function isSlotSelected( + slot: SlotDescriptor, + rowSlots: Array, + colSlots: Array, + selectForDelete?: boolean, +) { + const { row, rowSpan, column, colSpan } = slot + // For cells spanning multiple rows or columns we check if all rows or columns (toolbar cells) + // corresponding to this slot are selected, if either or all rows or all columns intersecting this cell + // are selected, we mark this cell selected. + const allRowsSelected = rowSlots + .filter((ts) => ts.row < row + rowSpan && ts.row + ts.rowSpan > row) + .every((ts) => + selectForDelete !== undefined + ? ts.isSelectedForInlineDelete + : ts.isSelected, + ) + const allColumnSelected = colSlots + .filter( + (ts) => ts.column < column + colSpan && ts.column + ts.colSpan > column, + ) + .every((ts) => + selectForDelete !== undefined + ? ts.isSelectedForInlineDelete + : ts.isSelected, + ) + return allRowsSelected || allColumnSelected +} + +export function selectCellsInSelectedRowCol( + gridToolbarSpecs: GridToolbarSpecs, + slots: Array, + selectForDelete?: boolean, +) { + const rowSlots = gridToolbarSpecs.rowTools.slots + const colSlots = gridToolbarSpecs.colTools.slots + slots.forEach((slot) => { + slot[ + selectForDelete !== undefined + ? "isSelectedForInlineDelete" + : "isSelected" + ] = isSlotSelected(slot, rowSlots, colSlots, selectForDelete) + }) + return [...slots] +} + +export function countOccurrences(array: Array) { + const slots: Array<{ slotId: string; span: number; location: number }> = [] + for (let i = 0; i < array.length; i++) { + const value = array[i] + const existingObject = slots.find((obj) => obj.slotId === value) + if (existingObject === undefined) { + slots.push({ slotId: value, span: 1, location: i }) + } else { + existingObject.span++ + } + } + return slots +} + +export function isIntersecting( + toolSlot: SlotDescriptor, + slot: SlotDescriptor, + isRow: boolean, +) { + const dimension = isRow ? "row" : "column" + const span = isRow ? "rowSpan" : "colSpan" + const sRow = slot[dimension] + const sSpan = slot[span] + const tRow = toolSlot[dimension] + const tSpan = toolSlot[span] + return sRow <= tRow && sRow + sSpan >= tRow + tSpan +} + +export function findIntersectingSlots( + slots: Array, + toolSlot: SlotDescriptor, + isRow: boolean, +) { + return slots.filter((slot) => isIntersecting(toolSlot, slot, isRow)) +} + +export function getCreateOrExtend( + slot: SlotDescriptor, + selectedToolSlot: SlotDescriptor, + placement: "before" | "after", + isRow: boolean, +) { + const dimension = isRow ? "row" : "column" + const span = isRow ? "rowSpan" : "colSpan" + return placement === "before" + ? slot[dimension] === selectedToolSlot[dimension] + : slot[dimension] + slot[span] === + selectedToolSlot[dimension] + selectedToolSlot[span] +} + +export function insertDimension( + type: "row" | "column", + gridToolbarSpecs: GridToolbarSpecs, + slots: Array, + setSlots: FunctionType, + placement: "before" | "after", + selection?: number, +) { + slots = JSON.parse(JSON.stringify(slots)) + const isRow = type === "row" + let referenceSlots = gridToolbarSpecs[isRow ? "colTools" : "rowTools"].slots + let newSlots: Array = [] + if (referenceSlots.length > 0) { + const insertAt: Array = generateInsertionIndexes( + gridToolbarSpecs, + isRow, + placement, + selection, + ) + insertAt.sort() + const trackedSlotsForSpanIncrease: { + [key: string]: { d: SlotDescriptor; count: number } + } = {} + insertAt.forEach((insertionIndex, index) => { + // Insertion indexes represent which row/column new item will end up, and not what was actually selected, + // Below line gets the selected slot by adjusting index back + const selectedToolSlot = + gridToolbarSpecs[isRow ? "rowTools" : "colTools"].slots[ + insertionIndex - index - (placement === "after" ? 1 : 0) + ] + const intersectedSlots = findIntersectingSlots( + slots, + selectedToolSlot, + isRow, + ) + const newSlotsAtCurrentDimension = referenceSlots + .map((referenceSlot) => { + const lateralRefToolIntersectedSlot = intersectedSlots.find((s) => + isIntersecting(referenceSlot, s, !isRow), + ) + if (lateralRefToolIntersectedSlot) { + const shouldCreate = getCreateOrExtend( + lateralRefToolIntersectedSlot, + selectedToolSlot, + placement, + isRow, + ) + if (shouldCreate) { + const slotId = uuid() + const gridArea = `ga-${slotId}` + return { + slot: slotId, + row: isRow ? insertionIndex : referenceSlot.row, + column: isRow ? referenceSlot.column : insertionIndex, + rowSpan: isRow ? 1 : referenceSlot.rowSpan, + colSpan: isRow ? referenceSlot.colSpan : 1, + gridArea, + } + } else { + if ( + !trackedSlotsForSpanIncrease[ + lateralRefToolIntersectedSlot.slot + ] + ) { + trackedSlotsForSpanIncrease[ + lateralRefToolIntersectedSlot.slot + ] = { d: lateralRefToolIntersectedSlot, count: 1 } + } else { + trackedSlotsForSpanIncrease[ + lateralRefToolIntersectedSlot.slot + ].count += 1 + } + } + } + }) + .filter((s) => !!s) as Array + newSlots = newSlots.concat(newSlotsAtCurrentDimension) + }) + insertAt.forEach((dimNum) => { + const fixDim = isRow ? "row" : "column" + const correctableSlots = slots.filter((s) => s[fixDim] >= dimNum) + correctableSlots.forEach((s) => s[fixDim]++) + }) + Object.values(trackedSlotsForSpanIncrease).forEach( + (obj) => (obj.d[isRow ? "rowSpan" : "colSpan"] += obj.count), + ) + } else { + const slotId = uuid() + const gridArea = `ga-${slotId}` + newSlots.push({ + slot: uuid(), + row: 0, + column: 0, + rowSpan: 1, + colSpan: 1, + gridArea, + }) + } + slots = slots.concat(newSlots) + setSlots(slots) +} + +export function generateInsertionIndexes( + gridToolbarSpecs: GridToolbarSpecs, + isRow: boolean, + placement: "before" | "after", + selection?: number, +) { + let insertAt: Array = [] + if (selection !== undefined) { + insertAt.push(placement === "after" ? selection + 1 : selection) + } else { + if (isRow) { + insertAt = generateInsertionIndexesForDimension( + gridToolbarSpecs, + "rowTools", + "row", + placement, + isRow, + ) + } else { + insertAt = generateInsertionIndexesForDimension( + gridToolbarSpecs, + "colTools", + "column", + placement, + isRow, + ) + } + } + insertAt.sort() + // If multiple row/col to be added, indexes of all rows (columns) except the first one will change by a value + // equal to their index value in insertAt array (second should be incremented by 1, third by 2 and so on hence we can use indexes) + insertAt = insertAt.map((dimNum, index) => dimNum + index) + return insertAt +} + +export function generateInsertionIndexesForDimension( + gridToolbarSpecs: GridToolbarSpecs, + tools: "rowTools" | "colTools", + dimension: "row" | "column", + placement: "before" | "after", + isRow: boolean, +) { + let insertAt: Array = [] + const reverseDimSlots = + gridToolbarSpecs[isRow ? "rowTools" : "colTools"].slots + const selections = gridToolbarSpecs[tools].slots + .filter((s) => s.isSelected) + .map((s) => s[dimension] + (placement === "before" ? 0 : 1)) + if (selections.length > 0) { + insertAt = insertAt.concat(selections) + } else { + if (placement === "before") { + insertAt.push(0) + } else { + insertAt.push( + Math.max(...reverseDimSlots.map((rS) => rS[dimension])) + 1, + ) + } + } + return insertAt +} + +export function generateSlots(rows: Array): Array { + const slotConfigs: Array = [] + const processedGridAreas = new Set() + + rows.forEach((row, rowIndex) => { + const parts = row.split(" ") + + parts.forEach((part, partIndex) => { + const gridArea = part.trim() + + if (!processedGridAreas.has(gridArea)) { + processedGridAreas.add(gridArea) + + // Calculate rowSpan and colSpan based on the repetitions in the grid template + const rowSpan = rows.filter((row) => row.includes(gridArea)).length + const colSpan = parts.filter((part) => part === gridArea).length + + slotConfigs.push({ + slot: gridArea, + gridArea, + row: rowIndex, + column: partIndex, + rowSpan, + colSpan, + }) + } + }) + }) + + return slotConfigs +} + +export function generateSlotConfigs(gridTemplate: string): { + slotConfigs: Array + rowHeights: Array + colWidths: Array +} { + // Split by double quotes to separate row and column definitions + const parts = gridTemplate + .split('"') + .map((p) => p.trim()) + .filter((p) => p) + let colWidths + + const rowsAndHeights: { [key: string]: string } = {} + + // Identify row and column parts + parts.forEach((part, index) => { + if (part.startsWith("ga-")) { + rowsAndHeights[part] = "auto" + } else { + if (part.includes("calc(")) { + const calcEndIndex = part.lastIndexOf(")") + const calcExpression = part.substring(0, calcEndIndex + 1) + rowsAndHeights[parts[index - 1]] = calcExpression + + const remainingPart = part.substring(calcEndIndex + 1).trim() + if (remainingPart.includes("/")) { + const colPart = remainingPart.split("/")[1].trim() + colWidths = colPart.split(" ").map((p) => p.trim()) + } + } else { + rowsAndHeights[parts[index - 1]] = part + } + } + }) + + const rowHeights = Object.values(rowsAndHeights) + const slotConfigs = generateSlots(Object.keys(rowsAndHeights)) + + return { slotConfigs, rowHeights, colWidths: colWidths || [] } +} \ No newline at end of file diff --git a/src/helper.tsx b/src/helper.tsx index 7a5eb23..700afda 100644 --- a/src/helper.tsx +++ b/src/helper.tsx @@ -28,522 +28,502 @@ interface StringifyOnce { (obj: any, replacer?: ReplacerFunction | null, indent?: number): string } -class Helper { - static populatePagesInRoutes(routes: RouteConfig[], fallback: FunctionType) { - routes && - routes.forEach((route) => { - if (typeof route.element === "string") { - const elementName = route.element - const Component: JSX.ElementType = lazy(() => - import(`../pages/${elementName}/${elementName}.tsx`).catch( - fallback, - ), - ) - route.element = - if (route.children) { - Helper.populateComponentsInRoutes(route.children, fallback) - } - } - }) - } +/** + * + * @param pathOrComponentName + * @param fallback + * @returns Lazy component, can be used as + */ +export function lazyImport(pathOrComponentName: string, fallback: FunctionType) { + return lazy(() => + import(pathOrComponentName) + .catch(() => import(`@armco/components/${pathOrComponentName}`)) + .catch(() => import(`@armco/shared-components/${pathOrComponentName}`)) + .catch(fallback), + ) +} - static populateComponentsInRoutes( - routes: RouteConfig[], - fallback: FunctionType, - ) { - routes && - routes.forEach((route) => { - const hierarchy = route.hierarchy - if (typeof route.element === "string") { - const elementName = route.element - const Component: JSX.ElementType = lazy( - () => - import( - `../components/${hierarchy}/${elementName}/${elementName}.tsx` - ).catch(fallback), - // () => import(`../components/atoms/Component_404`) - ) - route.element = - if (route.children) { - Helper.populateComponentsInRoutes(route.children, fallback) - } +export function populatePagesInRoutes(routes: RouteConfig[], fallback: FunctionType) { + routes && + routes.forEach((route) => { + if (typeof route.element === "string") { + const elementName = route.element + const Component: JSX.ElementType = lazy(() => + import(`../pages/${elementName}/${elementName}.tsx`).catch( + fallback, + ), + ) + route.element = + if (route.children) { + populateComponentsInRoutes(route.children, fallback) } - }) - } - - static recrusiveFilter( - data: Array, - filter: string, - matchCase?: boolean, - searchKeys?: false | Array, - ) { - if (!filter || !data || data.length === 0) { - return data - } - if (!searchKeys || searchKeys.length === 0) { - searchKeys = ["label", "name", "id", "value"] - } - const dataClone = JSON.parse(JSON.stringify(data)) - const filteredItems = dataClone.filter((obj: any) => { - return ( - searchKeys && - searchKeys.reduce((acc, key) => { - let isMatch = - obj[key] && - (matchCase - ? obj[key].indexOf(filter) > -1 - : obj[key] && - obj[key] - .toString() - .toLowerCase() - .indexOf(filter.toLowerCase()) > -1) - if (obj.children) { - obj.children = Helper.recrusiveFilter( - obj.children, - filter, - matchCase, - ) - isMatch = isMatch || obj.children.length > 0 - } - return acc || isMatch - }, false) - ) + } }) - return filteredItems - } +} - // static search(args: SearchArgs): ObjectType | undefined { - // const { obj, key, value } = args - // if (Array.isArray(obj)) { - // return obj.find((item) => { - // args.obj = item - // return Helper.search(args) - // }) - // } else { - // if (obj) { - // if (key in obj && (value === undefined || obj[key] === value)) { - // return obj - // } - // return Object.values(obj).find((item) => { - // if (typeof item === "object" && !(item instanceof Date)) { - // args.obj = item as ObjectType - // return Helper.search(args) - // } - // }) as ObjectType - // } - // } - // } - static search({ obj, key, value }: SearchArgs): ObjectType | undefined { - if (Array.isArray(obj)) { - for (const item of obj) { - const result = Helper.search({ obj: item, key, value }) +export function populateComponentsInRoutes( + routes: RouteConfig[], + fallback: FunctionType, +) { + routes && + routes.forEach((route) => { + const hierarchy = route.hierarchy + if (typeof route.element === "string") { + const elementName = route.element + const Component: JSX.ElementType = lazy( + () => + import( + `../components/${hierarchy}/${elementName}/${elementName}.tsx` + ).catch(fallback), + // () => import(`../components/Component_404`) + ) + route.element = + if (route.children) { + populateComponentsInRoutes(route.children, fallback) + } + } + }) +} + +export function recrusiveFilter( + data: Array, + filter: string, + matchCase?: boolean, + searchKeys?: false | Array, +) { + if (!filter || !data || data.length === 0) { + return data + } + if (!searchKeys || searchKeys.length === 0) { + searchKeys = ["label", "name", "id", "value"] + } + const dataClone = JSON.parse(JSON.stringify(data)) + const filteredItems = dataClone.filter((obj: any) => { + return ( + searchKeys && + searchKeys.reduce((acc, key) => { + let isMatch = + obj[key] && + (matchCase + ? obj[key].indexOf(filter) > -1 + : obj[key] && + obj[key] + .toString() + .toLowerCase() + .indexOf(filter.toLowerCase()) > -1) + if (obj.children) { + obj.children = recrusiveFilter( + obj.children, + filter, + matchCase, + ) + isMatch = isMatch || obj.children.length > 0 + } + return acc || isMatch + }, false) + ) + }) + return filteredItems +} + +export function search({ obj, key, value }: SearchArgs): ObjectType | undefined { + if (Array.isArray(obj)) { + for (const item of obj) { + const result = search({ obj: item, key, value }) + if (result) return result + } + } else if (obj && typeof obj === "object" && !(obj instanceof Date)) { + if (key in obj && (value === undefined || obj[key] === value)) { + return obj + } + for (const item of Object.values(obj)) { + if (typeof item === "object" && !(item instanceof Date)) { + const result = search({ obj: item as ObjectType, key, value }) if (result) return result } - } else if (obj && typeof obj === "object" && !(obj instanceof Date)) { - if (key in obj && (value === undefined || obj[key] === value)) { - return obj - } - for (const item of Object.values(obj)) { - if (typeof item === "object" && !(item instanceof Date)) { - const result = Helper.search({ obj: item as ObjectType, key, value }) - if (result) return result - } - } } - return undefined } + return undefined +} - static filterTreeStructure( - args: SearchArgs, - ): boolean | Array | 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 +export function filterTreeStructure( + args: SearchArgs, +): boolean | Array | 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 = Helper.filterTreeStructure(args) - if (Array.isArray(match) && match.length > 0) { - ;(obj as unknown as Array>).splice( - index, - 1, - match, - ) - } - return match + const match = filterTreeStructure(args) + if (Array.isArray(match) && match.length > 0) { + ;(obj as unknown as Array>).splice( + index, + 1, + 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 = Helper.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 match } return false - } - } - - static generateSlices( - count: number, - sliceLength: number, - labelFormat: string, - ) { - let i = 0, - j = 0 - const slices: Array = [] - while (i < count) { - let addUp = sliceLength - if (i + sliceLength > count) { - addUp = count % sliceLength - } - const label = - labelFormat === "range" ? i + 1 + " - " + (i + addUp) : "" + (j + 1) - slices.push({ - label, - name: label.replace(/ /g, "").replace(/-/g, "to"), - sliceIndex: j, - startIndex: i, - endIndex: i + addUp - 1, - }) - i += sliceLength - j++ - } - return slices - } - - static aggregate( - data: any, - aggregator: string, - ): { [key: string]: Array } { - let aggregated: { [key: string]: Array } = {} - data.forEach((item: any) => { - const key = item[aggregator] - let aggregatedArray: Array | undefined = aggregated[key] - if (!aggregatedArray) { - aggregatedArray = [] - aggregated[key] = aggregatedArray - } - aggregatedArray.push(item) }) - return aggregated - } + } 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 - static generateCategories( - data: any, - categories: Array | undefined, - ): { [key: string]: { [key: string]: Array } } { - const groups: { [key: string]: { [key: string]: Array } } = {} - if (categories && data) { - categories.forEach((key) => { - let group: { [key: string]: Array } = Helper.aggregate( - data, - key, - ) - groups[key] = group + 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 groups - } - - static toCamelCase(str: string, separater: string, includeFirst: boolean) { - const strParts = str.split(separater || " ") - return strParts - .map((part, i) => - i === 0 && includeFirst - ? part.charAt(0).toUpperCase() + part.slice(1) - : part, - ) - .join("") - } - - static generateRandomId(length: number = 8) { - return Math.random() - .toString(36) - .substring(2, length + 2) - } - - static copyOrPrompt(text?: string, cb?: FunctionType) { - if (Helper.copyToClipboard(text)) { - cb && cb() - } else { - prompt("Clipboard (Select: ⌘+a > Copy: ⌘+c)", text) - } - } - - static copyToClipboard(text?: string) { - const isNotSafari = typeof (window as any).safari === "undefined" - if (isNotSafari) { - text && navigator.clipboard.writeText(text) - return true - } else { - return false - } - } - - static findComponentDescription( - componentName: string, - components: ComponentList, - ) { - let hierarchy - let selectedItem = components.ATOMS.components.find( - (item: ComponentDescription) => - (typeof item === "string" ? item : item.name) === componentName, - ) - if (!selectedItem) { - selectedItem = components.MOLECULES.components.find( - (item: ComponentDescription) => - (typeof item === "string" ? item : item.name) === componentName, - ) - if (selectedItem) { - hierarchy = "MOLECULES" - } - } else { - hierarchy = "ATOMS" - } - return { - selectedItem: JSON.parse( - JSON.stringify(selectedItem), - ) as ComponentDescription, - hierarchy, - component: componentName, - } - } - - static matchArrayFilters( - searchList: Array | undefined, - filters: Array | 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 - } - - static importComponent( - name: string, - hierarchy: string, - fallback: FunctionType, - ) { - return lazy( - () => - import(`../components/${hierarchy}/${name}/${name}.tsx`).catch( - fallback, - ), - // () => import(`../components/atoms/Component_404`) - ) - } - - static getStylesFromClass(className: string): Record { - const styleSheet = Array.from(document.styleSheets).find((sheet) => - Array.from(sheet.cssRules).some( - (rule) => - rule instanceof CSSStyleRule && rule.selectorText === `.${className}`, - ), - ) - - if (styleSheet) { - const styleRule = Array.from(styleSheet.cssRules).find( - (rule) => - rule instanceof CSSStyleRule && rule.selectorText === `.${className}`, - ) as CSSStyleRule - - if (styleRule) { - const styles: Record = {} - for (let i = 0; i < styleRule.style.length; i++) { - const prop = styleRule.style[i] - styles[prop] = styleRule.style.getPropertyValue(prop) - } - return styles - } - } - - return {} - } - - static debounce void>( - func: T, - wait?: number, - immediate: boolean = false, - ): (...args: Parameters) => void { - let timeout: NodeJS.Timeout - - return function (...args: Parameters): void { - const later = () => { - timeout = undefined! - - if (!immediate) { - func(...args) - } - } - - clearTimeout(timeout) - - if (immediate && !timeout) { - func(...args) - } - - timeout = setTimeout(later, wait || 1000) - } - } - - static isMobile(): boolean { - let check = false - ;(function (a: string) { - if ( - /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test( - a, - ) || - /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test( - a.substr(0, 4), - ) - ) - check = true - })( - (navigator.userAgent || - navigator.vendor || - ("opera" in window && window.opera)) as string, - ) - return check - } - - static download(blob: Blob, filename: string) { - const link = document.createElement("a") - link.href = window.URL.createObjectURL(blob) - link.download = filename - document.body.appendChild(link) - link.click() - document.body.removeChild(link) - } - - static pad(num: number, separator?: string) { - return ("" + num).padStart(2, "0") + (separator ? separator : "") - } - - static toReadable(str?: string) { - return str - ?.replace(/([a-z])([A-Z])/g, "$1 $2") - .split(" ") - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(" ") - } - - static generateFileKey(file: File | { name: string }) { - return "size" in file - ? `${file.name}_${file.size}_${file.type}_${file.lastModified}` - : file.name + "-preexisting-" + Date.now() - } - - static 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 - } - } - - static isImage(type: string) { - return !!type && validImageMimeTypes.indexOf(type) > -1 - } - - static 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) + return false } } -export default Helper +export function generateSlices( + count: number, + sliceLength: number, + labelFormat: string, +) { + let i = 0, + j = 0 + const slices: Array = [] + while (i < count) { + let addUp = sliceLength + if (i + sliceLength > count) { + addUp = count % sliceLength + } + const label = + labelFormat === "range" ? i + 1 + " - " + (i + addUp) : "" + (j + 1) + slices.push({ + label, + name: label.replace(/ /g, "").replace(/-/g, "to"), + sliceIndex: j, + startIndex: i, + endIndex: i + addUp - 1, + }) + i += sliceLength + j++ + } + return slices +} + +export function aggregate( + data: any, + aggregator: string, +): { [key: string]: Array } { + let aggregated: { [key: string]: Array } = {} + data.forEach((item: any) => { + const key = item[aggregator] + let aggregatedArray: Array | undefined = aggregated[key] + if (!aggregatedArray) { + aggregatedArray = [] + aggregated[key] = aggregatedArray + } + aggregatedArray.push(item) + }) + return aggregated +} + +export function generateCategories( + data: any, + categories: Array | undefined, +): { [key: string]: { [key: string]: Array } } { + const groups: { [key: string]: { [key: string]: Array } } = {} + if (categories && data) { + categories.forEach((key) => { + let group: { [key: string]: Array } = aggregate( + data, + key, + ) + groups[key] = group + }) + } + return groups +} + +export function toCamelCase(str: string, separater: string, includeFirst: boolean) { + const strParts = str.split(separater || " ") + return strParts + .map((part, i) => + i === 0 && includeFirst + ? part.charAt(0).toUpperCase() + part.slice(1) + : part, + ) + .join("") +} + +export function generateRandomId(length: number = 8) { + return Math.random() + .toString(36) + .substring(2, length + 2) +} + +export function copyOrPrompt(text?: string, cb?: FunctionType) { + if (copyToClipboard(text)) { + cb && cb() + } else { + prompt("Clipboard (Select: ⌘+a > Copy: ⌘+c)", text) + } +} + +export function copyToClipboard(text?: string) { + const isNotSafari = typeof (window as any).safari === "undefined" + if (isNotSafari) { + text && navigator.clipboard.writeText(text) + return true + } else { + return false + } +} + +export function findComponentDescription( + componentName: string, + components: ComponentList, +) { + let hierarchy + let selectedItem = components.ATOMS.components.find( + (item: ComponentDescription) => + (typeof item === "string" ? item : item.name) === componentName, + ) + if (!selectedItem) { + selectedItem = components.MOLECULES.components.find( + (item: ComponentDescription) => + (typeof item === "string" ? item : item.name) === componentName, + ) + if (selectedItem) { + hierarchy = "MOLECULES" + } + } else { + hierarchy = "ATOMS" + } + return { + selectedItem: JSON.parse( + JSON.stringify(selectedItem), + ) as ComponentDescription, + hierarchy, + component: componentName, + } +} + +export function matchArrayFilters( + searchList: Array | undefined, + filters: Array | 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 { + const styleSheet = Array.from(document.styleSheets).find((sheet) => + Array.from(sheet.cssRules).some( + (rule) => + rule instanceof CSSStyleRule && rule.selectorText === `.${className}`, + ), + ) + + if (styleSheet) { + const styleRule = Array.from(styleSheet.cssRules).find( + (rule) => + rule instanceof CSSStyleRule && rule.selectorText === `.${className}`, + ) as CSSStyleRule + + if (styleRule) { + const styles: Record = {} + for (let i = 0; i < styleRule.style.length; i++) { + const prop = styleRule.style[i] + styles[prop] = styleRule.style.getPropertyValue(prop) + } + return styles + } + } + + return {} +} + +export function debounce void>( + func: T, + wait?: number, + immediate: boolean = false, +): (...args: Parameters) => void { + let timeout: NodeJS.Timeout + + return function (...args: Parameters): void { + const later = () => { + timeout = undefined! + + if (!immediate) { + func(...args) + } + } + + clearTimeout(timeout) + + if (immediate && !timeout) { + func(...args) + } + + timeout = setTimeout(later, wait || 1000) + } +} + +export function isMobile(): boolean { + let check = false + ;(function (a: string) { + if ( + /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test( + a, + ) || + /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test( + a.substr(0, 4), + ) + ) + check = true + })( + (navigator.userAgent || + navigator.vendor || + ("opera" in window && window.opera)) as string, + ) + return check +} + +export function pad(num: number, separator?: string) { + return ("" + num).padStart(2, "0") + (separator ? separator : "") +} + +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) +} diff --git a/src/index.ts b/src/index.ts index 61e6a74..4b80c79 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,11 @@ -export { dateFormat as DateFormatter } from "./dateformat" -export { default as DomHelper } from "./domHelper" -export { default as Adapter } from "./adapters" -export { default as Helper } from "./helper" -export { default as Network } from "./network" -export { default as Validator } from "./validators" -export { default as GridHelper } from "./gridHelper" -export { default as RecursionHelper } from "./recursionHelper" +export * from "./dateformat" +export * from "./domHelper" +export * from "./adapters" +export * from "./helper" +export * from "./network" +export * from "./validators" +export * from "./gridHelper" +export * from "./recursionHelper" export * from "./dateHelper" export * from "./chartGenerators" export * from "./hooks" diff --git a/src/network.ts b/src/network.ts index dfb3679..92e2bde 100644 --- a/src/network.ts +++ b/src/network.ts @@ -1,5 +1,5 @@ import { FunctionType, ObjectType } from "@armco/types" -import { API_CONFIG } from "@armco/configs" +import { HOST, STATIC_HOST } from "@armco/configs/endpoints" interface Options extends RequestInit { // headers?: { @@ -36,30 +36,29 @@ class Error { } } -export default class Network { - static MAX_ATTEMPTS_LIMIT = 10 + const MAX_ATTEMPTS_LIMIT = 10 - static async get(url: string, queryParams?: object, options?: Options) { - const urlString = Network.stringifyUrl(url, queryParams) + export async function get(url: string, queryParams?: object, options?: Options) { + const urlString = stringifyUrl(url, queryParams) options = { method: "GET", mode: "cors", credentials: "include", ...options, } - return Network.crud(urlString, options) + return crud(urlString, options) } - static async getStatic( + export async function getStatic( url: string, queryParams?: object | null, options?: Options | null, ) { - let urlString = Network.stringifyUrl(url, queryParams) + let urlString = stringifyUrl(url, queryParams) const environment = process.env.NODE_ENV || "development" const host = urlString.startsWith("http") ? "" - : API_CONFIG.STATIC_HOST[environment as keyof object] + : STATIC_HOST[environment as keyof object] urlString = host ? host + urlString : urlString options = { method: "GET", @@ -67,10 +66,10 @@ export default class Network { credentials: "include", ...options, } - return Network.crud(urlString, options) + return crud(urlString, options) } - static async post( + export async function post( url: string, data: object, queryParams?: object, @@ -78,7 +77,7 @@ export default class Network { noStringify?: boolean, noHeaders?: boolean, ) { - const urlString = Network.stringifyUrl(url, queryParams) + const urlString = stringifyUrl(url, queryParams) options = { method: "POST", // @ts-ignore @@ -90,20 +89,20 @@ export default class Network { options && (options.headers = { "Content-Type": "application/json" }) // @ts-ignore - return Network.crud(urlString, options) + return crud(urlString, options) } - static async upload( + export async function upload( url: string, file: File, queryParams?: object, options?: Options, ) { - let urlString = Network.stringifyUrl(url, queryParams) + let urlString = stringifyUrl(url, queryParams) const environment = process.env.NODE_ENV || "development" const host = urlString.startsWith("http") ? "" - : API_CONFIG.STATIC_HOST[environment as keyof object] + : STATIC_HOST[environment as keyof object] urlString = host ? host + urlString : urlString const formData = new FormData() formData.append("file", file) @@ -114,11 +113,11 @@ export default class Network { credentials: "include", ...options, } - return Network.crud(urlString, options) + return crud(urlString, options) } - static async put(url: string, data: object, queryParams?: object) { - const urlString = Network.stringifyUrl(url, queryParams) + export async function put(url: string, data: object, queryParams?: object) { + const urlString = stringifyUrl(url, queryParams) const options: Options = { method: "PUT", body: JSON.stringify(data), @@ -128,23 +127,23 @@ export default class Network { "Content-Type": "application/json", }, } - return Network.crud(urlString, options) + return crud(urlString, options) } - static async delete(url: string, queryParams: object) { - const urlString = Network.stringifyUrl(url, queryParams) + export async function deleteRec(url: string, queryParams: object) { + const urlString = stringifyUrl(url, queryParams) const options = { method: "DELETE", noInject: true, } - return Network.crud(urlString, options) + return crud(urlString, options) } - static async crud(urlString: string, options: Options) { + export async function crud(urlString: string, options: Options) { const environment = process.env.NODE_ENV || "development" const host = urlString.startsWith("http") ? "" - : API_CONFIG.HOST[environment as keyof object] + : HOST[environment as keyof object] const extras = options?.extras urlString = host ? host + urlString : urlString if (!options.headers) { @@ -183,7 +182,7 @@ export default class Network { throw new Error(err.error || err.message, status, extras) } - static stringifyUrl(url: string, queryParams?: any) { + export function stringifyUrl(url: string, queryParams?: any) { if (!queryParams) { return url || "" } @@ -203,7 +202,7 @@ export default class Network { : "" } - static async retry( + export async function retry( fn: FunctionType, retryOptions: RetryOptions | undefined = {}, ) { @@ -214,7 +213,7 @@ export default class Network { onSuccess, onFailure, } = retryOptions - maxAttempts = Math.min(maxAttempts, Network.MAX_ATTEMPTS_LIMIT) + maxAttempts = Math.min(maxAttempts, MAX_ATTEMPTS_LIMIT) let attempts = 0 const attempt = async () => { @@ -244,4 +243,3 @@ export default class Network { return attempt() } -} diff --git a/src/providers.tsx b/src/providers.tsx index 3714569..3b0758c 100644 --- a/src/providers.tsx +++ b/src/providers.tsx @@ -8,7 +8,7 @@ import { User, } from "@armco/types" import { ArContext } from "./contexts" -import Helper from "./helper" +import { isMobile } from "./helper" export const ArProvider = ({ children }: { children: ReactNode }) => { const [theme, setTheme] = useState(ArThemes.DARK1) @@ -19,7 +19,7 @@ export const ArProvider = ({ children }: { children: ReactNode }) => { ) const [notification, notify] = useState(undefined) const [drawerState, setDrawerState] = useState({ - collapsed: Helper.isMobile(), + collapsed: isMobile(), }) const [leftPanelContent, setLeftPanelContent] = useState< PanelContent | undefined diff --git a/src/recursionHelper.tsx b/src/recursionHelper.tsx index dd185e7..f06e81c 100644 --- a/src/recursionHelper.tsx +++ b/src/recursionHelper.tsx @@ -1,66 +1,62 @@ import { v4 as uuid } from "uuid" import { RecursionArgs, ObjectType, RecusionConditionTypes } from "@armco/types" -class RecursionHelper { - static recur(args: RecursionArgs) { - const { condition, data, isBrute, preop, postop, iterateOn } = args - if (isBrute) { - if (Array.isArray(data)) { - data.forEach((item) => { - RecursionHelper.recur({ ...args, data: item }) - }) - } else if (typeof data === "object") { - const preopResult = preop && preop(data) - Object.values(data).forEach((item: any) => { - RecursionHelper.recur({ ...args, data: item }) - }) - return (postop && postop(data)) || preopResult - } - } else { - if (Array.isArray(data)) { - data.forEach((item) => { - RecursionHelper.recur({ ...args, data: item }) - }) - } else if ( - typeof data === "object" && - (!condition || RecursionHelper.evaluateCondition(data, condition)) - ) { - const preopResult = preop && preop(data) - if (iterateOn) { - if (data[iterateOn]) { - RecursionHelper.recur({ ...args, data: data[iterateOn] }) - } - } else { - Object.values(data).forEach((item) => { - RecursionHelper.recur({ ...args, data: item }) - }) +export function recur(args: RecursionArgs) { + const { condition, data, isBrute, preop, postop, iterateOn } = args + if (isBrute) { + if (Array.isArray(data)) { + data.forEach((item) => { + recur({ ...args, data: item }) + }) + } else if (typeof data === "object") { + const preopResult = preop && preop(data) + Object.values(data).forEach((item: any) => { + recur({ ...args, data: item }) + }) + return (postop && postop(data)) || preopResult + } + } else { + if (Array.isArray(data)) { + data.forEach((item) => { + recur({ ...args, data: item }) + }) + } else if ( + typeof data === "object" && + (!condition || evaluateCondition(data, condition)) + ) { + const preopResult = preop && preop(data) + if (iterateOn) { + if (data[iterateOn]) { + recur({ ...args, data: data[iterateOn] }) } - return (postop && postop(data)) || preopResult + } else { + Object.values(data).forEach((item) => { + recur({ ...args, data: item }) + }) } + return (postop && postop(data)) || preopResult } - return null - } - - static injectIds(args: RecursionArgs) { - args.postop = (item) => !item.id && (item.id = uuid()) - RecursionHelper.recur(args) - } - - static evaluateCondition(data: ObjectType, condition: ObjectType): boolean { - if (!data || !condition) { - return false - } - if (condition.type === RecusionConditionTypes.KEY_EXISTS) { - return !!(condition.key && (condition.key as string) in data) - } else if (condition.type === RecusionConditionTypes.KEY_VALUE) { - return !!( - condition.key && - condition.value && - data[condition.key as string] === condition.value - ) - } - return false } + return null } -export default RecursionHelper +export function injectIds(args: RecursionArgs) { + args.postop = (item) => !item.id && (item.id = uuid()) + recur(args) +} + +export function evaluateCondition(data: ObjectType, condition: ObjectType): boolean { + if (!data || !condition) { + return false + } + if (condition.type === RecusionConditionTypes.KEY_EXISTS) { + return !!(condition.key && (condition.key as string) in data) + } else if (condition.type === RecusionConditionTypes.KEY_VALUE) { + return !!( + condition.key && + condition.value && + data[condition.key as string] === condition.value + ) + } + return false +} diff --git a/src/validators.ts b/src/validators.ts index 560e574..7da11ca 100644 --- a/src/validators.ts +++ b/src/validators.ts @@ -1,17 +1,13 @@ import { ObjectType } from "@armco/types" -class Validator { - static validateTask(taskObj: ObjectType) { - return ( - taskObj && - taskObj.name && - taskObj.client && - taskObj.target && - (taskObj.target as ObjectType).endpoint && - taskObj.schedule && - (taskObj.schedule as ObjectType).cron - ) - } +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 + ) } - -export default Validator diff --git a/vite.config.ts b/vite.config.ts index 93bd73b..9622bba 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,10 +3,11 @@ import {glob} from "glob" import { defineConfig } from "vitest/config" import react from "@vitejs/plugin-react" import dts from "vite-plugin-dts" +import { externalizeDeps } from "vite-plugin-externalize-deps" // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react(), dts({ outDir: "build/types" })], + plugins: [react(), dts({ outDir: "build/types" }), externalizeDeps()], build: { outDir: "build", lib: { @@ -14,7 +15,6 @@ export default defineConfig({ }, rollupOptions: { treeshake: true, - external: ["react", "react/jsx-runtime", "react-dom"], output: [ { format: "es",