Added more components and analytics

This commit is contained in:
2023-07-22 23:03:10 +05:30
parent 3a108b7c34
commit bfc26ab695
53 changed files with 8489 additions and 1289 deletions

5
.gitignore vendored
View File

@@ -29,4 +29,7 @@ dist
# Miscellaneous
*.local
.DS_Store
.DS_Store
analyse.html
stats.html

4
analyticsrc.json Normal file
View File

@@ -0,0 +1,4 @@
{
"apiKey": "lkdjlkfsd<:)(*Jk9klj",
"trackEvents": ["click"]
}

8459
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -28,6 +28,7 @@
"build-storybook": "storybook build"
},
"dependencies": {
"@armco/analytics": "^0.0.10",
"@armco/armory-react-components": "^0.0.20",
"@popperjs/core": "^2.11.8",
"@reduxjs/toolkit": "^1.8.1",
@@ -67,6 +68,7 @@
"prop-types": "^15.8.1",
"react": ">=16",
"react-dom": "^18.2.0",
"rollup-plugin-visualizer": "^5.9.2",
"sass": "^1.63.4",
"storybook": "^7.0.23",
"typescript": "^5.0.2",

View File

@@ -14,7 +14,6 @@ class ErrorBoundary extends Component<Props, State> {
}
public static getDerivedStateFromError(_: Error): State {
// Update state so the next render will show the fallback UI.
return { hasError: true }
}

View File

@@ -1,14 +1,13 @@
import { useEffect } from "react"
import { Suspense, useEffect } from "react"
import { useRoutes } from "react-router-dom"
import { AlertProps } from "./components/atoms/Alert/Alert"
import { useAppSelector } from "./hooks"
import { notification } from "./store"
import { Alert } from "./components"
import * as pages from "./pages"
import Helper from "./utils/helper"
import ROUTES from "./routes"
Helper.populateComponentsInRoutes(ROUTES, pages)
Helper.populateComponentsInRoutes(ROUTES, "../pages/")
interface RouterProps {}
@@ -22,10 +21,10 @@ const Router = (props: RouterProps) => {
}, [])
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
{useRoutes(ROUTES)}
<Alert {...alertInfo} />
</div>
</Suspense>
)
}

View File

