Added more components and analytics
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -29,4 +29,7 @@ dist
|
||||
|
||||
# Miscellaneous
|
||||
*.local
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
|
||||
analyse.html
|
||||
stats.html
|
||||
4
analyticsrc.json
Normal file
4
analyticsrc.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"apiKey": "lkdjlkfsd<:)(*Jk9klj",
|
||||
"trackEvents": ["click"]
|
||||
}
|
||||
8459
package-lock.json
generated
8459
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@import "../../static/styles/variables";
|
||||
|
||||
.ar-Content {
|
||||
background-color: var(--ar-bg-base);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from "react"
|
||||
import "./Content.component.scss"
|
||||
|
||||
interface ContentProps {
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@import "../../static/styles/variables";
|
||||
|
||||
.ar-Footer {
|
||||
background-color: var(--ar-bg-base);
|
||||
border-top: 1px solid #eee;
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import Calendar from "./Calendar"
|
||||
|
||||
export default Calendar
|
||||
export default Calendar
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import Checkbox from "./Checkbox"
|
||||
|
||||
export default Checkbox
|
||||
export default Checkbox
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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={() => {}}
|
||||
/>,
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,4 +20,8 @@
|
||||
height: 4.5rem;
|
||||
width: 5.5rem;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: var(--ar-bg-selected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
.ar-TabBar {
|
||||
|
||||
height: 3.1rem;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
3
src/app/components/molecules/Component_404/Component_404.component.scss
Executable file
3
src/app/components/molecules/Component_404/Component_404.component.scss
Executable file
@@ -0,0 +1,3 @@
|
||||
.ar-Component_404 {
|
||||
|
||||
}
|
||||
8
src/app/components/molecules/Component_404/Component_404.test.ts
Executable file
8
src/app/components/molecules/Component_404/Component_404.test.ts
Executable file
@@ -0,0 +1,8 @@
|
||||
import React from "react"
|
||||
import Component_404 from "./Component_404"
|
||||
|
||||
describe("Component_404", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
9
src/app/components/molecules/Component_404/Component_404.tsx
Executable file
9
src/app/components/molecules/Component_404/Component_404.tsx
Executable 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
|
||||
3
src/app/components/molecules/Component_404/index.ts
Executable file
3
src/app/components/molecules/Component_404/index.ts
Executable file
@@ -0,0 +1,3 @@
|
||||
import Component_404 from "./Component_404"
|
||||
|
||||
export default Component_404
|
||||
18
src/app/components/molecules/ErrorBoundary/ErrorBoundary.component.scss
Executable file
18
src/app/components/molecules/ErrorBoundary/ErrorBoundary.component.scss
Executable 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
src/app/components/molecules/ErrorBoundary/ErrorBoundary.test.ts
Executable file
8
src/app/components/molecules/ErrorBoundary/ErrorBoundary.test.ts
Executable file
@@ -0,0 +1,8 @@
|
||||
import React from "react"
|
||||
import ErrorBoundary from "./ErrorBoundary"
|
||||
|
||||
describe("ErrorBoundary", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
53
src/app/components/molecules/ErrorBoundary/ErrorBoundary.tsx
Executable file
53
src/app/components/molecules/ErrorBoundary/ErrorBoundary.tsx
Executable 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
|
||||
3
src/app/components/molecules/ErrorBoundary/index.ts
Executable file
3
src/app/components/molecules/ErrorBoundary/index.ts
Executable file
@@ -0,0 +1,3 @@
|
||||
import ErrorBoundary from "./ErrorBoundary"
|
||||
|
||||
export default ErrorBoundary
|
||||
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
25
src/app/pages/ComponentsViewerPage/ComponentsViewerPage.slice.d.ts
vendored
Normal file
25
src/app/pages/ComponentsViewerPage/ComponentsViewerPage.slice.d.ts
vendored
Normal 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
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
3
src/app/types/componentlist.d.ts
vendored
3
src/app/types/componentlist.d.ts
vendored
@@ -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 }>
|
||||
}
|
||||
|
||||
8
src/app/types/filterconfig.d.ts
vendored
8
src/app/types/filterconfig.d.ts
vendored
@@ -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>
|
||||
|
||||
33
src/app/utils/helper.d.ts
vendored
33
src/app/utils/helper.d.ts
vendored
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
10
src/react-app-env.d.ts
vendored
10
src/react-app-env.d.ts
vendored
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user