@@ -10,14 +10,11 @@ interface ComponentListProps {}
const ComponentList = (props: ComponentListProps): JSX.Element => {
const formattedTreeData = Adapter.adaptToTree(COMPONENTS)
const navigate = useNavigate()
const handleComponentSelect = (data: TreeListData) => {
const params = data?.data?.props
data?.data?.component &&
const handleComponentSelect = (treeNode: TreeListData) => {
const params = treeNode.data?.props
treeNode.data?.component &&
navigate(
Network.stringifyUrl(`/component/${data?.data?.component}`, params),
{
state: data,
},
Network.stringifyUrl(`/component/${treeNode.data?.component}`, params),
)
}
return (

View File

@@ -1,5 +1,3 @@
@import "../../static/styles/variables";
.ar-Content {
background-color: var(--ar-bg-base);
}

View File

@@ -1,4 +1,3 @@
import React from "react"
import "./Content.component.scss"
interface ContentProps {

View File

@@ -5,7 +5,10 @@
}
}
.background {
background-repeat: no-repeat;
background-size: 100%;
.ar-Editor__frame__main {
background-color: var(--ar-bg);
&.background {
background-repeat: no-repeat;
background-size: 100%;
}
}

View File

@@ -1,37 +1,71 @@
import { useEffect, useState } from "react"
import { useLocation } from "react-router-dom"
import { createPortal } from "react-dom"
import { useAppDispatch, useAppSelector } from "../../hooks"
import {
selectTreeItem,
selectedTreeItem,
} from "../../pages/ComponentsViewerPage/ComponentsViewerPage.slice"
import * as Armory from ".."
import { Alert, Toggle } from ".."
import Helper from "../../utils/helper"
import COMPONENTS from "../../config/components"
import * as images from "../../static/images"
import "./Editor.component.scss"
interface EditorProps {}
interface FrameContentDefinition {
componentName: string
component: JSX.ElementType
props?: {
[key: string]:
| string
| number
| boolean
| { [key: string]: any }
| undefined
}
data: { [key: string]: any }
hierarchy: string
}
interface FrameContentProps {
contentDefinition: FrameContentDefinition | null
notificationProps?: any
}
const Editor = (props: EditorProps): JSX.Element | string => {
const FrameContent = (
props: FrameContentProps,
): JSX.Element | string | null => {
const { contentDefinition, notificationProps } = props
const { component, props: componentProps, data } = contentDefinition || {}
const background: string = data?.test?.background
const SelectedComponent = component
return SelectedComponent ? (
<div
className={`ar-Editor__frame__main h-100 w-100 flex-center${
background ? " background" : ""
}`}
style={{
// @ts-ignore
backgroundImage: background ? `url(${images[background]})` : "",
}}
>
{data && data.type === "HOC" ? (
<SelectedComponent {...componentProps}>
<div
className="ar-Editor__frame__dummy-child"
style={{ height: "100vh", width: "100vw" }}
/>
</SelectedComponent>
) : (
<SelectedComponent {...componentProps} />
)}
{notificationProps && <Alert {...notificationProps} />}
</div>
) : (
"Select an item from the side panel to view here"
)
}
const Editor = (): JSX.Element | string => {
const [contentRef, setContentRef] = useState<HTMLIFrameElement | null>(null)
const [notificationProps, setNotificationProps] = useState<any>(null)
const selectedTreeItemObj = useAppSelector(selectedTreeItem)
const dispatch = useAppDispatch()
const selectedItemName =
selectedTreeItemObj?.data?.component || selectedTreeItemObj?.label
const selectedItemData = selectedTreeItemObj && selectedTreeItemObj.data
let SelectedComponent: JSX.ElementType | null | undefined =
selectedItemName && Armory[selectedItemName as keyof object]
let selectedItemProps = {}
if (!SelectedComponent && selectedItemData && selectedItemData.component) {
SelectedComponent =
selectedItemData && Armory[selectedItemData.component as keyof object]
}
selectedItemProps = selectedItemData && selectedItemData.props
const hierarchy = selectedItemData && selectedItemData.hierarchy
const [selectedComponentDefinition, setSelectedComponentDefinition] =
useState<FrameContentDefinition | null>(null)
const [theme, setTheme] = useState<string>("th-light-1")
const location = useLocation()
useEffect(() => {
@@ -41,123 +75,132 @@ const Editor = (props: EditorProps): JSX.Element | string => {
}, [])
useEffect(() => {
if (location && location.state) {
dispatch(selectTreeItem(location.state))
} else {
const locationPathArr = location.pathname.split("/")
const componentName = locationPathArr[locationPathArr.length - 1]
const { selectedItem } = Helper.findComponentDescription(
if (theme) {
const iframeHtml =
contentRef?.contentWindow?.document?.getElementsByTagName("html")[0]
iframeHtml && iframeHtml.setAttribute("ar-theme", theme)
}
}, [contentRef, theme])
useEffect(() => {
const locationPathArr = location.pathname.split("/")
const componentName = locationPathArr[locationPathArr.length - 1]
if (componentName) {
const { selectedItem, hierarchy } = Helper.findComponentDescription(
componentName,
COMPONENTS,
)
dispatch(
selectTreeItem({
data: selectedItem,
label: selectedItem ? selectedItem.name : "",
}),
if (selectedItem && hierarchy) {
let props: { [key: string]: string | number | boolean | undefined } = {
demo: true,
}
if (location.search) {
const params = location.search.substring(1)?.split("&")
if (params) {
params.forEach((prop) => {
const keyValue = prop.split("=")
const name = keyValue && keyValue[0]
let value = keyValue && keyValue[1]
value = value === "true" ? true : value
value = value === "false" ? false : value
if (prop && value && selectedItem) {
props[name] = value
}
})
}
}
if (
!selectedComponentDefinition ||
selectedComponentDefinition.componentName !== componentName ||
JSON.stringify(selectedComponentDefinition.props) !==
JSON.stringify(props)
) {
const SelectedComponent = Helper.importComponent(
componentName,
`../components/${hierarchy}/`,
)
setSelectedComponentDefinition({
componentName,
component: SelectedComponent,
props,
data: selectedItem,
hierarchy,
})
}
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location])
useEffect(() => {
if (contentRef && selectedComponentDefinition) {
Helper.injectComponentStyleInFrame(
selectedComponentDefinition.data as ComponentDescription,
selectedComponentDefinition.hierarchy,
contentRef,
)
}
}, [location, dispatch])
}, [contentRef, selectedComponentDefinition])
useEffect(() => {
if (contentRef) {
const bsLink =
contentRef?.contentWindow?.document.getElementsByTagName("link")
if (!bsLink || bsLink.length === 0) {
Helper.injectBootstrapInFrame(contentRef)
Helper.injectVariablesInFrame(contentRef)
Helper.injectGlobalInFrame(contentRef)
Helper.injectEditorStyleInFrame(contentRef)
Helper.injectComponentStyleInFrame(
{ name: "Alert" },
"ATOMS",
contentRef,
)
if (process.env.NODE_ENV === "production") {
Helper.injectLinkInFrame("/assets/index-40c9bed9.css", contentRef)
} else {
Helper.injectBootstrapInFrame(contentRef)
Helper.injectVariablesInFrame(contentRef)
Helper.injectGlobalInFrame(contentRef)
Helper.injectEditorStyleInFrame(contentRef)
Helper.injectComponentStyleInFrame(
{ name: "Alert" },
"ATOMS",
contentRef,
)
}
}
}
}, [contentRef])
useEffect(() => {
if (contentRef) {
const selectedItemName =
selectedTreeItemObj?.data?.component || selectedTreeItemObj?.label
const { selectedItem, hierarchy } = Helper.findComponentDescription(
selectedItemName,
COMPONENTS,
)
selectedItem &&
hierarchy &&
Helper.injectComponentStyleInFrame(selectedItem, hierarchy, contentRef)
}
}, [contentRef, hierarchy, selectedTreeItemObj])
// let selectedItemVariants
// selectedItem && (selectedItemVariants = selectedItem && selectedItem.variants)
// const currentVariants = selectedItemVariants && selectedItemVariants.map(variant => {
// })
let children = null
if (SelectedComponent) {
const frameBg: string = selectedItemData.test?.background
children = (
<div
className={`ar-Editor__main h-100 w-100 flex-center${
frameBg ? " background" : ""
}`}
style={{
// @ts-ignore
backgroundImage: frameBg ? `url(${images[frameBg]})` : "",
}}
>
{selectedItemData.type === "HOC" ? (
<SelectedComponent {...selectedItemProps} {...{ demo: true }}>
<div
className="ar-Editor__dummy-child"
style={{ height: "100vh", width: "100vw" }}
/>
</SelectedComponent>
) : (
<SelectedComponent {...selectedItemProps} {...{ demo: true }} />
)}
{notificationProps && <Armory.Alert {...notificationProps} />}
</div>
)
}
const mountNode = contentRef?.contentWindow?.document?.body
const iframeHtml =
contentRef?.contentWindow?.document?.getElementsByTagName("html")[0]
iframeHtml && iframeHtml.setAttribute("ar-theme", "th-light-1")
return (
<div className="ar-Editor w-100 h-100 d-flex flex-column">
<div className="ar-Editor__tools px-3 py-2">
<span className="float-end">
<Armory.Toggle
<Toggle
toggleOnName="Dark"
toggleOffName="Light"
onChange={(checked: boolean) => {
checked
? iframeHtml &&
iframeHtml.setAttribute("ar-theme", "th-light-1")
: iframeHtml && iframeHtml.setAttribute("ar-theme", "th-dark-1")
}}
onChange={(checked: boolean) =>
setTheme(checked ? "th-light-1" : "th-dark-1")
}
/>
</span>
</div>
{children ? (
<iframe
className="ar-Editor__frame w-100 h-100"
ref={setContentRef}
title="Armco form-componentComponent Viewer"
>
{mountNode && createPortal(children, mountNode)}
{/* <div className="ar-Editor__props-container d-inline-block h-100"></div> */}
</iframe>
) : (
"Select an item from the side panel to view here"
)}
<iframe
className="ar-Editor__frame w-100 h-100"
ref={setContentRef}
title="Armco form-componentComponent Viewer"
>
{mountNode &&
createPortal(
<FrameContent
contentDefinition={selectedComponentDefinition}
notificationProps={notificationProps}
/>,
mountNode,
)}
{/* <div className="ar-Editor__props-container d-inline-block h-100"></div> */}
</iframe>
</div>
)
}

View File

@@ -1,5 +1,3 @@
@import "../../static/styles/variables";
.ar-Footer {
background-color: var(--ar-bg-base);
border-top: 1px solid #eee;

View File

@@ -1,5 +1,6 @@
import Content from "../Content"
import Drawer from "../Drawer"
import { ErrorBoundary } from ".."
import "./Main.component.scss"
interface MainProps {
@@ -12,7 +13,9 @@ const Main = (props: MainProps): JSX.Element => {
return (
<main className="ar-Main d-flex flex-grow-1 w-100">
<Drawer classes="d-flex h-100">{drawerContent}</Drawer>
<Content classes="flex-center p-3">{mainContent}</Content>
<ErrorBoundary>
<Content classes="flex-center p-3">{mainContent}</Content>
</ErrorBoundary>
</main>
)
}

View File

@@ -31,8 +31,10 @@ const Alert = (props: AlertProps): JSX.Element => {
useEffect(() => {
if (externalShow || demo) {
setTimeout(() => show("show"), 0)
clearTimeout(timeoutRef)
timeoutRef = setTimeout(() => show(""), timeout || 5000)
if (!demo || timeout) {
clearTimeout(timeoutRef)
timeoutRef = setTimeout(() => show(""), timeout || 5000)
}
}
}, [position, type, timeout, externalShow, message, demo])

View File

@@ -1,3 +1,3 @@
import Calendar from "./Calendar"
export default Calendar
export default Calendar

View File

@@ -1,3 +1,3 @@
import Checkbox from "./Checkbox"
export default Checkbox
export default Checkbox

View File

@@ -3,7 +3,7 @@ import "./DateInput.component.scss"
interface DateInputProps {}
const DateInput = (props: any): JSX.Element => {
const DateInput = (props: DateInputProps): JSX.Element => {
return <div className="ar-DateInput">In Component DateInput</div>
}

View File

@@ -6,7 +6,7 @@ interface DropDownProps {
id?: string
label?: string
onSelectionChanged: Function
options: Array<Map<string, string | number>>
options: Array<{ [key: string]: string | number }>
}
const DropDown = (props: DropDownProps): JSX.Element => {
@@ -18,9 +18,7 @@ const DropDown = (props: DropDownProps): JSX.Element => {
onSelectionChanged(
{
value: e.target.value,
data: options.find(
(o) => (o.get("name") || o.get("label")) === e.target.value,
),
data: options.find((o) => (o.name || o.label) === e.target.value),
},
context,
)
@@ -38,9 +36,7 @@ const DropDown = (props: DropDownProps): JSX.Element => {
onChange={onLocalChange}
>
{options.map((option) => (
<option value={option.get("name") || option.get("label")}>
{option.get("label")}
</option>
<option value={option.name || option.label}>{option.label}</option>
))}
</select>
</div>

View File

@@ -1,7 +1,7 @@
import { Icon, SelectionPill } from "../.."
import "./Pagination.component.scss"
interface PaginationProps {
data: Array<Map<string, string | number>>
data: Array<{ [key: string]: string | number }>
maxPillsToShow?: number
}
@@ -17,18 +17,16 @@ const Pagination = (props: PaginationProps): JSX.Element => {
selectorBlockRenders.push(
<SelectionPill
classes="me-2 border-radius"
data={
new Map<string, string | number | JSX.Element>([
[
"label",
<Icon
src={`http://localhost:8080/icon/ai/AiOutlineLeft/grey/${navItemSize}`}
alt="<"
size={navItemSize}
/>,
],
])
}
data={{
label: (
<Icon
src="ai/AiOutlineLeft"
color="grey"
alt="<"
size={navItemSize}
/>
),
}}
onClick={() => {}}
/>,
)
@@ -52,18 +50,16 @@ const Pagination = (props: PaginationProps): JSX.Element => {
selectorBlockRenders.push(
<SelectionPill
classes="ms-2 border-radius"
data={
new Map<string, string | number | JSX.Element>([
[
"label",
<Icon
src={`http://localhost:8080/icon/ai/AiOutlineRight/grey/${navItemSize}`}
alt=">"
size={navItemSize}
/>,
],
])
}
data={{
label: (
<Icon
src="ai/AiOutlineRight"
color="grey"
alt=">"
size={navItemSize}
/>
),
}}
onClick={() => {}}
/>,
)

View File

@@ -1,3 +1,11 @@
.ar-Pill {
background-color: var(--ar-bg-invert-fade);
backdrop-filter: blur(20px);
color: var(--ar-color-font-invert);
border-radius: 0.8rem;
border: var(--ar-border);
.ar-Pill__delete-icon {
cursor: pointer;
}
}

View File

@@ -1,10 +1,31 @@
import React from "react"
import Icon from "../Icon"
import "./Pill.component.scss"
interface PillProps {}
export interface PillProps {
label: string | JSX.Element
data?: any
deletable?: boolean
onClick?: Function
onDelete?: Function
}
const Pill = (props: any): JSX.Element => {
return <div className="ar-Pill">In Component Pill</div>
const Pill = (props: PillProps): JSX.Element => {
const { label, deletable, data, onClick, onDelete } = props
return (
<span
className="ar-Pill position-relative px-2 py-1 inline-flex-center"
onClick={() => onClick && onClick(label, data)}
>
<span className="me-2">{label}</span>
{deletable && (
<Icon
src="io5/IoClose/white"
onClick={() => onDelete && onDelete(label, data)}
classes="ar-Pill__delete-icon"
/>
)}
</span>
)
}
export default Pill

View File

@@ -1,10 +1,43 @@
import React from "react"
import { useEffect, useState } from "react"
import Pill, { PillProps } from "../Pill/Pill"
import "./Pillbox.component.scss"
interface PillboxProps {}
interface PillboxProps {
classes?: string
data: Array<PillProps>
onChange: Function
}
const Pillbox = (props: any): JSX.Element => {
return <div className="ar-Pillbox">In Component Pillbox</div>
const Pillbox = (props: PillboxProps): JSX.Element => {
const { classes, data, onChange } = props
const [pills, setPills] = useState<Array<PillProps>>(data)
useEffect(() => {
setPills(data)
}, [data])
const onPillDeleted = (label: string, data: Array<PillProps>) => {
const deletedPillIndex = pills.findIndex(
(pill: PillProps) => pill.label === label,
)
const deletedPill = pills[deletedPillIndex]
pills.splice(deletedPillIndex, 1)
setPills([...pills])
onChange && onChange(deletedPill, "all")
}
return (
<div className={`ar-Pillbox${classes ? " " + classes : ""}`}>
{pills &&
pills.map((pillData) => (
<Pill
label={pillData.label}
data={pillData}
deletable={pillData.deletable}
onDelete={onPillDeleted}
/>
))}
</div>
)
}
export default Pillbox

View File

@@ -20,4 +20,8 @@
height: 4.5rem;
width: 5.5rem;
}
&.selected {
background-color: var(--ar-bg-selected);
}
}

View File

@@ -1,24 +1,30 @@
import { useState } from "react"
import "./SelectionPill.component.scss"
type PILLSIZES = "large" | "medium" | "small"
interface SelectionPillProps {
classes?: string
context?: string
data: Map<string, string | number | JSX.Element>
data: { [key: string]: string | number | JSX.Element }
onClick: Function
size?: string
size?: PILLSIZES
}
const SelectionPill = (props: SelectionPillProps): JSX.Element => {
const { classes, context, data, onClick, size } = props
const [selected, setSelected] = useState<boolean>()
return (
<span
className={`ar-SelectionPill d-inline-flex justify-content-center align-items-center border${
className={`ar-SelectionPill inline-flex-center border${
classes ? " " + classes : ""
}${size ? " " + size : ""}`}
onClick={() => onClick({ value: data.get("label") || "", data })}
}${size ? " " + size : ""}
${selected ? " selected" : ""}`}
onClick={() => {
setSelected(!selected)
onClick && onClick({ value: data.label || "", data })
}}
>
{data.get("label")}
{data?.label || ":)"}
</span>
)
}

View File

@@ -1,3 +1,44 @@
.ar-Tab {
width: 5rem;
height: 2rem;
line-height: 1;
border: var(--ar-border);
border-radius: 6px 6px 0 0;
padding: 0.4rem 1rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
&:hover {
background-color: var(--ar-bg-hover);
}
.active {
}
.compact {
}
.comfortable {
}
.modern {
}
.classic {
}
.ar-Tab__delete-icon {
right: 0.4rem;
top: 0.5rem;
&:hover {
background-color: var(--ar-bg-hover);
}
}
}

View File

@@ -1,10 +1,55 @@
import React from "react"
import Icon from "../Icon"
import "./Tab.component.scss"
interface TabProps {}
export interface TabProps {
label: string
data?: any
demo?: boolean
deletable?: boolean
id?: string
isActive?: boolean
onSelect?: Function
size?: string
variant?: string
}
const Tab = (props: any): JSX.Element => {
return <div className="ar-Tab">In Component Tab</div>
const dummyData = {
label: "Tab 1",
id: "23jhrfl2l3lkj2lk3jlkjlsd",
isActive: false,
}
const Tab = (props: TabProps): JSX.Element => {
const {
label,
data,
demo,
deletable,
id,
isActive,
onSelect,
size,
variant,
} = props
const useLabel = label ? label : demo ? dummyData.label : label
return (
<div
className={`ar-Tab position-relative${isActive ? " active" : ""}${
size ? " " + size : ""
}${variant ? " " + variant : ""}${deletable ? " pe-3" : ""}`}
id={id || useLabel}
onClick={() => onSelect && onSelect(id, data)}
>
{deletable && (
<Icon
containerClasses="ar-Tab__delete-icon position-absolute"
src="rx/RxCross2"
size="0.8rem"
/>
)}
{useLabel}
</div>
)
}
export default Tab

View File

@@ -1,3 +1,3 @@
.ar-TabBar {
height: 3.1rem;
}

View File

@@ -1,10 +1,71 @@
import React from "react"
import { useEffect, useState } from "react"
import { TabProps } from "../Tab/Tab"
import { Tab } from "../.."
import "./TabBar.component.scss"
interface TabBarProps {}
interface TabBarProps {
activeId?: string
data: TabProps[]
demo?: boolean
onTabChanged?: (id: string, data: any) => void
size?: string
variant?: string
}
const TabBar = (props: any): JSX.Element => {
return <div className="ar-TabBar">In Component TabBar</div>
const dummyData = [
{
label: "Tab 1",
id: "23jhrfl2l3lkj2lk3jlkjlsd",
isActive: false,
},
{
label: "Tab 2",
id: "oasdjfiahsdlfhaskdjfhlk",
isActive: true,
},
{
label: "Tab 3",
id: "kjahskjrh3hfaksdhf9dfhk23",
isActive: false,
},
]
const TabBar = (props: TabBarProps): JSX.Element => {
const { data, demo, onTabChanged, size, variant, activeId } = props
const [activeTab, setActiveTab] = useState<string>()
const useData = data || (demo && dummyData)
useEffect(() => {
if (activeId !== undefined) {
setActiveTab(activeId)
}
}, [activeId])
const onLocalTabChanged = (id: string, data: any) => {
if (activeId === undefined) {
setActiveTab(id)
}
onTabChanged && onTabChanged(id, data)
}
const tabRenders = useData
?.filter((t) => t)
.map((tab: TabProps) => {
return (
<Tab
label={tab.label}
id={tab.id}
isActive={activeTab === tab.id}
onSelect={onLocalTabChanged}
/>
)
})
return (
<div
className={`ar-TabBar d-flex${variant ? " " + variant : ""}${
size ? " " + size : ""
}`}
>
{tabRenders}
</div>
)
}
export default TabBar

View File

@@ -1,4 +1,6 @@
/* PLOP_INJECT_IMPORT */
import ErrorBoundary from "./molecules/ErrorBoundary"
import Component_404 from "./molecules/Component_404"
import AlertStackManager from "./molecules/AlertStackManager"
import SelectionPill from "./atoms/SelectionPill"
import CategoryFilter from "./molecules/CategoryFilter"
@@ -112,7 +114,9 @@ import Alert from "./atoms/Alert"
export {
/* PLOP_INJECT_EXPORT */
AlertStackManager,
ErrorBoundary,
Component_404,
AlertStackManager,
SelectionPill,
CategoryFilter,
AlphabetFilter,

View File

@@ -11,17 +11,15 @@ const AlphabetFilter = (props: AlphabetFilterProps): JSX.Element => {
const alphabet = alpha.map((x) => String.fromCharCode(x))
return (
<div className="ar-AlphabetFilter">
<label className="ar-AlphabetFilter__label label">
<div className="c-AlphabetFilter">
<label className="c-AlphabetFilter__label label">
Select icon starting with
</label>
<div className="ar-AlphabetFilter__pill-container">
<div className="c-AlphabetFilter__pill-container">
{alphabet.map((char) => (
<SelectionPill
classes="rounded me-2 mb-3"
data={
new Map<string, string | number | JSX.Element>([["label", char]])
}
data={{ label: char }}
size="small"
onClick={(data: BasicFilterConfig) =>
onSelectionChanged(data, "alphabet")

View File

@@ -3,30 +3,56 @@ import Helper from "../../../utils/helper"
import "./CategoryFilter.component.scss"
interface CategoryFilterProps {
categoryGroups: Map<string, Map<string, Array<string>>>
onCategorySelection: Function
categoryGroups: { [key: string]: { [key: string]: Array<string> } }
currentCategoryFilters?: { [key: string]: Array<BasicFilterConfig> }
onFilterChange: Function
}
const CategoryFilter = (props: CategoryFilterProps): JSX.Element => {
const { categoryGroups, onCategorySelection } = props
const { categoryGroups, currentCategoryFilters, onFilterChange } = props
const onCategorySelection = (
data: BasicFilterConfig,
categoryType: string,
) => {
let filters = currentCategoryFilters
if (!filters) {
filters = {}
}
let groupCategories = filters[
categoryType as keyof BasicFilterType
] as BasicFilterConfig[]
let selectedItemIndex = -1
if (!groupCategories) {
groupCategories = []
filters[categoryType as keyof BasicFilterType] = groupCategories
} else {
selectedItemIndex = groupCategories?.findIndex(
(category) => category.value === data.value,
)
}
if (selectedItemIndex === -1) {
groupCategories.push(data)
} else {
groupCategories.splice(selectedItemIndex, 1)
}
onFilterChange(filters, "categories")
}
const categoryRenders = (
categories: Map<string, Array<string>> | undefined,
categories: { [key: string]: Array<string> } | undefined,
group: string,
) => {
return (
categories && (
<div className="ar-CategoryFilter__pill-container">
{Array.from(categories.keys()).map((category) => (
{Object.keys(categories).map((category) => (
<SelectionPill
classes="rounded me-2 mb-3"
data={
new Map<string, string | number | JSX.Element>([
["label", category],
])
}
data={{ label: category }}
size="small"
onClick={(data: BasicFilterConfig) =>
onCategorySelection(data, category)
onCategorySelection(data, group)
}
/>
))}
@@ -34,20 +60,18 @@ const CategoryFilter = (props: CategoryFilterProps): JSX.Element => {
)
)
}
const groupRenders = Array.from(categoryGroups.keys()).map(
(group: string) => {
return (
<div className="ar-CategoryFilter__group">
<label className="ar-CategoryFilter__label label">
{Helper.toCamelCase(group, " ", true)}
</label>
<div className="ar-CategoryFilter__filters">
{categoryRenders(categoryGroups.get(group))}
</div>
const groupRenders = Object.keys(categoryGroups).map((group: string) => {
return (
<div className="ar-CategoryFilter__group">
<label className="ar-CategoryFilter__label label">
{Helper.toCamelCase(group, " ", true)}
</label>
<div className="ar-CategoryFilter__filters">
{categoryRenders(categoryGroups[group], group)}
</div>
)
},
)
</div>
)
})
return <div className="ar-CategoryFilter">{groupRenders}</div>
}

View File

@@ -0,0 +1,3 @@
.ar-Component_404 {
}

View File

@@ -0,0 +1,8 @@
import React from "react"
import Component_404 from "./Component_404"
describe("Component_404", () => {
it("renders without error", () => {
})
})

View File

@@ -0,0 +1,9 @@
import "./Component_404.component.scss"
interface Component_404Props {}
const Component_404 = (props: Component_404Props): JSX.Element => {
return <div className="ar-Component_404">In Component Component_404</div>
}
export default Component_404

View File

@@ -0,0 +1,3 @@
import Component_404 from "./Component_404"
export default Component_404

View File

@@ -0,0 +1,18 @@
.ar-ErrorBoundary {
.ar-ErrorBoundary__container {
border: var(--ar-border);
background-color: var(--ar-bg-base);
transition: box-shadow 0.3s;
font-size: 0.85rem;
&:hover {
box-shadow: 0px 4px 8px var(--ar-shadow);
}
.ar-ErrorBoundary__help-link {
border: var(--ar-border);
background-color: var(--ar-bg-hover);
border-radius: 3px;
font-size: 0.75rem;
cursor: pointer;
}
}
}

View File

@@ -0,0 +1,8 @@
import React from "react"
import ErrorBoundary from "./ErrorBoundary"
describe("ErrorBoundary", () => {
it("renders without error", () => {
})
})

View File

@@ -0,0 +1,53 @@
import { Component, ErrorInfo, ReactNode } from "react"
import { Link } from "react-router-dom"
import Icon from "../../atoms/Icon"
import "./ErrorBoundary.component.scss"
interface ErrorBoundaryProps {
children?: ReactNode
}
interface State {
hasError: boolean
}
class ErrorBoundary extends Component<ErrorBoundaryProps, State> {
public state: State = {
hasError: false,
}
public static getDerivedStateFromError(_: Error): State {
return { hasError: true }
}
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error("Uncaught error:", error, errorInfo)
}
public render() {
if (this.state.hasError) {
return (
<div className="ar-ErrorBoundary h-100 flex-center">
<div className="ar-ErrorBoundary__container text-center p-5">
<Icon
src="md/MdSmsFailed"
size="7rem"
color="#FFC300"
containerClasses="mb-3"
/>
<h5>Oops, seems we've hit a roadblock</h5>
<h6 className="mb-4">We're trying our best to get back soon!</h6>
<small>
If the issue persists please reach out to{" "}
<a href="/help">Armco's support</a>
</small>
</div>
</div>
)
}
return this.props.children
}
}
export default ErrorBoundary

View File

@@ -0,0 +1,3 @@
import ErrorBoundary from "./ErrorBoundary"
export default ErrorBoundary

View File

@@ -1,14 +1,16 @@
import { useState } from "react"
import { AlphabetFilter, CategoryFilter, DropDown } from "../.."
import { AlphabetFilter, CategoryFilter, DropDown, Pillbox } from "../.."
import { PillProps } from "../../atoms/Pill/Pill"
import Helper from "../../../utils/helper"
import "./Filters.component.scss"
const Filters = (props: FiltersProps): JSX.Element => {
const { config, data, initialFilters, onFilterChange } = props
const { config, data, filteredData, initialFilters, onFilterChange } = props
const [filters, setFilters] = useState<FilterState | undefined>(
initialFilters,
)
const total = data && data.length
const useData = filteredData || data
const total = useData && useData.length
const countOptions = Helper.generateSlices(
total || 0,
@@ -21,27 +23,106 @@ const Filters = (props: FiltersProps): JSX.Element => {
type: string,
): void => {
const filterClone = filters ? { ...filters } : {}
const existing = filterClone[type as keyof FilterState]
if (
existing &&
type !== "count" &&
data &&
type === "alphabet" &&
"value" in existing &&
"value" in data &&
existing.value === data.value
) {
filterClone[type as keyof FilterState] = undefined
} else {
filterClone[type as keyof FilterState] = data
switch (type) {
case "alphabet":
let alphabetFilters = filterClone[type as keyof FilterState]
if (!alphabetFilters) {
alphabetFilters = []
filterClone[type as keyof FilterState] = alphabetFilters
}
alphabetFilters = alphabetFilters as Array<BasicFilterConfig>
const existingIndex = alphabetFilters.findIndex(
(bfc) => bfc.value === (data as BasicFilterConfig)?.value,
)
existingIndex > -1
? alphabetFilters.splice(existingIndex, 1)
: alphabetFilters.push(data as BasicFilterConfig)
break
case "all":
const filterType = (data as BasicFilterConfig)?.data?.type
if (filterType) {
if (filterType === "alphabet") {
filterClone[filterType] = []
} else if (filterType === "categories") {
const catFilter:
| {
[key: string]: Array<BasicFilterConfig>
}
| undefined =
filters &&
(filters[filterType] as {
[key: string]: Array<BasicFilterConfig>
})
const subType: string | number | undefined = (
data as BasicFilterConfig
)?.data?.subType
if (catFilter && subType) {
catFilter[subType] = []
}
}
}
break
case "categories":
case "count":
default:
filterClone[type as keyof FilterState] = data
}
setFilters(filterClone)
onFilterChange && onFilterChange(filterClone)
}
const flatFilters: Array<PillProps> = []
filters &&
Object.keys(filters).forEach((key) => {
if (key === "count") {
flatFilters.push({
label: (
<>
count:{" "}
<strong>
{(filters[key] as BasicFilterConfig)?.value as string}
</strong>
</>
),
data: { filterData: filters[key], type: key },
})
}
if (
key === "alphabet" &&
filters[key] &&
(filters[key] as Array<BasicFilterConfig>).length > 0
) {
const alphaFilters = (filters[key] as Array<BasicFilterConfig>)
.map((bfc) => bfc.value)
.join(" OR ")
flatFilters.push({
label: `starts:${alphaFilters}`,
data: { filterData: filters[key], type: key },
deletable: true,
})
}
if (key === "categories") {
const categories: BasicFilterType | undefined = filters[key]
categories &&
Object.keys(categories).forEach((catKey) => {
const categoryValue: Array<BasicFilterConfig> =
categories[catKey as keyof BasicFilterType]
if (categoryValue && categoryValue.length > 0) {
const groupFilters = categoryValue
.map((bfc) => bfc.value)
.join(" OR ")
flatFilters.push({
label: `${catKey}:${groupFilters}`,
data: { filterData: categoryValue, type: key, subType: catKey },
deletable: true,
})
}
})
}
})
return (
<div className="ar-Filters p-3 h-100 overflow-auto">
<h4>Filters</h4>
<h5>Filters</h5>
{"count" in config && (
<DropDown
context="count"
@@ -53,18 +134,25 @@ const Filters = (props: FiltersProps): JSX.Element => {
}}
/>
)}
<Pillbox
classes="mb-3"
data={flatFilters}
onChange={onLocalFilterChange}
/>
{config.alphabet && (
<AlphabetFilter onSelectionChanged={onLocalFilterChange} />
)}
{"categories" in config && (
<CategoryFilter
categoryGroups={Helper.generateCategories(data, config.categories)}
onCategorySelection={(data: BasicFilterConfig, category: string) => {
const categories: BasicFilterType =
filters && filters.categories ? filters.categories : new Map()
"set" in categories && categories.set(category, data)
onLocalFilterChange(categories, "categories")
}}
onFilterChange={onLocalFilterChange}
currentCategoryFilters={
filters && filters.categories
? (filters.categories as {
[key: string]: Array<BasicFilterConfig>
})
: {}
}
/>
)}
</div>

View File

@@ -128,6 +128,7 @@ const COMPONENTS: ComponentList = {
{
name: "InlineMenu",
props: [],
test: { background: "Balloon" },
variants: {},
},
{
@@ -183,8 +184,10 @@ const COMPONENTS: ComponentList = {
},
{
name: "Pagination",
props: [],
variants: {},
props: ["data", "maxPillsToShow"],
variants: {
maxPillsToShow: [3, 5, 10],
},
},
{
name: "Picklist",
@@ -241,6 +244,13 @@ const COMPONENTS: ComponentList = {
props: [],
variants: {},
},
{
name: "SelectionPill",
props: ["classes", "context", "data", "onClick", "size"],
variants: {
size: ["small", "medium", "large"],
},
},
{
name: "Slider",
props: [],
@@ -253,13 +263,31 @@ const COMPONENTS: ComponentList = {
},
{
name: "Tab",
props: [],
variants: {},
props: [
"label",
"data",
"deletable",
"id",
"isActive",
"onSelect",
"size",
"variant",
],
variants: {
deletable: [true, false],
isActive: [true, false],
size: ["compact", "comfortable"],
variant: ["classic", "modern"],
},
},
{
name: "TabBar",
props: [],
variants: {},
props: ["activeId", "data", "onTabChanged", "size", "variant"],
variants: {
size: ["compact", "comfortable"],
variant: ["classic", "modern"],
},
uses: [{ name: "Tab", hierarchy: "ATOMS" }],
},
{
name: "Table",

View File

@@ -0,0 +1,25 @@
import { PayloadAction } from "@reduxjs/toolkit"
import { RootState } from "../../store"
export interface ComponentsViewerPageState {
selectedTreeItem?: TreeListData | null
}
export declare const componentsViewerPageSlice: import("@reduxjs/toolkit").Slice<
ComponentsViewerPageState,
{
selectTreeItem: (
state: import("immer/dist/internal").WritableDraft<ComponentsViewerPageState>,
action: PayloadAction<TreeListData>,
) => void
},
"componentsViewerPage"
>
export declare const selectTreeItem: import("@reduxjs/toolkit").ActionCreatorWithPayload<
TreeListData,
"componentsViewerPage/selectTreeItem"
>
export declare const selectedTreeItem: (
state: RootState,
) => TreeListData | null | undefined
declare const _default: import("redux").Reducer<ComponentsViewerPageState>
export default _default

View File

@@ -3,6 +3,7 @@ import Header from "../../components/Header"
import Footer from "../../components/Footer"
import ComponentList from "../../components/ComponentList"
import Editor from "../../components/Editor"
import { ErrorBoundary } from "../../components"
import "./ComponentsViewerPage.page.scss"
interface ComponentsViewerPageProps {}
@@ -13,7 +14,14 @@ const ComponentsViewerPage = (
return (
<div className="ar-ComponentsViewerPage vh-100 d-flex flex-column">
<Header />
<Main drawerContent={<ComponentList />} mainContent={<Editor />} />
<Main
drawerContent={<ComponentList />}
mainContent={
<ErrorBoundary>
<Editor />
</ErrorBoundary>
}
/>
<Footer />
</div>
)

View File

@@ -25,10 +25,7 @@ const IconsPage = (props: IconsPageProps): JSX.Element => {
const [filters, setFilters] = useState<FilterState | undefined>({
count: {
value: "1to1000",
data: new Map<string, number | string>([
["startIndex", 0],
["endIndex", 999],
]),
data: { startIndex: 0, endIndex: 999 },
},
})
@@ -48,36 +45,40 @@ const IconsPage = (props: IconsPageProps): JSX.Element => {
)
}
if (filters && finalFilteredIconList) {
const countFilter =
filters.count && "data" in filters.count && filters.count.data
if (countFilter) {
finalFilteredIconList = finalFilteredIconList.slice(
countFilter.get("startIndex") as number,
countFilter.get("endIndex") as number,
)
}
const alphabet =
filters.alphabet &&
"value" in filters.alphabet &&
filters.alphabet.value
if (alphabet) {
finalFilteredIconList = finalFilteredIconList.filter((icon) =>
icon.name.toLowerCase().startsWith(alphabet.toLowerCase()),
(filters.alphabet as Array<BasicFilterConfig>).length > 0 &&
(filters.alphabet as Array<BasicFilterConfig>).map(
(al: BasicFilterConfig) => al.value,
)
}
Helper.matchArrayFilters(
finalFilteredIconList,
alphabet,
"name",
"starts",
)
const categories = filters.categories && filters.categories
if (categories) {
Array.from(
(categories as Map<string, BasicFilterConfig>).keys(),
).forEach((category) => {
finalFilteredIconList = finalFilteredIconList?.filter((icon) => {
return icon.group === category
})
Object.keys(categories).forEach((category) => {
const categoryValue = categories[category as keyof BasicFilterType]
Helper.matchArrayFilters(
finalFilteredIconList,
categoryValue,
"group",
)
})
}
const countFilter =
filters.count && "data" in filters.count && filters.count.data
if (countFilter) {
finalFilteredIconList = finalFilteredIconList.slice(
"startIndex" in countFilter ? +countFilter.startIndex : 0,
"endIndex" in countFilter ? +countFilter.endIndex : 0,
)
}
}
setFilteredIcons(finalFilteredIconList)
}
@@ -91,6 +92,7 @@ const IconsPage = (props: IconsPageProps): JSX.Element => {
<Filters
config={{ count: 1000, alphabet: true, categories: ["group"] }}
data={icons}
filteredData={filteredIcons}
initialFilters={filters}
onFilterChange={setFilters}
/>

View File

@@ -1,4 +1,5 @@
@import "~bootstrap/scss/bootstrap.scss";
@use "./variables";
@use "bootstrap/scss/bootstrap.scss";
html {
font-size: 16px;
@@ -48,6 +49,12 @@ code {
align-items: center;
}
.inline-flex-center {
display: inline-flex;
justify-content: center;
align-items: center;
}
.flex-v-center {
display: flex;
align-items: center;

View File

@@ -29,6 +29,7 @@
--ar-bg-invert-fade: rgba(0, 0, 0, 0.6);
--ar-bg-secondary: #ebecfc;
--ar-bg-hover: #f5f8ff;
--ar-bg-hover-2: #e0e3ea;
--ar-bg-selected: #e3e5f7;
--ar-bg-mild: #fbfbff;
--ar-color-highlight: #0000f3;
@@ -54,6 +55,7 @@
--ar-bg-base: #030307;
--ar-bg: black;
--ar-bg-hover: #0a0700;
--ar-bg-hover-2: #1f1c15;
--ar-bg-selected: #0c0a02;
--ar-bg-mild: #040400;
--ar-color-highlight: #0000fa;

View File

@@ -2,7 +2,8 @@ interface ComponentDescription {
name: string
type?: string
props?: Array<any>
variants?: { [key: string]: Array<string | boolean> }
propValues?: { [key: string]: string }
variants?: { [key: string]: Array<string | boolean | number> }
test?: { [key: string]: any }
uses?: Array<{ [key: string]: string }>
}

View File

@@ -1,6 +1,6 @@
interface BasicFilterConfig {
value: string
data?: Map<string, string | number>
data?: { [key: string]: string | number }
}
interface FilterConfig {
@@ -11,6 +11,7 @@ interface FilterConfig {
interface FiltersProps {
data?: Array<any>
filteredData?: Array<any>
config: FilterConfig
initialFilters?: FilterState
onFilterChange: Function
@@ -22,4 +23,7 @@ interface FilterState {
categories?: BasicFilterType
}
type BasicFilterType = Map<string, BasicFilterConfig> | BasicFilterConfig
type BasicFilterType =
| { [key: string]: Array<BasicFilterConfig> }
| BasicFilterConfig
| Array<BasicFilterConfig>

View File

@@ -1,9 +1,28 @@
declare class Helper {
static populateComponentsInRoutes(routes: RouteConfig[], components: any): void;
static recrusiveFilter(data: Array<any>, filter: string, matchCase?: boolean, searchProps?: Array<string>): any;
static generateSlices(count: number, sliceLength: number, labelFormat: string): Map<string, string | number>[];
static aggregate(data: any, aggregator: string): Map<string, Array<string>>;
static generateCategories(data: any, categories: Array<string> | undefined): Map<string, Map<string, Array<string>>>;
static toCamelCase(str: string, separater: string, includeFirst: boolean): string;
static populateComponentsInRoutes(
routes: RouteConfig[],
components: any,
): void
static recrusiveFilter(
data: Array<any>,
filter: string,
matchCase?: boolean,
searchProps?: Array<string>,
): any
static generateSlices(
count: number,
sliceLength: number,
labelFormat: string,
): Array<{ [key: string]: string | number }>
static aggregate(data: any, aggregator: string): Map<string, Array<string>>
static generateCategories(
data: any,
categories: Array<string> | undefined,
): Map<string, Map<string, Array<string>>>
static toCamelCase(
str: string,
separater: string,
includeFirst: boolean,
): string
}
export default Helper;
export default Helper

View File

@@ -1,14 +1,20 @@
import * as sass from "sass"
import { lazy } from "react"
class Helper {
static populateComponentsInRoutes(routes: RouteConfig[], components: any) {
static populateComponentsInRoutes(routes: RouteConfig[], path: string) {
routes &&
routes.forEach((route) => {
const Component: JSX.ElementType =
components[route.element as keyof object]
route.element = <Component />
if (route.children) {
Helper.populateComponentsInRoutes(route.children, components)
if (typeof route.element === "string") {
const elementName = route.element
const Component: JSX.ElementType = lazy(() =>
import(`${path}${elementName}`).catch(
() => import(`../components/molecules/Component_404`),
),
)
route.element = <Component />
if (route.children) {
Helper.populateComponentsInRoutes(route.children, path)
}
}
})
}
@@ -65,29 +71,30 @@ class Helper {
}
const label =
labelFormat === "range" ? i + 1 + " - " + (i + addUp) : "" + (j + 1)
slices.push(
new Map<string, string | number>([
["label", label],
["name", label.replace(/ /g, "").replace(/-/g, "to")],
["sliceIndex", j],
["startIndex", i],
["endIndex", i + addUp - 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): Map<string, Array<string>> {
let aggregated: Map<string, Array<string>> = new Map()
static aggregate(
data: any,
aggregator: string,
): { [key: string]: Array<string> } {
let aggregated: { [key: string]: Array<string> } = {}
data.forEach((item: any) => {
const key = item[aggregator]
let aggregatedArray: Array<string> | undefined = aggregated.get(key)
let aggregatedArray: Array<string> | undefined = aggregated[key]
if (!aggregatedArray) {
aggregatedArray = []
aggregated.set(key, aggregatedArray)
aggregated[key] = aggregatedArray
}
aggregatedArray.push(item)
})
@@ -97,12 +104,15 @@ class Helper {
static generateCategories(
data: any,
categories: Array<string> | undefined,
): Map<string, Map<string, Array<string>>> {
const groups: Map<string, Map<string, Array<string>>> = new Map()
): { [key: string]: { [key: string]: Array<string> } } {
const groups: { [key: string]: { [key: string]: Array<string> } } = {}
if (categories && data) {
categories.forEach((key) => {
let group: Map<string, Array<string>> = Helper.aggregate(data, key)
groups.set(key, group)
let group: { [key: string]: Array<string> } = Helper.aggregate(
data,
key,
)
groups[key] = group
})
}
return groups
@@ -149,7 +159,43 @@ class Helper {
} else {
hierarchy = "ATOMS"
}
return { selectedItem, hierarchy, component: componentName }
return {
selectedItem: JSON.parse(JSON.stringify(selectedItem)),
hierarchy,
component: componentName,
}
}
static matchArrayFilters(
searchList: Array<any> | undefined,
filters: Array<any> | false | undefined,
match: string,
type?: string,
) {
if (filters && searchList) {
return type && type === "starts"
? searchList.filter(
(item) =>
filters.findIndex((al) =>
item[match].toLowerCase().startsWith(al.toLowerCase()),
) > -1,
)
: searchList.filter(
(item) =>
filters.findIndex(
(al) => item[match].toLowerCase() === al.toLowerCase(),
) > -1,
)
}
return searchList
}
static importComponent(name: string, path: string) {
return lazy(() =>
import(`${path}${name}`).catch(
() => import(`../components/molecules/Component_404`),
),
)
}
//// Style related helpers
@@ -214,23 +260,26 @@ class Helper {
component?: { [key: string]: string },
clipCallback?: any,
) {
fetch(scssPath)
.then((r) => r.text())
.then((text) => {
const { clipStart, clipEnd } = clipCallback
? clipCallback(text)
: () => ({})
text = text
.substring(
clipStart ||
text.indexOf("__vite__css = ") + '__vite__css = "'.length,
clipEnd || text.indexOf("__vite__updateStyle(") - 2,
)
.replace(/\\n/g, "")
.replace(/\\"/g, '"')
const css = sass.compileString(text).css
Helper.injectStyleInFrame(css, frame, component)
})
import("sass").then((module) => {
const { compileString } = module
fetch(scssPath)
.then((r) => r.text())
.then((text) => {
const { clipStart, clipEnd } = clipCallback
? clipCallback(text)
: () => ({})
text = text
.substring(
clipStart ||
text.indexOf("__vite__css = ") + '__vite__css = "'.length,
clipEnd || text.indexOf("__vite__updateStyle(") - 2,
)
.replace(/\\n/g, "")
.replace(/\\"/g, '"')
const css = compileString(text).css
Helper.injectStyleInFrame(css, frame, component)
})
})
}
static injectStyleInFrame(

View File

@@ -4,7 +4,8 @@ import { BrowserRouter } from "react-router-dom"
import { Provider } from "react-redux"
import { store } from "./app/store"
import Router from "./app/Router"
import ErrorBoundary from "./app/ErrorBoundary"
import { ErrorBoundary } from "./app/components"
import "@armco/analytics"
import "./app/static/styles/_global.scss"
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement)

View File

@@ -40,16 +40,16 @@ declare module "*.png" {
}
declare module "*.webp" {
const src: string
export default src
const src: string
export default src
}
declare module "*.svg" {
import * as React from "react"
export const ReactComponent: React.FunctionComponent<React.SVGProps<
SVGSVGElement
> & { title?: string }>
export const ReactComponent: React.FunctionComponent<
React.SVGProps<SVGSVGElement> & { title?: string }
>
const src: string
export default src

View File

@@ -1,10 +1,20 @@
import { defineConfig } from "vitest/config"
import react from "@vitejs/plugin-react"
import path from "path"
import { visualizer } from "rollup-plugin-visualizer"
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
plugins: [
react(),
visualizer({
template: "treemap", // or sunburst
open: true,
gzipSize: true,
brotliSize: true,
filename: "analyse.html", // will be saved in project's root
}),
],
server: {
open: true,
port: 3000,
@@ -16,7 +26,10 @@ export default defineConfig({
},
build: {
outDir: "build",
sourcemap: true,
// sourcemap: true,
rollupOptions: {
treeshake: true,
},
},
test: {
globals: true,