Removed boilerplate, partial consolidation of components here:
This commit is contained in:
@@ -1,23 +0,0 @@
|
||||
Features to port to new Armory:
|
||||
|
||||
- PWA
|
||||
- Dashboard
|
||||
- Compare Tool
|
||||
- Server with sockets (another repository)
|
||||
- Live preview of page being created
|
||||
- Project, Page, Component
|
||||
- React table
|
||||
- Drag n drop with preview of component being dragged
|
||||
- Rich Tooltips
|
||||
- Logger view showing user activity
|
||||
- Live code being generated for dropped component
|
||||
- Component properties viewer
|
||||
- Undo/redo functionality
|
||||
- Login/signup page
|
||||
- Landing page with login/signup prompt
|
||||
|
||||
New:
|
||||
|
||||
- Chat/conversations
|
||||
- Notifications
|
||||
- Modal
|
||||
@@ -1,4 +0,0 @@
|
||||
License only feaures:
|
||||
|
||||
- Document versions
|
||||
- Infinite number of API calls
|
||||
@@ -1,100 +0,0 @@
|
||||
import React, { useRef, useEffect, useState } from "react"
|
||||
import * as d3 from "d3"
|
||||
import { GDP_DATA as data } from "./__mocks__/gdpData"
|
||||
|
||||
interface GDPData {
|
||||
[year: string]: Array<[string, number]>
|
||||
}
|
||||
|
||||
interface BarChartProps {
|
||||
data: GDPData
|
||||
currentYear: string
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
const width = 1400
|
||||
const height = 400
|
||||
const BarChart: React.FC<BarChartProps> = () => {
|
||||
const ref = useRef(null)
|
||||
const [currentView, setCurrentView] = useState<number>(0)
|
||||
|
||||
useEffect(() => {
|
||||
if (!data[Object.keys(data)[currentView]]) return
|
||||
|
||||
const svg = d3.select(ref.current)
|
||||
const margin = { top: 20, right: 20, bottom: 30, left: 40 }
|
||||
const x = d3
|
||||
.scaleBand()
|
||||
.rangeRound([0, width - margin.left - margin.right])
|
||||
.padding(0.1)
|
||||
const y = d3
|
||||
.scaleLinear()
|
||||
.rangeRound([height - margin.top - margin.bottom, 0])
|
||||
|
||||
const currentData = data[Object.keys(data)[currentView]].map(
|
||||
([country, gdp]) => ({
|
||||
country,
|
||||
gdp,
|
||||
}),
|
||||
)
|
||||
|
||||
x.domain(currentData?.map((d) => d.country) as Array<string>)
|
||||
y.domain([0, d3.max(currentData, (d) => d.gdp) || 0])
|
||||
|
||||
const g = svg
|
||||
.append("g")
|
||||
.attr("transform", `translate(${margin.left},${margin.top})`)
|
||||
|
||||
g.append("g")
|
||||
.attr("class", "axis axis--x")
|
||||
.attr("transform", `translate(0,${height - margin.top - margin.bottom})`)
|
||||
.call(d3.axisBottom(x))
|
||||
|
||||
g.append("g")
|
||||
.attr("class", "axis axis--y")
|
||||
.call(d3.axisLeft(y).ticks(10, "%"))
|
||||
|
||||
g.selectAll(".bar")
|
||||
.data(currentData)
|
||||
.enter()
|
||||
.append("rect")
|
||||
.attr("class", "bar")
|
||||
.attr("x", (d) => x(d.country as string) || 0)
|
||||
.attr("y", (d) => y(d.gdp))
|
||||
.attr("width", x.bandwidth())
|
||||
.attr(
|
||||
"height",
|
||||
(d) => height - margin.top - margin.bottom - (y(d.gdp) || 0),
|
||||
)
|
||||
|
||||
return () => {
|
||||
svg.selectAll("*").remove()
|
||||
}
|
||||
}, [data, currentView, width, height])
|
||||
|
||||
useEffect(() => {
|
||||
let interval: NodeJS.Timeout
|
||||
const dataLength = Object.keys(data).length
|
||||
interval = setInterval(() => {
|
||||
setCurrentView((prevView) => {
|
||||
const nextView = (prevView + 1) % dataLength
|
||||
if (nextView < dataLength) {
|
||||
return nextView
|
||||
} else {
|
||||
clearInterval(interval)
|
||||
return prevView
|
||||
}
|
||||
})
|
||||
}, 1000)
|
||||
return () => clearInterval(interval)
|
||||
}, [data])
|
||||
|
||||
return (
|
||||
<div className="w-100 h-100">
|
||||
<svg ref={ref} width={width} height={height} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BarChart
|
||||
@@ -10,11 +10,9 @@
|
||||
|
||||
### Published at npm and publicly available
|
||||
|
||||
### PLACEHOLDER PACKAGE, IN DEV
|
||||
### !! [PLACEHOLDER PACKAGE][BETA] !!
|
||||
|
||||
### Version with implementations: 1.0.0
|
||||
|
||||
###### IN BETA
|
||||
|
||||
[npm-url]: https://www.npmjs.com/package/@armco/armory-react-components
|
||||
[github-license-url]: https://github.com/ReStruct-Corporate-Advantage/armory-react-components/blob/main/LICENSE
|
||||
[npm-url]: https://www.npmjs.com/package/@armco/components
|
||||
[github-license-url]: https://github.com/ReStruct-Corporate-Advantage/components/blob/main/LICENSE
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
Renderer workflow
|
||||
|
||||
- Drag n drop components/Click add on slot and add component
|
||||
- Generate config for the component
|
||||
- props
|
||||
- slot
|
||||
- Process the config -> Parent -> props and handlers -> children
|
||||
|
||||
|
||||
|
||||
Guidelines
|
||||
|
||||
- Try to keep the config as flat as possible with hierarchy information
|
||||
- Try minimizing entier tree render, only render what's changed
|
||||
- Entire tree renders when main configuration changes
|
||||
- This is majorly with form components, buttons that either need to save and update state or perform actions like API calls, or modify layout like show modals hide show drawer etc.
|
||||
- To manage this rely on redux selectors as much as possible so that state change is delegated to lowest possible levels
|
||||
- For eg. When a button to expand drawer is clicked and the drawer contains a form, entire form should not re-render
|
||||
- Config should be decentralized with multiple middle level controllers to isolate state changes between these middle level items. Examples include:
|
||||
- Any form
|
||||
- Drawer
|
||||
- Header
|
||||
- Footer
|
||||
- Main
|
||||
- Card
|
||||
- Widget
|
||||
- At form level too, cross element communication should prevent changes at parent form level, communicate via redux
|
||||
- Redux design becomes important here wherein it should be as flat as possible and might end up containing large number of fields - one for each field with qualifiers
|
||||
- Create dynamic reducers and selectors for low level form fields
|
||||
|
||||
|
||||
|
||||
Features needed in renderer
|
||||
|
||||
- Visualize - Just display content of components (good enough for a static website)
|
||||
- Perform actions:
|
||||
- Show Modal
|
||||
- Fetch Data
|
||||
- Subscribe to data
|
||||
- Publish (dispatch) actions9
|
||||
@@ -1,3 +0,0 @@
|
||||
Features/Landing Page CTA
|
||||
|
||||
Share a live URL with your team and users
|
||||
@@ -1,8 +0,0 @@
|
||||
Website builder
|
||||
|
||||
- Form - Name, category, optional description, target audience
|
||||
- Select page layout
|
||||
- Header, drawer, main, footer
|
||||
- Multi row
|
||||
- Custom - specify rows and columns hierarchy
|
||||
- Grid layout - specify height and width of grids (to be studied)
|
||||
14
index.html
14
index.html
@@ -1,14 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Armory React Repository</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root" style="overflow:hidden;"></div>
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
12
package.json
12
package.json
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@armco/armory-react-components",
|
||||
"name": "@armco/components",
|
||||
"description": "React Component Library for Armco's stack of products and services",
|
||||
"version": "0.0.47",
|
||||
"type": "module",
|
||||
@@ -11,23 +11,17 @@
|
||||
"build:publish": "./scripts/build.sh",
|
||||
"build:publish:compile": "tsc --p ./tsconfig-build.json && vite build --config vite-publish.config.ts",
|
||||
"generate": "plop",
|
||||
"atom": "plop atom",
|
||||
"molecule": "plop molecule",
|
||||
"component": "plop component",
|
||||
"page": "plop page",
|
||||
"preview": "vite preview",
|
||||
"test": "NODE_ENV=development vitest",
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint .",
|
||||
"type-check": "tsc",
|
||||
"publish:dry": "npm publish --dry-run",
|
||||
"publish:local": "./scripts/publish-local.sh",
|
||||
"publish:public": "./scripts/publish.sh",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build"
|
||||
"publish:public": "./scripts/publish.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@armco/analytics": "^0.2.8",
|
||||
"@armco/utils": "^0.0.0",
|
||||
"@lottiefiles/react-lottie-player": "^3.5.3",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@reduxjs/toolkit": "^1.8.1",
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Component, ErrorInfo, ReactNode } from "react"
|
||||
|
||||
interface Props {
|
||||
children?: ReactNode
|
||||
}
|
||||
|
||||
interface State {
|
||||
hasError: boolean
|
||||
}
|
||||
|
||||
class ErrorBoundary extends Component<Props, 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 <h1>Sorry.. there was an error</h1>
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary
|
||||
@@ -1,81 +0,0 @@
|
||||
import { Suspense, useEffect } from "react"
|
||||
import { useRoutes } from "react-router-dom"
|
||||
import { v4 as uuid } from "uuid"
|
||||
import { AlertProps, ArAlertType, ArThemes } from "@armco/types"
|
||||
import Header from "./components/Header"
|
||||
import { useAppDispatch, useAppSelector } from "./hooks"
|
||||
import {
|
||||
notification,
|
||||
notify,
|
||||
setLoggedIn,
|
||||
setRightPanelContent,
|
||||
setUser,
|
||||
} from "./store"
|
||||
import { Alert } from "./components"
|
||||
import Helper from "./utils/helper"
|
||||
import { Network } from "./utils"
|
||||
import ROUTES from "./routes"
|
||||
import { ENDPOINTS } from "./config/constants"
|
||||
import API_CONFIG from "./config/api-config"
|
||||
|
||||
Helper.populatePagesInRoutes(ROUTES)
|
||||
|
||||
interface RouterProps {}
|
||||
|
||||
const Router = (props: RouterProps) => {
|
||||
const alertInfo = useAppSelector<AlertProps | undefined>(notification)
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
useEffect(() => {
|
||||
document
|
||||
.getElementsByTagName("html")[0]
|
||||
.setAttribute("ar-theme", ArThemes.DARK1)
|
||||
Network.get(
|
||||
API_CONFIG.IAM[process.env.NODE_ENV] +
|
||||
ENDPOINTS.USERS.ROOT +
|
||||
ENDPOINTS.USERS.CHECK,
|
||||
)
|
||||
.then((response) => {
|
||||
if (response && response.status === 200) {
|
||||
dispatch(setLoggedIn(true))
|
||||
if (response.body) {
|
||||
dispatch(setUser(response.body))
|
||||
}
|
||||
} else {
|
||||
dispatch(setLoggedIn(false))
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.status === 403) {
|
||||
dispatch(setLoggedIn(false))
|
||||
}
|
||||
})
|
||||
window.onmessage = (e) => {
|
||||
if (e.data && e.data.event === "LOGGED_IN") {
|
||||
dispatch(setLoggedIn(true))
|
||||
dispatch(
|
||||
notify({
|
||||
show: true,
|
||||
message: "Logged in successfully!",
|
||||
type: ArAlertType.SUCCESS,
|
||||
uid: uuid(),
|
||||
}),
|
||||
)
|
||||
dispatch(setRightPanelContent({ name: "" }))
|
||||
dispatch(setUser(e.data.data.body.user))
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const matchedRouteElement = useRoutes(ROUTES)
|
||||
|
||||
return (
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<Header routeDetails={matchedRouteElement?.props.match} />
|
||||
{matchedRouteElement}
|
||||
{alertInfo && <Alert {...alertInfo} />}
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
export default Router
|
||||
@@ -1,3 +0,0 @@
|
||||
.ar-AssetSelector {
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import AssetSelector from "./AssetSelector"
|
||||
|
||||
describe("AssetSelector", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,18 +0,0 @@
|
||||
import { AssetSelectorProps, ArIconTileTypes } from "@armco/types"
|
||||
import { ArIconsViewer } from ".."
|
||||
import "./AssetSelector.component.scss"
|
||||
|
||||
const AssetSelector = (props: AssetSelectorProps): JSX.Element => {
|
||||
const { classes } = props
|
||||
return (
|
||||
<div className={`ar-AssetSelector${classes ? " " + classes : ""}`}>
|
||||
<ArIconsViewer
|
||||
view={{ name: ArIconTileTypes.COMPACT }}
|
||||
hasSearch
|
||||
isDraggable
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AssetSelector
|
||||
@@ -1,3 +0,0 @@
|
||||
import AssetSelector from "./AssetSelector"
|
||||
|
||||
export default AssetSelector
|
||||
@@ -1,3 +0,0 @@
|
||||
.ar-ComponentList {
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import ComponentList from "./ComponentList"
|
||||
|
||||
describe("ComponentList", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,60 +0,0 @@
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { ArThemes, ComponentRepository, TreeListData } from "@armco/types"
|
||||
import {
|
||||
Adapter,
|
||||
COMPONENTS,
|
||||
ErrorBoundary,
|
||||
getCurrentTheme,
|
||||
Network,
|
||||
TreeList,
|
||||
useAppSelector,
|
||||
} from ".."
|
||||
import * as atoms from "../atoms"
|
||||
import * as molecules from "../molecules"
|
||||
import "./ComponentList.component.scss"
|
||||
|
||||
const Components: ComponentRepository = { ...atoms, ...molecules }
|
||||
|
||||
interface ComponentListProps {}
|
||||
|
||||
const formattedTreeData = Adapter.adaptToTreeFromComponentConfig(COMPONENTS)
|
||||
|
||||
const ComponentList = (props: ComponentListProps): JSX.Element => {
|
||||
const navigate = useNavigate()
|
||||
const theme = useAppSelector<ArThemes>(getCurrentTheme)
|
||||
|
||||
const handleComponentSelect = (treeNode: TreeListData) => {
|
||||
const params = treeNode.data?.props
|
||||
treeNode.data?.component &&
|
||||
navigate(
|
||||
Network.stringifyUrl(`/components/${treeNode.data?.component}`, params),
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ar-ComponentList h-100 w-100 px-2">
|
||||
<TreeList
|
||||
onItemSelect={handleComponentSelect}
|
||||
data={formattedTreeData}
|
||||
title="Component List"
|
||||
firstExpanded={true}
|
||||
theme={theme}
|
||||
customTooltip={(item: TreeListData) => {
|
||||
const Component =
|
||||
typeof item.label === "string" && Components[item.label]
|
||||
return Component ? (
|
||||
<ErrorBoundary>
|
||||
<Component demo />
|
||||
</ErrorBoundary>
|
||||
) : (
|
||||
item.label
|
||||
)
|
||||
}}
|
||||
isDraggable
|
||||
showTooltip
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ComponentList
|
||||
@@ -1,3 +0,0 @@
|
||||
import ComponentList from "./ComponentList"
|
||||
|
||||
export default ComponentList
|
||||
@@ -1,3 +0,0 @@
|
||||
.ar-ConfigRowItem {
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import ConfigRowItem from "./ConfigRowItem"
|
||||
|
||||
describe("ConfigRowItem", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,97 +0,0 @@
|
||||
import { ChangeEvent, useState } from "react"
|
||||
import { ArButtonVariants, ConfigRowItemProps } from "@armco/types"
|
||||
import { Button, LoadableIcon, TextInput } from ".."
|
||||
import "./ConfigRowItem.component.scss"
|
||||
|
||||
const ConfigRowItem = (props: ConfigRowItemProps): JSX.Element => {
|
||||
const { config, disabled, isNew, onAdd, onUpdate, onDelete } = props
|
||||
const [key, setKey] = useState<string>(config?.key || "")
|
||||
const [value, setValue] = useState<string>(config?.value || "")
|
||||
const [edited, setEdited] = useState<boolean>()
|
||||
|
||||
const configIsSubmittable = (isNew || edited) && key && value
|
||||
return (
|
||||
<div className="ar-ConfigRowItem row">
|
||||
<div className="col-3">
|
||||
<TextInput
|
||||
value={key}
|
||||
isDisabled={disabled && !edited}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
setKey(e.target.value)
|
||||
}
|
||||
placeholder="Config key"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-4">
|
||||
<TextInput
|
||||
placeholder="Enter a value, text/json etc."
|
||||
isDisabled={disabled && !edited}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
setValue(e.target.value)
|
||||
}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
{isNew ? (
|
||||
<div className="col-1 flex-v-center">
|
||||
<Button
|
||||
content="Add"
|
||||
variant={ArButtonVariants.SUCCESS}
|
||||
preIcon="io/IoMdAdd"
|
||||
onClick={() => {
|
||||
configIsSubmittable && onAdd && onAdd(key, value)
|
||||
setKey("")
|
||||
setValue("")
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="col-2 flex-v-center">
|
||||
<span
|
||||
className={`me-3${
|
||||
configIsSubmittable ? " cursor-pointer" : " pe-none"
|
||||
}`}
|
||||
>
|
||||
<LoadableIcon
|
||||
icon="fa/FaCheck"
|
||||
color={configIsSubmittable ? "green" : "rgba(0, 128, 0, 0.3)"}
|
||||
onClick={() => {
|
||||
config?._id &&
|
||||
configIsSubmittable &&
|
||||
onUpdate &&
|
||||
onUpdate(config?._id, key, value)
|
||||
setKey("")
|
||||
setValue("")
|
||||
}}
|
||||
hoverShadow
|
||||
/>
|
||||
</span>
|
||||
<span className={`me-3${edited ? " pe-none" : " cursor-pointer"}`}>
|
||||
<LoadableIcon
|
||||
icon={edited ? "rx/RxCross2" : "md/MdModeEditOutline"}
|
||||
color={isNew || edited ? "rgba(165, 42, 42, 0.3)" : "brown"}
|
||||
onClick={() => (edited ? setEdited(false) : setEdited(true))}
|
||||
/>
|
||||
</span>
|
||||
<span className={`me-3 ${edited ? "pe-none" : "cursor-pointer"}`}>
|
||||
<LoadableIcon
|
||||
icon="md/MdAdd"
|
||||
width="1.3rem"
|
||||
color={!configIsSubmittable ? "green" : "rgba(0, 128, 0, 0.3)"}
|
||||
onClick={() => onAdd && onAdd(key, value)}
|
||||
/>
|
||||
</span>
|
||||
<span className={edited ? "pe-none" : "cursor-pointer"}>
|
||||
<LoadableIcon
|
||||
icon="ri/RiDeleteBin6Line"
|
||||
color={isNew || edited ? "rgba(128, 0, 0, 0.3)" : "red"}
|
||||
onClick={() => onDelete && onDelete(config?._id)}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConfigRowItem
|
||||
@@ -1,3 +0,0 @@
|
||||
import ConfigRowItem from "./ConfigRowItem"
|
||||
|
||||
export default ConfigRowItem
|
||||
@@ -1,3 +0,0 @@
|
||||
.ar-ConfigurationList {
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import ConfigurationList from "./ConfigurationList"
|
||||
|
||||
describe("ConfigurationList", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,28 +0,0 @@
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { ConfigurationListProps, TreeListData } from "@armco/types"
|
||||
import { Network, TreeList } from ".."
|
||||
import "./ConfigurationList.component.scss"
|
||||
|
||||
const ConfigurationList = (props: ConfigurationListProps): JSX.Element => {
|
||||
const { list } = props
|
||||
const navigate = useNavigate()
|
||||
const handleComponentSelect = (treeNode: TreeListData) => {
|
||||
const params = treeNode.data?.props
|
||||
treeNode.data?.component &&
|
||||
navigate(
|
||||
Network.stringifyUrl(`/components/${treeNode.data?.component}`, params),
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className="ar-ConfigurationList w-100 p-2">
|
||||
<TreeList
|
||||
onItemSelect={handleComponentSelect}
|
||||
data={list || []}
|
||||
title="Configurations"
|
||||
firstExpanded={true}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConfigurationList
|
||||
@@ -1,3 +0,0 @@
|
||||
import ConfigurationList from "./ConfigurationList"
|
||||
|
||||
export default ConfigurationList
|
||||
@@ -1,10 +0,0 @@
|
||||
.ar-ConfigurationLoginPrompt {
|
||||
width: 50vw;
|
||||
background-color: var(--ar-bg);
|
||||
border-radius: 0.25rem;
|
||||
|
||||
small {
|
||||
color: var(--ar-color-secondary);
|
||||
line-height: 1rem;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import ConfigurationLoginPrompt from "./ConfigurationLoginPrompt"
|
||||
|
||||
describe("ConfigurationLoginPrompt", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,48 +0,0 @@
|
||||
import { ArButtonVariants, ConfigurationLoginPromptProps } from "@armco/types"
|
||||
import { Button, LoadableIcon, setRightPanelContent, useAppDispatch } from ".."
|
||||
import "./ConfigurationLoginPrompt.component.scss"
|
||||
|
||||
const ConfigurationLoginPrompt = (
|
||||
props: ConfigurationLoginPromptProps,
|
||||
): JSX.Element => {
|
||||
const dispatch = useAppDispatch()
|
||||
return (
|
||||
<div className="ar-ConfigurationLoginPrompt p-3 border">
|
||||
<div className="flex-h-center flex-column">
|
||||
<h6 className="flex-v-center" style={{ color: "#ffbf00" }}>
|
||||
<LoadableIcon
|
||||
classes="me-2"
|
||||
icon="fa/FaExclamationTriangle"
|
||||
color="#ffbf00"
|
||||
/>
|
||||
Please login to continue
|
||||
</h6>
|
||||
<small>
|
||||
This is the configurations feature where you can create and save
|
||||
configurations as key-value pairs.
|
||||
</small>
|
||||
<small>
|
||||
These key-value pairs can then be accessed using an endpoint inside
|
||||
your application.
|
||||
</small>
|
||||
<small className="mb-3">
|
||||
<strong>
|
||||
In order to be able to save and secure these configurations, you
|
||||
need to create an account, if one doesn't exist already and login
|
||||
using the same. Please use the button below to continue.
|
||||
</strong>
|
||||
</small>
|
||||
</div>
|
||||
<Button
|
||||
content="Login"
|
||||
variant={ArButtonVariants.SUCCESS}
|
||||
postIcon="io5/IoArrowForwardCircle"
|
||||
onClick={() => {
|
||||
dispatch(setRightPanelContent({ name: "LoginProvider" }))
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConfigurationLoginPrompt
|
||||
@@ -1,3 +0,0 @@
|
||||
import ConfigurationLoginPrompt from "./ConfigurationLoginPrompt"
|
||||
|
||||
export default ConfigurationLoginPrompt
|
||||
@@ -1,3 +0,0 @@
|
||||
.ar-ConfigurationNoConfigPrompt {
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import ConfigurationNoConfigPrompt from "./ConfigurationNoConfigPrompt"
|
||||
|
||||
describe("ConfigurationNoConfigPrompt", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,102 +0,0 @@
|
||||
import { ReactNode } from "react"
|
||||
import {
|
||||
ArButtonVariants,
|
||||
ArPopoverSlots,
|
||||
ArPopoverTriggers,
|
||||
ConfigurationNoConfigPromptProps,
|
||||
} from "@armco/types"
|
||||
import {
|
||||
Button,
|
||||
LoadableIcon,
|
||||
NamespaceOrgForm,
|
||||
Popover,
|
||||
setModalState,
|
||||
useAppDispatch,
|
||||
} from ".."
|
||||
import "./ConfigurationNoConfigPrompt.component.scss"
|
||||
|
||||
const ConfigurationNoConfigPrompt = (
|
||||
props: ConfigurationNoConfigPromptProps,
|
||||
): ReactNode => {
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
return (
|
||||
<div className="ar-ConfigurationNoConfigPrompt flex-center flex-column border px-3 py-5 w-50">
|
||||
<LoadableIcon
|
||||
classes="mb-4"
|
||||
icon="gr/GrConfigure"
|
||||
size="7rem"
|
||||
color="lightgrey"
|
||||
/>
|
||||
<p className="ar-ConfigurationNoConfigPrompt__message mx-5">
|
||||
You don't have any configurations created yet, you can start by
|
||||
selecting one of the options below
|
||||
</p>
|
||||
<div className="ar-ConfigurationNoConfigPrompt__buttons d-flex">
|
||||
<Popover trigger={ArPopoverTriggers.HOVER}>
|
||||
<Button
|
||||
classes="me-3"
|
||||
content="+ Space"
|
||||
variant={ArButtonVariants.SUCCESS}
|
||||
slot={ArPopoverSlots.ANCHOR}
|
||||
onClick={() =>
|
||||
dispatch(
|
||||
setModalState({
|
||||
show: true,
|
||||
isSticky: true,
|
||||
content: <NamespaceOrgForm context="namespace" />,
|
||||
closeHandler: () => dispatch(setModalState({ show: false })),
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<div
|
||||
slot={ArPopoverSlots.POPOVER}
|
||||
className="text-wrap"
|
||||
style={{ maxWidth: "10rem" }}
|
||||
>
|
||||
<p>This is where you keep your configurations.</p>
|
||||
<p className="mb-3">
|
||||
Use namespaces in your project/s to avoid fetching all the
|
||||
configurations for multiple projects.
|
||||
</p>
|
||||
<b>Namespace is required to create a configuration</b>
|
||||
</div>
|
||||
</Popover>
|
||||
<Popover trigger={ArPopoverTriggers.HOVER}>
|
||||
<Button
|
||||
variant={ArButtonVariants.SUCCESS}
|
||||
content="+ Organization"
|
||||
slot={ArPopoverSlots.ANCHOR}
|
||||
onClick={() =>
|
||||
dispatch(
|
||||
setModalState({
|
||||
show: true,
|
||||
isSticky: true,
|
||||
content: <NamespaceOrgForm context="org" />,
|
||||
closeHandler: () => dispatch(setModalState({ show: false })),
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<div
|
||||
slot={ArPopoverSlots.POPOVER}
|
||||
className="text-wrap"
|
||||
style={{ maxWidth: "10rem" }}
|
||||
>
|
||||
<p>
|
||||
You may skip creating an organization as one will be created for
|
||||
you if you don't.
|
||||
</p>
|
||||
<p>
|
||||
Or you may choose to create one. This will allow you to control
|
||||
severl aspects of an organization at creation time itself
|
||||
</p>
|
||||
</div>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConfigurationNoConfigPrompt
|
||||
@@ -1,3 +0,0 @@
|
||||
import ConfigurationNoConfigPrompt from "./ConfigurationNoConfigPrompt"
|
||||
|
||||
export default ConfigurationNoConfigPrompt
|
||||
@@ -1,13 +0,0 @@
|
||||
.ar-ConfigurationViewer {
|
||||
background-color: var(--ar-bg);
|
||||
.ar-ConfigurationViewer__header {
|
||||
background-color: var(--ar-bg-base);
|
||||
border-bottom: 1px solid var(--ar-color-layout-border);
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import ConfigurationViewer from "./ConfigurationViewer"
|
||||
|
||||
describe("ConfigurationViewer", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,210 +0,0 @@
|
||||
import { v4 as uuid } from "uuid"
|
||||
import {
|
||||
ArAlertType,
|
||||
ArButtonVariants,
|
||||
ArPopoverPositions,
|
||||
ArPopoverSlots,
|
||||
ArSizes,
|
||||
ConfigurationViewerProps,
|
||||
ObjectType,
|
||||
} from "@armco/types"
|
||||
import {
|
||||
API_CONFIG,
|
||||
Breadcrumb,
|
||||
Button,
|
||||
ConfigRowItem,
|
||||
ENDPOINTS,
|
||||
InlineMenu,
|
||||
LoadableIcon,
|
||||
NamespaceInfoBox,
|
||||
NamespaceOrgForm,
|
||||
Network,
|
||||
notify,
|
||||
Popover,
|
||||
setModalState,
|
||||
useAppDispatch,
|
||||
} from ".."
|
||||
import "./ConfigurationViewer.component.scss"
|
||||
|
||||
const ConfigurationViewer = (props: ConfigurationViewerProps): JSX.Element => {
|
||||
const { namespace, fetchNamespaces } = props
|
||||
const configurations = namespace?.configs
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const addConfig = (key: string, value: string, _id?: string) => {
|
||||
const payload: ObjectType = {
|
||||
key,
|
||||
value,
|
||||
namespace: namespace._id,
|
||||
version: "v1",
|
||||
}
|
||||
_id && (payload._id = _id)
|
||||
Network.post(
|
||||
API_CONFIG.CONFIG[process.env.NODE_ENV] +
|
||||
ENDPOINTS.CONFIG.ROOT +
|
||||
ENDPOINTS.CONFIG.SAVE +
|
||||
namespace._id,
|
||||
payload,
|
||||
)
|
||||
.then((res) => {
|
||||
if (res.status === 200) {
|
||||
fetchNamespaces()
|
||||
dispatch(
|
||||
notify({
|
||||
show: true,
|
||||
message: "Added a new config successfully",
|
||||
type: ArAlertType.SUCCESS,
|
||||
uid: uuid(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
dispatch(
|
||||
notify({
|
||||
show: true,
|
||||
message: "Failed to add new config",
|
||||
type: ArAlertType.ERROR,
|
||||
uid: uuid(),
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const deleteConfig = (_id?: string) => {
|
||||
Network.get(
|
||||
API_CONFIG.CONFIG[process.env.NODE_ENV] +
|
||||
ENDPOINTS.CONFIG.ROOT +
|
||||
ENDPOINTS.CONFIG.DELETE +
|
||||
"/" +
|
||||
namespace._id +
|
||||
"/" +
|
||||
_id,
|
||||
)
|
||||
.then((res) => {
|
||||
if (res.status === 200) {
|
||||
fetchNamespaces()
|
||||
dispatch(
|
||||
notify({
|
||||
show: true,
|
||||
message: "Removed config successfully",
|
||||
type: ArAlertType.SUCCESS,
|
||||
uid: uuid(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
dispatch(
|
||||
notify({
|
||||
show: true,
|
||||
message: "Failed to delete config",
|
||||
type: ArAlertType.ERROR,
|
||||
uid: uuid(),
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
return (
|
||||
<div className="ar-ConfigurationViewer d-flex flex-column w-100 h-100">
|
||||
<div className="ar-ConfigurationViewer__header px-3 py-2 w-100">
|
||||
<Breadcrumb
|
||||
classes="d-inline-block"
|
||||
data={[{ label: namespace.name, route: `/config/${namespace.name}` }]}
|
||||
/>
|
||||
<Button
|
||||
classes="float-end"
|
||||
content="Edit"
|
||||
color="brown"
|
||||
preIcon="fa/FaRegEdit"
|
||||
variant={ArButtonVariants.LINK}
|
||||
size={ArSizes.SMALL}
|
||||
/>
|
||||
<Popover
|
||||
classes="float-end"
|
||||
// trigger={ArPopoverTriggers.HOVER}
|
||||
position={ArPopoverPositions.BOTTOMLEFT}
|
||||
version="v1"
|
||||
>
|
||||
<Button
|
||||
content="Add"
|
||||
color="green"
|
||||
preIcon="md/MdAddCircleOutline"
|
||||
variant={ArButtonVariants.LINK}
|
||||
size={ArSizes.SMALL}
|
||||
slot={ArPopoverSlots.ANCHOR}
|
||||
/>
|
||||
<InlineMenu
|
||||
slot={ArPopoverSlots.POPOVER}
|
||||
data={{
|
||||
Org: {},
|
||||
Namespace: {
|
||||
onClick: () =>
|
||||
dispatch(
|
||||
setModalState({
|
||||
show: true,
|
||||
isSticky: true,
|
||||
content: <NamespaceOrgForm context="namespace" />,
|
||||
closeHandler: () =>
|
||||
dispatch(setModalState({ show: false })),
|
||||
}),
|
||||
),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Popover>
|
||||
<LoadableIcon
|
||||
classes="cursor-pointer"
|
||||
icon="io/IoMdInformationCircle"
|
||||
color="#0d6efd"
|
||||
onClick={() =>
|
||||
dispatch(
|
||||
setModalState({
|
||||
show: true,
|
||||
isSticky: true,
|
||||
content: <NamespaceInfoBox namespace={namespace} />,
|
||||
closeHandler: () => dispatch(setModalState({ show: false })),
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="ar-ConfigurationViewer__content flex-1 d-flex px-3 py-2 w-100">
|
||||
<div className="d-flex flex-1 flex-column">
|
||||
<ConfigRowItem onAdd={addConfig} isNew />
|
||||
<div className="my-3 border" />
|
||||
{configurations && configurations.length > 0 ? (
|
||||
configurations.map((configuration) => {
|
||||
return (
|
||||
<ConfigRowItem
|
||||
onUpdate={(_id: string, key: string, value: string) =>
|
||||
addConfig(key, value, _id)
|
||||
}
|
||||
onDelete={deleteConfig}
|
||||
config={configuration}
|
||||
disabled
|
||||
/>
|
||||
)
|
||||
})
|
||||
) : (
|
||||
<div className="row flex-1">
|
||||
<div className="col flex-center fw-bold">
|
||||
Nothing here, start by adding configurations above
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="ar-ConfigurationViewer__footer px-3 py-2 w-100">
|
||||
<Button
|
||||
classes="float-end"
|
||||
content="Submit"
|
||||
variant={ArButtonVariants.SUCCESS}
|
||||
size={ArSizes.SMALL}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConfigurationViewer
|
||||
@@ -1,3 +0,0 @@
|
||||
import ConfigurationViewer from "./ConfigurationViewer"
|
||||
|
||||
export default ConfigurationViewer
|
||||
@@ -1,41 +0,0 @@
|
||||
.ar-Editor {
|
||||
border: 1px solid var(--ar-color-layout-border);
|
||||
.ar-Editor__tools {
|
||||
background-color: var(--ar-bg-base);
|
||||
border-bottom: 1px solid var(--ar-color-layout-border);
|
||||
}
|
||||
|
||||
.ar-Editor__prop-selector {
|
||||
min-height: 2.5rem;
|
||||
&::-webkit-scrollbar {
|
||||
height: 2px;
|
||||
background-color: #fefefe;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #cdcdcd;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #ababab;
|
||||
border-radius: 2px;
|
||||
width: 3rem;
|
||||
height: 10px;
|
||||
&:hover {
|
||||
background: #888;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#root.ar-Editor__frame__main {
|
||||
background-color: var(--ar-bg-tertiary);
|
||||
&.background {
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100%;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import Editor from "./Editor"
|
||||
|
||||
describe("Editor", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,359 +0,0 @@
|
||||
import { ReactNode, useEffect, useState } from "react"
|
||||
import { useLocation, useNavigate } from "react-router-dom"
|
||||
import { createPortal } from "react-dom"
|
||||
import {
|
||||
ArDropdownVariants,
|
||||
ArSizes,
|
||||
ArThemes,
|
||||
ComponentDescription,
|
||||
FrameContentDefinition,
|
||||
FrameContentProps,
|
||||
PRIMITIVES,
|
||||
} from "@armco/types"
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
COMPONENTS,
|
||||
Toggle,
|
||||
Helper,
|
||||
Network,
|
||||
Dropdown,
|
||||
} from ".."
|
||||
import StyleHelper from "./StyleHelper"
|
||||
import * as images from "../../static/images"
|
||||
import "./Editor.component.scss"
|
||||
|
||||
const children = {
|
||||
bg: (
|
||||
<div
|
||||
key="dummy-bg"
|
||||
className="ar-Editor__frame__dummy-child"
|
||||
style={{ height: "100vh", width: "100vw" }}
|
||||
/>
|
||||
),
|
||||
content: (
|
||||
<span
|
||||
key="dummy-content"
|
||||
slot="content"
|
||||
className="bg"
|
||||
style={{ height: "10rem", width: "10rem" }}
|
||||
>
|
||||
Popover Content
|
||||
</span>
|
||||
),
|
||||
popover: (
|
||||
<div
|
||||
key="dummy-popover"
|
||||
slot="popover"
|
||||
style={{ height: "10rem", width: "10rem" }}
|
||||
>
|
||||
Popover Content
|
||||
</div>
|
||||
),
|
||||
list: (
|
||||
<div
|
||||
key="dummy-list"
|
||||
className="d-grid"
|
||||
style={{
|
||||
gridTemplateColumns: "repeat(auto-fill, 5rem)",
|
||||
gap: "1rem",
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
{Array.from({ length: 40 }, (_, i) => (
|
||||
<div
|
||||
key={"dummy-list-item-" + i}
|
||||
className="bg border border-radius-l2"
|
||||
style={{ width: "5rem", height: "5rem" }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
anchor: <Button content="Click Me!" slot="anchor" />,
|
||||
}
|
||||
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
|
||||
let childRenders: Array<JSX.Element> = []
|
||||
if (data?.type === "HOC") {
|
||||
childRenders = data.children.map(
|
||||
(name: string) => children[name as keyof object],
|
||||
)
|
||||
}
|
||||
return SelectedComponent ? (
|
||||
<div
|
||||
className={`ar-Editor__frame__main h-100 w-100 overflow-auto p-5 flex-column${
|
||||
background ? " background" : ""
|
||||
}`}
|
||||
style={{
|
||||
// @ts-ignore
|
||||
backgroundImage: background ? `url(${images[background]})` : "",
|
||||
}}
|
||||
id="root"
|
||||
>
|
||||
{data && data.type === "HOC" ? (
|
||||
<SelectedComponent {...componentProps}>
|
||||
{childRenders}
|
||||
</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 [selectedComponentDefinition, setSelectedComponentDefinition] =
|
||||
useState<FrameContentDefinition | null>(null)
|
||||
// const [theme, setTheme] = useState<string>(ArThemes.LIGHT1)
|
||||
const [theme, setTheme] = useState<string>(ArThemes.DARK1)
|
||||
const [portal, setPortal] = useState<ReactNode>()
|
||||
const [component, setComponent] = useState<string>()
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
|
||||
useEffect(() => {
|
||||
window.onmessage = (e) => {
|
||||
"ar" in e.data && setNotificationProps(e.data?.ar)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (theme) {
|
||||
const iframeHtml =
|
||||
contentRef?.contentWindow?.document?.getElementsByTagName("html")[0]
|
||||
iframeHtml && iframeHtml.setAttribute("ar-theme", theme)
|
||||
const iframeBody = iframeHtml?.getElementsByTagName("body")[0]
|
||||
iframeBody && (iframeBody.style.backgroundColor = "var(--ar-bg)")
|
||||
}
|
||||
}, [contentRef, theme])
|
||||
|
||||
useEffect(() => {
|
||||
const locationPathArr = location.pathname
|
||||
.split("/")
|
||||
.filter((p: string) => p)
|
||||
const componentName =
|
||||
locationPathArr.length > 1 && locationPathArr[locationPathArr.length - 1]
|
||||
if (componentName) {
|
||||
setComponent(componentName)
|
||||
const { selectedItem, hierarchy } = Helper.findComponentDescription(
|
||||
componentName,
|
||||
COMPONENTS,
|
||||
)
|
||||
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: string | boolean = keyValue && keyValue[1]
|
||||
value = value === "true" ? true : decodeURIComponent(value)
|
||||
value =
|
||||
value === "false" ? false : decodeURIComponent(value as string)
|
||||
if (prop && value && selectedItem) {
|
||||
props[name] = value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
if (
|
||||
!selectedComponentDefinition ||
|
||||
selectedComponentDefinition.componentName !== componentName ||
|
||||
JSON.stringify(selectedComponentDefinition.props) !==
|
||||
JSON.stringify(props)
|
||||
) {
|
||||
const SelectedComponent = Helper.importComponent(
|
||||
componentName,
|
||||
hierarchy?.toLowerCase(),
|
||||
)
|
||||
setSelectedComponentDefinition({
|
||||
componentName,
|
||||
component: SelectedComponent,
|
||||
props,
|
||||
data: selectedItem,
|
||||
hierarchy,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [location])
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
contentRef &&
|
||||
selectedComponentDefinition &&
|
||||
process.env.NODE_ENV !== "production"
|
||||
) {
|
||||
StyleHelper.injectComponentStyleInFrame(
|
||||
selectedComponentDefinition.data as ComponentDescription,
|
||||
selectedComponentDefinition.hierarchy,
|
||||
contentRef,
|
||||
)
|
||||
}
|
||||
}, [contentRef, selectedComponentDefinition])
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedComponentDefinition) {
|
||||
const { demo, theme, ...selectedProps } =
|
||||
selectedComponentDefinition.props || {}
|
||||
const newUrl = Network.stringifyUrl("/", selectedProps).slice(1)
|
||||
selectedProps && newUrl !== location.search && navigate(newUrl)
|
||||
}
|
||||
}, [selectedComponentDefinition])
|
||||
|
||||
useEffect(() => {
|
||||
if (contentRef) {
|
||||
const bsLink =
|
||||
contentRef?.contentWindow?.document.getElementsByTagName("link")
|
||||
if (!bsLink || bsLink.length === 0) {
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
StyleHelper.injectLinkInFrame(
|
||||
["index", "ComponentsViewerPage"],
|
||||
contentRef,
|
||||
)
|
||||
} else {
|
||||
StyleHelper.injectBootstrapInFrame(contentRef)
|
||||
StyleHelper.injectVariablesInFrame(contentRef)
|
||||
StyleHelper.injectAnimationInFrame(contentRef)
|
||||
StyleHelper.injectGlobalInFrame(contentRef)
|
||||
StyleHelper.injectEditorStyleInFrame(contentRef)
|
||||
StyleHelper.injectComponentStyleInFrame(
|
||||
{ name: "Alert" },
|
||||
"ATOMS",
|
||||
contentRef,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [contentRef])
|
||||
|
||||
const mountNode = contentRef?.contentWindow?.document?.body
|
||||
|
||||
useEffect(() => {
|
||||
if (mountNode) {
|
||||
if (selectedComponentDefinition && selectedComponentDefinition.props) {
|
||||
selectedComponentDefinition.props.theme = theme
|
||||
}
|
||||
setPortal(
|
||||
createPortal(
|
||||
<FrameContent
|
||||
contentDefinition={selectedComponentDefinition}
|
||||
notificationProps={notificationProps}
|
||||
/>,
|
||||
mountNode,
|
||||
),
|
||||
)
|
||||
}
|
||||
}, [selectedComponentDefinition, mountNode, notificationProps, theme])
|
||||
|
||||
const currentItemVariants = selectedComponentDefinition?.data.variants || []
|
||||
|
||||
return (
|
||||
<div className="ar-Editor w-100 h-100 d-flex flex-column">
|
||||
<div className="ar-Editor__tools">
|
||||
<div className="row justify-content-end">
|
||||
<div className="col flex-v-center justify-content-end">
|
||||
<div className="ar-Editor__prop-selector flex-v-center border-right me-3 overflow-auto py-2 px-3">
|
||||
{Object.keys(currentItemVariants)
|
||||
.filter((prop: string) => prop !== "demo")
|
||||
.map((prop: any, index: number, arr) => {
|
||||
const propsValues = currentItemVariants[prop]
|
||||
const isBoolean =
|
||||
propsValues.length === 2 &&
|
||||
propsValues.indexOf(true) > -1 &&
|
||||
propsValues.indexOf(false) > -1
|
||||
return isBoolean ? (
|
||||
<Toggle
|
||||
key={"prop-toggle-" + index + "-" + prop}
|
||||
classes="me-3"
|
||||
label={Helper.toReadable(prop)}
|
||||
onChange={(isChecked: boolean) =>
|
||||
selectedComponentDefinition?.componentName &&
|
||||
setSelectedComponentDefinition({
|
||||
...selectedComponentDefinition,
|
||||
props: {
|
||||
...(selectedComponentDefinition.props || {}),
|
||||
[prop]: isChecked,
|
||||
},
|
||||
})
|
||||
}
|
||||
isOn={Boolean(
|
||||
selectedComponentDefinition?.props &&
|
||||
selectedComponentDefinition?.props[prop],
|
||||
)}
|
||||
size={ArSizes.SMALL}
|
||||
hideStatus
|
||||
/>
|
||||
) : (
|
||||
<Dropdown
|
||||
key={"prop-dropdown-" + index + "-" + prop}
|
||||
classes="me-2"
|
||||
contentMatchAnchorWidth={false}
|
||||
options={propsValues.map((propValue: PRIMITIVES) => ({
|
||||
name: propValue,
|
||||
isSelected:
|
||||
selectedComponentDefinition?.props &&
|
||||
propValue + "" ===
|
||||
selectedComponentDefinition?.props[prop],
|
||||
}))}
|
||||
onSelectionChanged={(obj) => {
|
||||
selectedComponentDefinition?.componentName &&
|
||||
setSelectedComponentDefinition({
|
||||
...selectedComponentDefinition,
|
||||
props: {
|
||||
...(selectedComponentDefinition.props || {}),
|
||||
[prop]: obj.value,
|
||||
},
|
||||
})
|
||||
}}
|
||||
variant={ArDropdownVariants.SELECTIONSASPILLS}
|
||||
placeholder={Helper.toReadable(prop)}
|
||||
version="v2"
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<span className="float-end pe-3">
|
||||
<Toggle
|
||||
isOn={theme === ArThemes.DARK1}
|
||||
toggleOnName="Dark"
|
||||
toggleOffName="Light"
|
||||
onChange={(checked: boolean) =>
|
||||
setTheme(checked ? ArThemes.DARK1 : ArThemes.LIGHT1)
|
||||
}
|
||||
value={true}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* <FrameContent
|
||||
contentDefinition={selectedComponentDefinition}
|
||||
notificationProps={notificationProps}
|
||||
/> */}
|
||||
<iframe
|
||||
className="ar-Editor__frame w-100 h-100"
|
||||
ref={setContentRef}
|
||||
title="Armco Component Viewer"
|
||||
>
|
||||
{portal}
|
||||
</iframe>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Editor
|
||||
@@ -1,140 +0,0 @@
|
||||
import { ComponentDescription } from "@armco/types"
|
||||
|
||||
//// Editor Frame Style related helpers
|
||||
class StyleHelper {
|
||||
static injectGlobalInFrame(frame: HTMLIFrameElement | null) {
|
||||
const clipCallback = (text: string) => {
|
||||
return {
|
||||
clipStart: text && text.indexOf("html {\\n font-size: 16px"),
|
||||
}
|
||||
}
|
||||
StyleHelper.injectScssInFrame(
|
||||
"/src/app/static/styles/_global.scss",
|
||||
frame,
|
||||
undefined,
|
||||
clipCallback,
|
||||
)
|
||||
}
|
||||
|
||||
static injectBootstrapInFrame(frame: HTMLIFrameElement | null) {
|
||||
StyleHelper.injectLinkInFrame(
|
||||
"/src/app/static/styles/bootstrap.min.css",
|
||||
frame,
|
||||
)
|
||||
}
|
||||
|
||||
static injectVariablesInFrame(frame: HTMLIFrameElement | null) {
|
||||
StyleHelper.injectScssInFrame(
|
||||
"/src/app/static/styles/_variables.scss",
|
||||
frame,
|
||||
)
|
||||
}
|
||||
|
||||
static injectAnimationInFrame(frame: HTMLIFrameElement | null) {
|
||||
StyleHelper.injectScssInFrame(
|
||||
"/src/app/static/styles/_animations.scss",
|
||||
frame,
|
||||
)
|
||||
}
|
||||
|
||||
static injectEditorStyleInFrame(frame: HTMLIFrameElement | null) {
|
||||
StyleHelper.injectScssInFrame(
|
||||
"/src/app/components/Editor/Editor.component.scss",
|
||||
frame,
|
||||
)
|
||||
}
|
||||
|
||||
static injectComponentStyleInFrame(
|
||||
selectedComponent: ComponentDescription,
|
||||
hierarchy: string,
|
||||
frame: HTMLIFrameElement | null,
|
||||
) {
|
||||
if (hierarchy && selectedComponent && selectedComponent.name) {
|
||||
const componentStyleElement =
|
||||
frame?.contentWindow?.document.querySelector("style.component-style")
|
||||
componentStyleElement && componentStyleElement.remove()
|
||||
const dependencyComponentsToInject = selectedComponent.uses
|
||||
? [
|
||||
{ name: selectedComponent.name, hierarchy },
|
||||
...selectedComponent.uses,
|
||||
]
|
||||
: [{ name: selectedComponent.name, hierarchy }]
|
||||
dependencyComponentsToInject.forEach(
|
||||
(component: { [key: string]: string }) => {
|
||||
const scssPath = `/src/app/components/${component.hierarchy.toLowerCase()}/${
|
||||
component.name
|
||||
}/${component.name}.component.scss`
|
||||
StyleHelper.injectScssInFrame(scssPath, frame, component)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
static injectScssInFrame(
|
||||
scssPath: string,
|
||||
frame: HTMLIFrameElement | null,
|
||||
component?: { [key: string]: string },
|
||||
clipCallback?: any,
|
||||
) {
|
||||
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
|
||||
StyleHelper.injectStyleInFrame(css, frame, component)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
static injectStyleInFrame(
|
||||
css: string,
|
||||
frame: HTMLIFrameElement | null,
|
||||
component?: { [key: string]: string },
|
||||
) {
|
||||
const style = document.createElement("style")
|
||||
if (component && component.name) {
|
||||
style.setAttribute("id", component.name)
|
||||
style.setAttribute("class", "component-style")
|
||||
}
|
||||
style.appendChild(document.createTextNode(css))
|
||||
frame?.contentWindow?.document?.head.appendChild(style)
|
||||
}
|
||||
|
||||
static injectLinkInFrame(
|
||||
link: string | Array<string>,
|
||||
frame: HTMLIFrameElement | null,
|
||||
) {
|
||||
if (Array.isArray(link)) {
|
||||
const links = document.querySelectorAll("link[rel='stylesheet']")
|
||||
link.forEach((item: string) => {
|
||||
Array.from(links).forEach((selectedLink) => {
|
||||
const cssUrl = selectedLink.getAttribute("href")
|
||||
if (cssUrl && cssUrl.startsWith(`/assets/${item}`)) {
|
||||
const clone = selectedLink.cloneNode(true)
|
||||
frame?.contentWindow?.document?.head.appendChild(clone)
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
const cssLink = document.createElement("link")
|
||||
cssLink.href = link
|
||||
cssLink.rel = "stylesheet"
|
||||
cssLink.type = "text/css"
|
||||
frame?.contentWindow?.document?.head.appendChild(cssLink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default StyleHelper
|
||||
@@ -1,3 +0,0 @@
|
||||
import Editor from "./Editor"
|
||||
|
||||
export default Editor
|
||||
@@ -1,28 +0,0 @@
|
||||
.ar-FavoritesList {
|
||||
max-width: 0;
|
||||
transition: max-width 0.3s;
|
||||
&.show {
|
||||
max-width: 10rem;
|
||||
}
|
||||
|
||||
.ar-FavoritesList__header {
|
||||
background-color: var(--ar-color-prominent);
|
||||
|
||||
&.loggedIn {
|
||||
background-color: var(--ar-color-success);
|
||||
}
|
||||
}
|
||||
|
||||
&.shrink {
|
||||
max-width: 4rem;
|
||||
}
|
||||
|
||||
.ar-FavoriteItem {
|
||||
width: 0;
|
||||
transition: width 0.3s linear;
|
||||
|
||||
&.show {
|
||||
width: 5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import FavoritesList from "./FavoritesList"
|
||||
|
||||
describe("FavoritesList", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,125 +0,0 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import {
|
||||
ArButtonVariants,
|
||||
ArPopoverPositions,
|
||||
ArPopoverTriggers,
|
||||
ArSizes,
|
||||
FavoritesItemProps,
|
||||
FavoritesListProps,
|
||||
IconTileProps,
|
||||
} from "@armco/types"
|
||||
import { getFavorites } from "../../pages/IconsPage/IconsPage.slice"
|
||||
import {
|
||||
Button,
|
||||
getLoggedIn,
|
||||
IconTile,
|
||||
LoadableIcon,
|
||||
Popover,
|
||||
setRightPanelContent,
|
||||
useAppDispatch,
|
||||
useAppSelector,
|
||||
} from ".."
|
||||
import "./FavoritesList.component.scss"
|
||||
|
||||
const FavoriteItem = (props: FavoritesItemProps): JSX.Element | null => {
|
||||
const { index, favorite } = props
|
||||
const [shown, show] = useState<boolean>()
|
||||
|
||||
useEffect(() => {
|
||||
show(true)
|
||||
return () => {
|
||||
show(false)
|
||||
setTimeout(() => {}, 300)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<span
|
||||
className={`ar-FavoriteItem d-flex flex-column${shown ? " show" : ""}`}
|
||||
>
|
||||
<IconTile key={index} icon={favorite.icon} hideFooter hideBorder />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
const FavoritesList = (props: FavoritesListProps): JSX.Element => {
|
||||
const favorites = useAppSelector(getFavorites)
|
||||
const isLoggedIn = useAppSelector<boolean | undefined>(getLoggedIn)
|
||||
const dispatch = useAppDispatch()
|
||||
const icons =
|
||||
favorites &&
|
||||
favorites.map((favorite: IconTileProps, index: number) => (
|
||||
<FavoriteItem index={index} favorite={favorite} />
|
||||
))
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`ar-FavoritesList h-100${icons?.length > 0 ? " show" : ""}${
|
||||
isLoggedIn ? " shrink" : ""
|
||||
}`}
|
||||
>
|
||||
<LoadableIcon icon="tb/TbLayoutSidebarRightCollapse" />
|
||||
{!isLoggedIn ? (
|
||||
<Popover
|
||||
trigger={ArPopoverTriggers.HOVER}
|
||||
position={ArPopoverPositions.LEFTBOTTOM}
|
||||
version="v1"
|
||||
>
|
||||
<span
|
||||
className="ar-FavoritesList__header p-2 border-bottom d-flex justify-content-center w-100"
|
||||
slot="anchor"
|
||||
>
|
||||
<LoadableIcon icon="io/IoIosWarning" color="white" />
|
||||
</span>
|
||||
<div slot="popover" className="p-2">
|
||||
<strong className="mb-2 d-inline-block">
|
||||
You're not logged in, all of your favorites will be lost when the
|
||||
session is over.
|
||||
</strong>
|
||||
<p className="mb-0">
|
||||
Please login to ensure your changes are saved!
|
||||
</p>
|
||||
</div>
|
||||
</Popover>
|
||||
) : (
|
||||
<Popover
|
||||
trigger={ArPopoverTriggers.HOVER}
|
||||
position={ArPopoverPositions.LEFT}
|
||||
>
|
||||
<span
|
||||
className="ar-FavoritesList__header loggedIn p-2 border-bottom d-flex justify-content-center w-100"
|
||||
slot="anchor"
|
||||
>
|
||||
<LoadableIcon icon="io/IoIosCheckmarkCircleOutline" color="white" />
|
||||
</span>
|
||||
<div slot="popover" className="p-2">
|
||||
<strong className="mb-2 d-inline-block">
|
||||
Your favorites are being synchronized with your account!
|
||||
</strong>
|
||||
</div>
|
||||
</Popover>
|
||||
)}
|
||||
<div
|
||||
className={`ar-FavoritesList__favorites flex-v-center flex-column mt-3${
|
||||
icons?.length > 0 ? " px-2" : ""
|
||||
}`}
|
||||
>
|
||||
{icons}
|
||||
</div>
|
||||
{!isLoggedIn && (
|
||||
<Button
|
||||
classes="mx-2"
|
||||
variant={ArButtonVariants.WARNING}
|
||||
size={ArSizes.SMALL}
|
||||
content="Login"
|
||||
preIcon="ci/CiWarning"
|
||||
onClick={() => {
|
||||
dispatch(setRightPanelContent({ name: "LoginProvider" }))
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FavoritesList
|
||||
@@ -1,3 +0,0 @@
|
||||
import FavoritesList from "./FavoritesList"
|
||||
|
||||
export default FavoritesList
|
||||
@@ -1,12 +0,0 @@
|
||||
.ar-FontsList {
|
||||
.ar-FontsList__header-search-upload {
|
||||
background-color: var(--ar-bg);
|
||||
color: var(--ar-colora);
|
||||
background-color: var(--ar-bg-base);
|
||||
}
|
||||
|
||||
.ar-FontsList__font-tile-container {
|
||||
background-color: var(--ar-bg);
|
||||
color: var(--ar-color);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import FontsList from "./FontsList"
|
||||
|
||||
describe("FontsList", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,154 +0,0 @@
|
||||
import { ChangeEvent, useEffect, useState } from "react"
|
||||
import {
|
||||
ArButtonVariants,
|
||||
ArLoaderTypes,
|
||||
ArSizes,
|
||||
ArrayType,
|
||||
FontsListProps,
|
||||
FunctionType,
|
||||
ObjectType,
|
||||
} from "@armco/types"
|
||||
import {
|
||||
API_CONFIG,
|
||||
Button,
|
||||
ENDPOINTS,
|
||||
FontTile,
|
||||
Helper,
|
||||
Loader,
|
||||
Network,
|
||||
Pagination,
|
||||
SearchField,
|
||||
Toggle,
|
||||
} from ".."
|
||||
import "./FontsList.component.scss"
|
||||
|
||||
const fetchFontsPage = (
|
||||
limit: number,
|
||||
from: number,
|
||||
filters: ObjectType,
|
||||
dataSetter: FunctionType,
|
||||
pageSetter: FunctionType,
|
||||
setLoading: FunctionType,
|
||||
) => {
|
||||
const pageApi =
|
||||
API_CONFIG.STATIC_HOST[process.env.NODE_ENV] +
|
||||
ENDPOINTS.STATIC.FONT.ROOT +
|
||||
ENDPOINTS.STATIC.FONT.PAGE
|
||||
const queryParams: ObjectType = { pageSize: limit, from, ...filters }
|
||||
Network.get(pageApi, queryParams)
|
||||
.then((response) => {
|
||||
if (response && response.status === 200) {
|
||||
if (response.body && dataSetter) {
|
||||
const content = JSON.parse(response.body)
|
||||
const styleElement = document.createElement("style")
|
||||
styleElement.type = "text/css"
|
||||
styleElement.appendChild(
|
||||
document.createTextNode(content.returnFontGroups),
|
||||
)
|
||||
document.head.appendChild(styleElement)
|
||||
|
||||
dataSetter(content.fontNames)
|
||||
pageSetter(content.fontNames.slice(0, 30))
|
||||
}
|
||||
}
|
||||
setLoading(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
const FontsList = (props: FontsListProps): JSX.Element => {
|
||||
const [searchText, setSearchText] = useState<string | undefined>()
|
||||
const [fonts, setFonts] = useState<ArrayType>()
|
||||
const [page, setPage] = useState<ArrayType>()
|
||||
const [loading, setLoading] = useState<boolean>()
|
||||
|
||||
// TODO: Fetch Fonts
|
||||
useEffect(() => {
|
||||
const filters: ObjectType = {}
|
||||
setLoading(true)
|
||||
fetchFontsPage(30, 0, filters, setFonts, setPage, setLoading)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const filters: ObjectType = {}
|
||||
if (searchText) {
|
||||
filters.search = searchText
|
||||
}
|
||||
setLoading(true)
|
||||
fetchFontsPage(200, 0, filters, setFonts, setPage, setLoading)
|
||||
}, [searchText])
|
||||
|
||||
return (
|
||||
<div className="ar-FontsList h-100 w-100">
|
||||
{!fonts && (
|
||||
<Loader label="Loading Fonts..." type={ArLoaderTypes.SHAPES} />
|
||||
)}
|
||||
<div className="ar-FontsList__header-search-upload py-2 px-3 mb-2 border">
|
||||
<div className="row">
|
||||
<span className="offset-4 col-4 d-none d-md-flex flex-v-center justify-content-end">
|
||||
<Button
|
||||
classes="h-100 float-end me-3"
|
||||
content="Upload"
|
||||
size={ArSizes.SMALL}
|
||||
variant={ArButtonVariants.SUCCESS}
|
||||
/>
|
||||
<Toggle
|
||||
onChange={() => {}}
|
||||
toggleOnName="Compact"
|
||||
toggleOffName="Compact"
|
||||
/>
|
||||
</span>
|
||||
<div className="col-4 offset-4 offset-md-0 flex-v-center">
|
||||
<SearchField
|
||||
classes="bg-white"
|
||||
placeholder="Search by name, style, description"
|
||||
onChange={Helper.debounce(
|
||||
(event: ChangeEvent<HTMLInputElement>) =>
|
||||
setSearchText(event.target.value),
|
||||
1000,
|
||||
)}
|
||||
items={
|
||||
[]
|
||||
// fonts
|
||||
// ? fonts.map(
|
||||
// (font): SearchItem => ({
|
||||
// label: font.name,
|
||||
// data: font,
|
||||
// }),
|
||||
// )
|
||||
// : []
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{page && (
|
||||
<div className="ar-FontsList__font-tile-container py-2 px-3 border d-flex justify-content-between flex-wrap flex-grow-1">
|
||||
{loading && (
|
||||
<Loader label="Applying filters..." type={ArLoaderTypes.CIRCLE} />
|
||||
)}
|
||||
{(page as Array<string>).map((font) => (
|
||||
<FontTile font={font} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<Pagination
|
||||
classes="my-3 flex-center"
|
||||
data={fonts}
|
||||
maxPillsToShow={5}
|
||||
pageSetter={setPage}
|
||||
// trigger={ArPageTriggers.SCROLL}
|
||||
count={1}
|
||||
load={100}
|
||||
// dataFetcher={(load, count) =>
|
||||
// fetchIconsPage(load, count, {}, setIcons, setPage, setLoading)
|
||||
// }
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FontsList
|
||||
@@ -1,3 +0,0 @@
|
||||
import FontsList from "./FontsList"
|
||||
|
||||
export default FontsList
|
||||
@@ -1,39 +0,0 @@
|
||||
.ar-Header {
|
||||
background-color: var(--ar-bg-base);
|
||||
border-bottom: 1px solid var(--ar-color-layout-border);
|
||||
|
||||
.ar-Header__app-logo {
|
||||
cursor: pointer;
|
||||
.ar-Header__brand {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.ar-Header__app-name {
|
||||
font-family: "Allerta Stencil";
|
||||
font-weight: 700;
|
||||
font-size: 1.5rem;
|
||||
|
||||
.ar-Header__app-name-i {
|
||||
color: red;
|
||||
}
|
||||
.ar-Header__app-name-o {
|
||||
color: green;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ar-Header__app-search {
|
||||
transition: width 0.3s;
|
||||
@media screen and (max-width: 576px) {
|
||||
flex: 1;
|
||||
|
||||
.ar-SearchField .ar-TextInput {
|
||||
border-top-left-radius: 0.5rem;
|
||||
border-bottom-left-radius: 0.5rem;
|
||||
}
|
||||
.ar-SearchField .ar-Button {
|
||||
border-top-right-radius: 0.5rem;
|
||||
border-bottom-right-radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import Header from "./Header"
|
||||
|
||||
describe("Header", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,104 +0,0 @@
|
||||
import { useState } from "react"
|
||||
import { useLocation, useNavigate } from "react-router-dom"
|
||||
import { ArThemes, DrawerProps, HeaderProps, ObjectType } from "@armco/types"
|
||||
import {
|
||||
getCurrentTheme,
|
||||
getDrawerState,
|
||||
FlexTools,
|
||||
LoadableIcon,
|
||||
SearchField,
|
||||
setDrawerState,
|
||||
setTheme,
|
||||
useAppDispatch,
|
||||
useAppSelector,
|
||||
} from ".."
|
||||
import "./Header.component.scss"
|
||||
|
||||
const Header = (props: HeaderProps): JSX.Element => {
|
||||
const { routeDetails } = props
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const [searchFocussed, setSearchFocussed] = useState<boolean>()
|
||||
const drawerState = useAppSelector<DrawerProps | undefined>(getDrawerState)
|
||||
const dispatch = useAppDispatch()
|
||||
const theme = useAppSelector<string>(getCurrentTheme)
|
||||
const route = (routeDetails?.route as ObjectType)?.class
|
||||
const isLanding =
|
||||
!routeDetails ||
|
||||
["landing", "assets", "playground", "tools-and-services"].indexOf(
|
||||
route as string,
|
||||
) > -1
|
||||
|
||||
const showExpander =
|
||||
location.pathname.startsWith("/components") ||
|
||||
location.pathname.startsWith("/icons")
|
||||
|
||||
return (
|
||||
<header className="ar-Header w-100 flex-v-center px-2">
|
||||
<div
|
||||
className="ar-Header__app-logo h-100 flex-v-center me-3 d-none d-sm-flex"
|
||||
onClick={() => navigate("/")}
|
||||
>
|
||||
<span className="ar-Header__app-name h-100 flex-center">
|
||||
Stuffle.
|
||||
<span className="ar-Header__app-name-i">i</span>
|
||||
<span className="ar-Header__app-name-o">o</span>
|
||||
</span>
|
||||
</div>
|
||||
{showExpander && (
|
||||
<LoadableIcon
|
||||
classes="ar-Drawer__expander d-inline d-sm-none me-3"
|
||||
icon={
|
||||
drawerState?.collapsed
|
||||
? "tb/TbLayoutSidebarLeftExpand"
|
||||
: "tb/TbLayoutSidebarLeftCollapse"
|
||||
}
|
||||
size="2rem"
|
||||
onClick={() =>
|
||||
dispatch(setDrawerState({ collapsed: !drawerState?.collapsed }))
|
||||
}
|
||||
color={theme === ArThemes.DARK1 ? "lightgrey" : "black"}
|
||||
/>
|
||||
)}
|
||||
{!isLanding && (
|
||||
<div
|
||||
tabIndex={-1}
|
||||
onFocus={() => setSearchFocussed(true)}
|
||||
onBlur={() => setSearchFocussed(false)}
|
||||
className={`ar-Header__app-search h-100 flex-v-center me-3 ${
|
||||
searchFocussed ? "w-50" : "w-25"
|
||||
}`}
|
||||
>
|
||||
<SearchField
|
||||
items={[]}
|
||||
onChange={() => {}}
|
||||
placeholder="Search icons, components, fonts..."
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<LoadableIcon
|
||||
classes="d-inline d-sm-none ms-auto"
|
||||
icon="cg.CgDarkMode"
|
||||
size="2rem"
|
||||
onClick={() => {
|
||||
const nextTheme =
|
||||
theme === ArThemes.LIGHT1 ? ArThemes.DARK1 : ArThemes.LIGHT1
|
||||
dispatch(setTheme(nextTheme))
|
||||
document
|
||||
.getElementsByTagName("html")[0]
|
||||
.setAttribute("ar-theme", nextTheme)
|
||||
}}
|
||||
color={theme === ArThemes.DARK1 ? "lightgrey" : "black"}
|
||||
/>
|
||||
<div
|
||||
className={`h-100 d-none d-sm-inline${
|
||||
isLanding ? " w-100" : " ms-auto"
|
||||
}`}
|
||||
>
|
||||
<FlexTools isLanding={isLanding} route={route as string} />
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
||||
@@ -1,3 +0,0 @@
|
||||
import Header from "./Header"
|
||||
|
||||
export default Header
|
||||
@@ -1,48 +0,0 @@
|
||||
.ar-IconController {
|
||||
.ar-IconController__main, .ar-IconController__header {
|
||||
background-color: var(--ar-bg-base);
|
||||
}
|
||||
|
||||
.ar-IconController__color-palette {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.ar-IconController__download-button, .ar-IconController__animate-button {
|
||||
background-color: orange;
|
||||
border-color: orange;
|
||||
}
|
||||
|
||||
.ar-IconController__main {
|
||||
border-radius: 0.3rem;
|
||||
}
|
||||
|
||||
.ar-IconController__controls-form {
|
||||
background-color: var(--ar-bg);
|
||||
}
|
||||
|
||||
.hover-show {
|
||||
cursor: pointer;
|
||||
.ar-IconController__download-icon {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.ar-IconController__download-icon {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
transition: box-shadow 0.3s;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 8px var(--ar-shadow);
|
||||
border-radius: 3px
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ar-IconController__meta-item {
|
||||
color: var(--ar-color-secondary);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import IconController from "./IconController"
|
||||
|
||||
describe("IconController", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,378 +0,0 @@
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useEffect, useState } from "react"
|
||||
import {
|
||||
ArButtonVariants,
|
||||
ArPopoverSlots,
|
||||
IconControllerProps,
|
||||
ObjectType,
|
||||
} from "@armco/types"
|
||||
import { getIconStyles } from "../../pages/IconPage/IconPage.slice"
|
||||
import {
|
||||
API_CONFIG,
|
||||
Breadcrumb,
|
||||
Button,
|
||||
DomHelper,
|
||||
LoadableIcon,
|
||||
Network,
|
||||
Select,
|
||||
Suggestions,
|
||||
setRightPanelContent,
|
||||
Tags,
|
||||
TextInput,
|
||||
useAppDispatch,
|
||||
useAppSelector,
|
||||
} from ".."
|
||||
import "./IconController.component.scss"
|
||||
|
||||
const complement = (hex?: string) => {
|
||||
if (hex) {
|
||||
const hexParts = hex.substring(1).toLowerCase().split("")
|
||||
const hexMap = { a: 10, b: 11, c: 12, d: 13, e: 14, f: 15 }
|
||||
const reverseMap = { 10: "a", 11: "b", 12: "c", 13: "d", 14: "e", 15: "f" }
|
||||
let complementValue = "#"
|
||||
hexParts.forEach((part: string) => {
|
||||
let num = +part
|
||||
if (isNaN(num)) {
|
||||
num = hexMap[part as keyof object]
|
||||
}
|
||||
const complementNumeric = 15 - num
|
||||
complementValue +=
|
||||
complementNumeric > 9
|
||||
? reverseMap[complementNumeric as keyof object]
|
||||
: complementNumeric
|
||||
})
|
||||
return complementValue
|
||||
}
|
||||
return "#fff"
|
||||
}
|
||||
|
||||
const STATIC_ROOT = API_CONFIG.STATIC_HOST[process.env.NODE_ENV]
|
||||
|
||||
const IconController = (props: IconControllerProps): JSX.Element => {
|
||||
const { group, icon, name } = props
|
||||
const [size, setSize] = useState<string>()
|
||||
const [unit, setUnit] = useState<string>("rem")
|
||||
const [similarIcons, setSimilarIcons] = useState<Array<string>>()
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
const iconStyles = useAppSelector(getIconStyles)
|
||||
const fontColor = complement(iconStyles?.bgColor || "white")
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
// const suggestions = getSuggestions(group, name)
|
||||
|
||||
useEffect(() => {
|
||||
const filename = `${group}_${name}-black.png`
|
||||
Network.getStatic(`/static/${filename}`).then((res) => {
|
||||
const imgBlob = res.body
|
||||
const formData = new FormData()
|
||||
const file = new File([imgBlob], filename, { type: imgBlob.type })
|
||||
formData.append("file", file)
|
||||
formData.append("count", "20")
|
||||
Network.post(
|
||||
"http://localhost:5002/api/similar-icon-paths",
|
||||
formData,
|
||||
undefined,
|
||||
{ headers: { "Content-Type": "multipart/form-data" } },
|
||||
true,
|
||||
true,
|
||||
).then((res) => {
|
||||
if (res.status === 200) {
|
||||
const iconPaths = res.body.map((str: string) =>
|
||||
str.replace(
|
||||
"../images",
|
||||
`${API_CONFIG.STATIC_HOST[process.env.NODE_ENV]}/static`,
|
||||
),
|
||||
)
|
||||
// setSimilarIcons(iconPaths)
|
||||
Network.post(STATIC_ROOT + "/icon/png-to-svg", {
|
||||
urls: iconPaths,
|
||||
}).then((res) => {
|
||||
setSimilarIcons(res.body)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}, [name, group])
|
||||
|
||||
return (
|
||||
<div className="ar-IconController container h-100 overflow-auto">
|
||||
{group && name && (
|
||||
<Breadcrumb
|
||||
data={[
|
||||
{ label: group, route: `/icon/${group}` },
|
||||
{ label: name, route: `/icon/${group}/${name}` },
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<div className="ar-IconController__header mb-3 p-3 border d-flex">
|
||||
<h6 className="mb-0 fw-bold">
|
||||
{group}.{name}
|
||||
</h6>
|
||||
<Button
|
||||
containerClasses="ms-auto"
|
||||
classes="ar-IconController__animate-button me-3"
|
||||
variant={ArButtonVariants.PRIMARY}
|
||||
postIcon="md/MdAnimation"
|
||||
content="Animate"
|
||||
onClick={() =>
|
||||
navigate("/icon/animate", {
|
||||
state: { icon, iconStyles, name: `${group}_${name}` },
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
classes="ar-IconController__download-button me-3"
|
||||
variant={ArButtonVariants.PRIMARY}
|
||||
postIcon="md/MdDownload"
|
||||
content="Download"
|
||||
onClick={() =>
|
||||
DomHelper.downloadSvg(
|
||||
(icon as ObjectType).icon as string,
|
||||
name || "download.svg",
|
||||
{
|
||||
size: "3rem",
|
||||
...iconStyles,
|
||||
color: iconStyles?.strokeColor || "royalblue",
|
||||
},
|
||||
)
|
||||
}
|
||||
/>
|
||||
<LoadableIcon
|
||||
classes="ar-IconController__color-palette cursor-pointer hover-shadow"
|
||||
icon="io/IoIosColorPalette"
|
||||
color="orange"
|
||||
slot={ArPopoverSlots.ANCHOR}
|
||||
size="2rem"
|
||||
onClick={() => dispatch(setRightPanelContent({ name: "IconEditor" }))}
|
||||
/>
|
||||
</div>
|
||||
<div className="ar-IconController__main mb-3 p-3 border">
|
||||
<div className="row">
|
||||
<div
|
||||
className="ar-IconController__icon-sizes col"
|
||||
style={{ backgroundColor: iconStyles?.bgColor }}
|
||||
>
|
||||
{group && name && (
|
||||
<div className="row h-100 border-right">
|
||||
<div className="col flex-center flex-column border-right">
|
||||
<div className="h-50 flex-center flex-column border-bottom w-100 position-relative hover-show">
|
||||
<LoadableIcon
|
||||
classes="ar-IconController__download-icon position-absolute top-1 end-1"
|
||||
icon="md/MdDownload"
|
||||
color="orange"
|
||||
size="2.5rem"
|
||||
onClick={() =>
|
||||
DomHelper.downloadSvg(
|
||||
(icon as ObjectType).icon as string,
|
||||
name,
|
||||
{
|
||||
size: "3rem",
|
||||
...iconStyles,
|
||||
color: iconStyles?.strokeColor || "royalblue",
|
||||
},
|
||||
)
|
||||
}
|
||||
/>
|
||||
<LoadableIcon
|
||||
key={`${group}/${name}`}
|
||||
icon={`${group}/${name}`}
|
||||
size="3rem"
|
||||
color={iconStyles?.strokeColor || "royalblue"}
|
||||
fillColor={iconStyles?.fillColor}
|
||||
strokeColor={iconStyles?.strokeColor}
|
||||
strokeWidth={iconStyles?.strokeWidth}
|
||||
/>
|
||||
<span
|
||||
className="fw-bold"
|
||||
style={{ color: fontColor || "black" }}
|
||||
>
|
||||
3rem
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-50 flex-center flex-column w-100 position-relative hover-show">
|
||||
<LoadableIcon
|
||||
classes="ar-IconController__download-icon position-absolute top-1 end-1"
|
||||
icon="md/MdDownload"
|
||||
color="orange"
|
||||
size="2.5rem"
|
||||
onClick={() =>
|
||||
DomHelper.downloadSvg(
|
||||
(icon as ObjectType).icon as string,
|
||||
name,
|
||||
{
|
||||
size: "7rem",
|
||||
...iconStyles,
|
||||
color: iconStyles?.strokeColor || "royalblue",
|
||||
},
|
||||
)
|
||||
}
|
||||
/>
|
||||
<LoadableIcon
|
||||
key={`${group}/${name}`}
|
||||
icon={`${group}/${name}`}
|
||||
size="7rem"
|
||||
color={iconStyles?.strokeColor || "royalblue"}
|
||||
fillColor={iconStyles?.fillColor}
|
||||
strokeColor={iconStyles?.strokeColor}
|
||||
strokeWidth={iconStyles?.strokeWidth}
|
||||
/>
|
||||
<span
|
||||
className="fw-bold"
|
||||
style={{ color: fontColor || "black" }}
|
||||
>
|
||||
7rem
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col flex-center flex-column position-relative hover-show">
|
||||
<LoadableIcon
|
||||
classes="ar-IconController__download-icon position-absolute top-1 end-1"
|
||||
icon="md/MdDownload"
|
||||
color="orange"
|
||||
size="2.5rem"
|
||||
onClick={() =>
|
||||
DomHelper.downloadSvg(
|
||||
(icon as ObjectType).icon as string,
|
||||
name,
|
||||
{
|
||||
size: size && unit ? size + unit : "20rem",
|
||||
...iconStyles,
|
||||
color: iconStyles?.strokeColor || "royalblue",
|
||||
},
|
||||
)
|
||||
}
|
||||
/>
|
||||
<LoadableIcon
|
||||
key={`${group}/${name}`}
|
||||
icon={`${group}/${name}`}
|
||||
size={`${size}${unit}` || "20rem"}
|
||||
color={iconStyles?.strokeColor || "royalblue"}
|
||||
fillColor={iconStyles?.fillColor}
|
||||
strokeColor={iconStyles?.strokeColor}
|
||||
strokeWidth={iconStyles?.strokeWidth}
|
||||
/>
|
||||
<span
|
||||
className="fw-bold"
|
||||
style={{ color: fontColor || "black" }}
|
||||
>
|
||||
{size && unit ? size + unit : "20rem"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="ar-IconController__controls col d-flex">
|
||||
<div className="row w-100">
|
||||
{icon && (
|
||||
<div className="ar-IconController__meta col-4">
|
||||
<h6>Icon Details</h6>
|
||||
<div className="ar-IconController__meta-item row lh-1-5">
|
||||
<div className="col">
|
||||
<strong>Created By: </strong>
|
||||
{icon.createdby as string}
|
||||
</div>
|
||||
</div>
|
||||
<div className="ar-IconController__meta-item row lh-1-5">
|
||||
<div className="col">
|
||||
<strong>Description: </strong>
|
||||
{icon.description as string}
|
||||
</div>
|
||||
</div>
|
||||
<div className="ar-IconController__meta-item row lh-1-5">
|
||||
<div className="col">
|
||||
<strong>Downloaded: </strong>
|
||||
{((icon.meta as ObjectType)?.downloadedTimes as number) ||
|
||||
0}
|
||||
</div>
|
||||
</div>
|
||||
<div className="ar-IconController__meta-item row lh-1-5">
|
||||
<div className="col">
|
||||
<strong>Liked: </strong>
|
||||
{((icon.meta as ObjectType)?.downloadedTimes as number) ||
|
||||
0}
|
||||
</div>
|
||||
</div>
|
||||
<div className="ar-IconController__meta-item row lh-1-5">
|
||||
<div className="col">
|
||||
<strong>Size: </strong>
|
||||
{(icon.meta as ObjectType)?.size as number}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={`ar-IconController__controls-form ${
|
||||
icon ? "col-8" : "col-12"
|
||||
} border`}
|
||||
>
|
||||
<div className="row h-100 py-3">
|
||||
<div className="ar-IconController__controls-form-container col-12 h-25">
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<TextInput
|
||||
type="slider"
|
||||
label="Icon Size"
|
||||
min={1}
|
||||
max={30}
|
||||
onChange={(e) =>
|
||||
setSize((e.target as HTMLInputElement).value)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="col">
|
||||
<Select
|
||||
label="Unit"
|
||||
onSelectionChanged={(e: { value: string }) =>
|
||||
setUnit(e.value)
|
||||
}
|
||||
options={[
|
||||
// @ts-ignore
|
||||
{ label: "rem", value: "rem" },
|
||||
// @ts-ignore
|
||||
{ label: "px", value: "px" },
|
||||
// @ts-ignore
|
||||
{ label: "vh", value: "vh" },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* <Tags classes="col-12 h-100 px-0" label={name} /> */}
|
||||
{icon && (
|
||||
<Tags
|
||||
// clickHandler={clickHandler}
|
||||
classes="col-12 h-75 px-0"
|
||||
label={name}
|
||||
tags={Object.fromEntries(
|
||||
(
|
||||
icon.tags as Array<{ name: string; verified: string }>
|
||||
).map((key) => [key.name, 1]),
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Suggestions
|
||||
classes="border mb-3"
|
||||
suggestions={similarIcons}
|
||||
title={`Icons Similar to ${name}`}
|
||||
inSvgString
|
||||
/>
|
||||
<Suggestions
|
||||
classes="border mb-3"
|
||||
suggestions={similarIcons}
|
||||
title="Trending Icons"
|
||||
inSvgString
|
||||
// suggestions={recentlyVisited}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconController
|
||||
@@ -1,3 +0,0 @@
|
||||
import IconController from "./IconController"
|
||||
|
||||
export default IconController
|
||||
@@ -1,2 +0,0 @@
|
||||
.ar-IconEditor {
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import IconEditor from "./IconEditor"
|
||||
|
||||
describe("IconEditor", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,46 +0,0 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { v4 as uuid } from "uuid"
|
||||
import { IconEditorProps } from "@armco/types"
|
||||
import { setIconStyles } from "../../pages/IconPage/IconPage.slice"
|
||||
import { IconStyleSelector, useAppDispatch } from ".."
|
||||
import "./IconEditor.component.scss"
|
||||
|
||||
const iconEditorTabs = [
|
||||
{
|
||||
label: "Icon",
|
||||
id: uuid(),
|
||||
},
|
||||
{
|
||||
label: "Background",
|
||||
id: uuid(),
|
||||
},
|
||||
]
|
||||
|
||||
const IconEditor = (props: IconEditorProps): JSX.Element => {
|
||||
const { layout } = props
|
||||
const [iconStyles, setIconStylesState] = useState<{
|
||||
fillColor?: string
|
||||
strokeColor?: string
|
||||
bgColor?: string
|
||||
strokeWidth?: string
|
||||
}>()
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
useEffect(() => {
|
||||
iconStyles && dispatch(setIconStyles(iconStyles))
|
||||
}, [iconStyles, dispatch])
|
||||
|
||||
return (
|
||||
<div className="ar-IconEditor h-100">
|
||||
<div className="ar-IconEditor__config-form h-100 overflow-auto">
|
||||
<IconStyleSelector
|
||||
iconStyles={iconStyles}
|
||||
setIconStyles={setIconStylesState}
|
||||
layout={layout}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconEditor
|
||||
@@ -1,3 +0,0 @@
|
||||
import IconEditor from "./IconEditor"
|
||||
|
||||
export default IconEditor
|
||||
@@ -1,12 +0,0 @@
|
||||
.ar-IconMergeContainer {
|
||||
svg {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
margin-left: 2rem;
|
||||
margin-right: 2rem;
|
||||
|
||||
path:hover {
|
||||
outline: 1px dotted grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import IconMergeContainer from "./IconMergeContainer"
|
||||
|
||||
describe("IconMergeContainer", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,153 +0,0 @@
|
||||
import { useEffect, useRef } from "react"
|
||||
import { ArrayType, IconMergeContainerProps } from "@armco/types"
|
||||
import { DomHelper } from ".."
|
||||
import "./IconMergeContainer.component.scss"
|
||||
|
||||
const iconTest = [
|
||||
{
|
||||
name: "CiAlignBottom",
|
||||
icon: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><g id="Align_Bottom"><g><path d="M3.548,20.922h16.9a.5.5,0,0,0,0-1H3.548a.5.5,0,0,0,0,1Z"></path><path d="M9,18.919H6.565a2.5,2.5,0,0,1-2.5-2.5V5.578a2.5,2.5,0,0,1,2.5-2.5H9a2.5,2.5,0,0,1,2.5,2.5V16.419A2.5,2.5,0,0,1,9,18.919ZM6.565,4.078a1.5,1.5,0,0,0-1.5,1.5V16.419a1.5,1.5,0,0,0,1.5,1.5H9a1.5,1.5,0,0,0,1.5-1.5V5.578A1.5,1.5,0,0,0,9,4.078Z"></path><path d="M17.437,18.919H15a2.5,2.5,0,0,1-2.5-2.5V10.55A2.5,2.5,0,0,1,15,8.05h2.434a2.5,2.5,0,0,1,2.5,2.5v5.869A2.5,2.5,0,0,1,17.437,18.919ZM15,9.05a1.5,1.5,0,0,0-1.5,1.5v5.869a1.5,1.5,0,0,0,1.5,1.5h2.434a1.5,1.5,0,0,0,1.5-1.5V10.55a1.5,1.5,0,0,0-1.5-1.5Z"></path></g></g></svg>',
|
||||
createdby: "Armco",
|
||||
createdAt: "2023-10-14T20:46:51.541Z",
|
||||
updatedAt: "2023-10-14T20:46:51.541Z",
|
||||
meta: {
|
||||
size: "772 bytes",
|
||||
downloadTimes: 0,
|
||||
favoriteTimes: 0,
|
||||
},
|
||||
group: "ci",
|
||||
description: "",
|
||||
tags: [
|
||||
{
|
||||
name: "Ci",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "Align",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "Bottom",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "icon",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "armco",
|
||||
state: "verified",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "CiAlignCenterH",
|
||||
icon: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><g id="Align_Center-H"><path d="M17.42,4.062H12.5v-.51a.5.5,0,0,0-1,0v.51H6.58a2.507,2.507,0,0,0-2.5,2.5V9a2.5,2.5,0,0,0,2.5,2.5H11.5v1H9.06A2.507,2.507,0,0,0,6.56,15v2.44a2.507,2.507,0,0,0,2.5,2.5H11.5v.51a.5.5,0,0,0,1,0v-.51h2.43a2.5,2.5,0,0,0,2.5-2.5V15a2.5,2.5,0,0,0-2.5-2.5H12.5v-1h4.92A2.5,2.5,0,0,0,19.92,9V6.562A2.507,2.507,0,0,0,17.42,4.062ZM11.5,18.942H9.06a1.511,1.511,0,0,1-1.5-1.5V15a1.5,1.5,0,0,1,1.5-1.5H11.5Zm0-8.44H6.58A1.5,1.5,0,0,1,5.08,9V6.562a1.5,1.5,0,0,1,1.5-1.5H11.5Zm3.43,3a1.5,1.5,0,0,1,1.5,1.5v2.44a1.5,1.5,0,0,1-1.5,1.5H12.5V13.5ZM18.92,9a1.5,1.5,0,0,1-1.5,1.5H12.5V5.062h4.92a1.5,1.5,0,0,1,1.5,1.5Z"></path></g></svg>',
|
||||
createdby: "Armco",
|
||||
createdAt: "2023-10-14T20:46:51.541Z",
|
||||
updatedAt: "2023-10-14T20:46:51.541Z",
|
||||
meta: {
|
||||
size: "790 bytes",
|
||||
downloadTimes: 0,
|
||||
favoriteTimes: 0,
|
||||
},
|
||||
group: "ci",
|
||||
description: "",
|
||||
tags: [
|
||||
{
|
||||
name: "Ci",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "Align",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "Center",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "H",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "icon",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "armco",
|
||||
state: "verified",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "CiAlignCenterV",
|
||||
icon: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><g id="Align_Center-V"><path d="M20.446,11.5h-.51V9.07a2.5,2.5,0,0,0-2.5-2.5h-2.43a2.5,2.5,0,0,0-2.5,2.5V11.5H11.5V6.58A2.5,2.5,0,0,0,9,4.08H6.566a2.5,2.5,0,0,0-2.5,2.5V11.5h-.52a.5.5,0,0,0,0,1h.52v4.92a2.5,2.5,0,0,0,2.5,2.5H9a2.5,2.5,0,0,0,2.5-2.5V12.5h1.01v2.43a2.5,2.5,0,0,0,2.5,2.5h2.43a2.5,2.5,0,0,0,2.5-2.5V12.5h.51A.5.5,0,0,0,20.446,11.5ZM10.5,17.42A1.5,1.5,0,0,1,9,18.92H6.566a1.5,1.5,0,0,1-1.5-1.5V12.5H10.5Zm0-5.92H5.066V6.58a1.5,1.5,0,0,1,1.5-1.5H9a1.5,1.5,0,0,1,1.5,1.5Zm8.44,3.43a1.5,1.5,0,0,1-1.5,1.5h-2.43a1.5,1.5,0,0,1-1.5-1.5V12.5h5.43Zm0-3.43h-5.43V9.07a1.5,1.5,0,0,1,1.5-1.5h2.43a1.5,1.5,0,0,1,1.5,1.5Z"></path></g></svg>',
|
||||
createdby: "Armco",
|
||||
createdAt: "2023-10-14T20:46:51.541Z",
|
||||
updatedAt: "2023-10-14T20:46:51.541Z",
|
||||
meta: {
|
||||
size: "784 bytes",
|
||||
downloadTimes: 0,
|
||||
favoriteTimes: 0,
|
||||
},
|
||||
group: "ci",
|
||||
description: "",
|
||||
tags: [
|
||||
{
|
||||
name: "Ci",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "Align",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "Center",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "V",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "icon",
|
||||
state: "verified",
|
||||
},
|
||||
{
|
||||
name: "armco",
|
||||
state: "verified",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
const IconMergeContainer = (props: IconMergeContainerProps): JSX.Element => {
|
||||
const { icons } = props
|
||||
const svgRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const childList: Array<SVGSVGElement> = []
|
||||
iconTest?.forEach((icon) => {
|
||||
const svgElement = DomHelper.svgStringToElement(icon.icon)
|
||||
if (svgElement) {
|
||||
childList.push(svgElement)
|
||||
}
|
||||
})
|
||||
|
||||
svgRef.current?.replaceChildren(...childList)
|
||||
const paths = childList.reduce((acc: ArrayType, svgElement) => {
|
||||
const pathList = svgElement.querySelectorAll("path")
|
||||
console.log(pathList)
|
||||
pathList.forEach((pathItem) => {
|
||||
pathItem.setAttribute("draggable", "true")
|
||||
pathItem.addEventListener("dragstart", (e) => console.log(e))
|
||||
})
|
||||
return acc.concat(Array.from(pathList) as any)
|
||||
}, [])
|
||||
;(paths[0] as any).parentElement?.replaceChildren(...paths)
|
||||
}, [svgRef])
|
||||
|
||||
return <div className="ar-IconMergeContainer" ref={svgRef} />
|
||||
}
|
||||
|
||||
export default IconMergeContainer
|
||||
@@ -1,3 +0,0 @@
|
||||
import IconMergeContainer from "./IconMergeContainer"
|
||||
|
||||
export default IconMergeContainer
|
||||
@@ -1,3 +0,0 @@
|
||||
.ar-IconStyleSelector {
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import IconStyleSelector from "./IconStyleSelector"
|
||||
|
||||
describe("IconStyleSelector", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,85 +0,0 @@
|
||||
import { v4 as uuid } from "uuid"
|
||||
import { IconStyles, IconStyleSelectorProps } from "@armco/types"
|
||||
import { AdvancedColorPicker, Slider } from ".."
|
||||
import "./IconStyleSelector.component.scss"
|
||||
|
||||
const fillColorId = uuid()
|
||||
const strokeColorId = uuid()
|
||||
const bgColorId = uuid()
|
||||
|
||||
const IconStyleSelector = (props: IconStyleSelectorProps): JSX.Element => {
|
||||
const { layout, setIconStyles } = props
|
||||
|
||||
const setIconStylesLocal = (propName: string, propValue: string) => {
|
||||
console.log(propName)
|
||||
setIconStyles((currentIconStyles: IconStyles) => ({
|
||||
...currentIconStyles,
|
||||
[propName]: propValue,
|
||||
}))
|
||||
}
|
||||
|
||||
const fill = (
|
||||
<AdvancedColorPicker
|
||||
key="fill-color-selector"
|
||||
id={fillColorId}
|
||||
displaySample
|
||||
onColorSelect={(color) => setIconStylesLocal("fillColor", color)}
|
||||
title="Fill Color"
|
||||
/>
|
||||
)
|
||||
|
||||
const strokeWidth = (
|
||||
<Slider
|
||||
containerClasses="py-2 border-top border-bottom mb-4"
|
||||
label="Stroke Width"
|
||||
min={1}
|
||||
max={10}
|
||||
onChange={(e) => setIconStylesLocal("strokeWidth", e.target.value)}
|
||||
withManual
|
||||
/>
|
||||
)
|
||||
const stroke = (
|
||||
<AdvancedColorPicker
|
||||
key="stoke-color-selector"
|
||||
id={strokeColorId}
|
||||
displaySample
|
||||
onColorSelect={(color) => setIconStylesLocal("strokeColor", color)}
|
||||
title="Stroke Color"
|
||||
/>
|
||||
)
|
||||
|
||||
const bg = (
|
||||
<AdvancedColorPicker
|
||||
key="bg-color-selector"
|
||||
id={bgColorId}
|
||||
displaySample
|
||||
onColorSelect={(color) => setIconStylesLocal("bgColor", color)}
|
||||
title="Background Color"
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="ar-IconStyleSelector p-3 w-100 overflow-auto">
|
||||
{layout === "horizontal" ? (
|
||||
<>
|
||||
<div className="d-flex">
|
||||
<div className="px-3">{fill}</div>
|
||||
<div className="px-3">{stroke}</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col">{strokeWidth}</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{fill}
|
||||
{strokeWidth}
|
||||
{stroke}
|
||||
{bg}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconStyleSelector
|
||||
@@ -1,3 +0,0 @@
|
||||
import IconStyleSelector from "./IconStyleSelector"
|
||||
|
||||
export default IconStyleSelector
|
||||
@@ -1,67 +0,0 @@
|
||||
.ar-IconsList {
|
||||
.ar-IconsList__header-pagination-search-upload {
|
||||
background-color: var(--ar-bg);
|
||||
color: var(--ar-colora);
|
||||
}
|
||||
|
||||
.ar-IconList__selection-manager {
|
||||
background-color: var(--ar-bg);
|
||||
color: var(--ar-colora);
|
||||
transition: all 0.3s;
|
||||
|
||||
&.hide {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
padding: 0 !important;
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ar-IconsList__icon-tile-container {
|
||||
background-color: var(--ar-bg);
|
||||
color: var(--ar-color);
|
||||
}
|
||||
|
||||
.ar-IconTile {
|
||||
cursor: pointer;
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
transition: width 0.3s, padding 0.3s;
|
||||
|
||||
&.compact {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
&.list {
|
||||
width: 8rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--ar-bg-hover);
|
||||
}
|
||||
|
||||
footer {
|
||||
height: 1.9rem;
|
||||
border-top: 1px solid var(--bs-border-color);
|
||||
background-color: var(--ar-bg-base);
|
||||
}
|
||||
}
|
||||
|
||||
.ar-IconsList__upload-button {
|
||||
background-color: orange;
|
||||
border-color: orange;
|
||||
}
|
||||
|
||||
.ar-IconsList__color-palette {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 576px) {
|
||||
.ar-IconTile {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import IconsList from "./IconsList"
|
||||
|
||||
describe("IconsList", () => {
|
||||
it("renders without error", () => {})
|
||||
})
|
||||
@@ -1,430 +0,0 @@
|
||||
import { ChangeEvent, useEffect, useState } from "react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { v4 as uuid } from "uuid"
|
||||
import {
|
||||
ArButtonVariants,
|
||||
ArIconTileTypes,
|
||||
ArLoaderTypes,
|
||||
ArPopoverSlots,
|
||||
ArPopoverTriggers,
|
||||
ArSizes,
|
||||
FunctionType,
|
||||
IconResponse,
|
||||
IconStyles,
|
||||
IconTileProps,
|
||||
IconsListProps,
|
||||
ObjectType,
|
||||
SearchItem,
|
||||
SegmentData,
|
||||
} from "@armco/types"
|
||||
import {
|
||||
getFavorites,
|
||||
getSelectedTag,
|
||||
removeFavorite,
|
||||
setFavorites,
|
||||
} from "../../pages/IconsPage/IconsPage.slice"
|
||||
import {
|
||||
getIconStyles,
|
||||
setIconStyles,
|
||||
} from "../../pages/IconPage/IconPage.slice"
|
||||
import {
|
||||
API_CONFIG,
|
||||
Button,
|
||||
ENDPOINTS,
|
||||
Helper,
|
||||
IconStyleSelector,
|
||||
IconTile,
|
||||
LoadableIcon,
|
||||
Loader,
|
||||
Network,
|
||||
notify,
|
||||
Pagination,
|
||||
Pillbox,
|
||||
Popover,
|
||||
SearchField,
|
||||
SegmentedControl,
|
||||
setRightPanelContent,
|
||||
useAppDispatch,
|
||||
useAppSelector,
|
||||
} from ".."
|
||||
import "./IconsList.component.scss"
|
||||
|
||||
const fetchIconsPage = (
|
||||
limit: number,
|
||||
from: number,
|
||||
filters: ObjectType,
|
||||
dataSetter: FunctionType,
|
||||
pageSetter: FunctionType,
|
||||
setLoading: FunctionType,
|
||||
) => {
|
||||
const pageApi =
|
||||
API_CONFIG.STATIC_HOST[process.env.NODE_ENV] +
|
||||
ENDPOINTS.STATIC.ICON.ROOT +
|
||||
ENDPOINTS.STATIC.ICON.PAGE
|
||||
const queryParams: ObjectType = { pageSize: limit, from, ...filters }
|
||||
Network.get(pageApi, queryParams)
|
||||
.then((response) => {
|
||||
if (response && response.status === 200) {
|
||||
if (response.body && dataSetter) {
|
||||
dataSetter(response.body)
|
||||
pageSetter(response.body.slice(0, 100))
|
||||
}
|
||||
}
|
||||
setLoading(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
const IconsList = (props: IconsListProps): JSX.Element => {
|
||||
const { variant } = props
|
||||
const [searchText, setSearchText] = useState<string | undefined>()
|
||||
const [icons, setIcons] = useState<Array<IconResponse>>()
|
||||
const [page, setPage] = useState<Array<IconResponse>>()
|
||||
const [view, setView] = useState<SegmentData>()
|
||||
const [loading, setLoading] = useState<boolean>()
|
||||
const [isSelectMode, toggleSelectMode] = useState<boolean>()
|
||||
const [selectedIcons, setSelectedIcons] = useState<Array<IconResponse>>()
|
||||
const dispatch = useAppDispatch()
|
||||
const favorites = useAppSelector(getFavorites)
|
||||
const selectedTag = useAppSelector<string | undefined>(getSelectedTag)
|
||||
const iconStyles = useAppSelector<IconStyles | undefined>(getIconStyles)
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
useEffect(() => {
|
||||
setView({ name: ArIconTileTypes.COMFY })
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (favorites) {
|
||||
dispatch(
|
||||
setRightPanelContent(
|
||||
favorites.length > 0 ? { name: "FavoritesList" } : { name: "" },
|
||||
),
|
||||
)
|
||||
}
|
||||
}, [favorites, dispatch])
|
||||
|
||||
useEffect(() => {
|
||||
const filters: ObjectType = {}
|
||||
if (selectedTag) {
|
||||
filters.tags = selectedTag
|
||||
}
|
||||
if (searchText) {
|
||||
filters.search = searchText
|
||||
}
|
||||
setLoading(true)
|
||||
fetchIconsPage(2000, 0, filters, setIcons, setPage, setLoading)
|
||||
}, [selectedTag, searchText])
|
||||
|
||||
const onIconTileClick = (iconProps: IconResponse) => {
|
||||
isSelectMode
|
||||
? setSelectedIcons((currentIcons) => {
|
||||
currentIcons = [...(currentIcons || [])]
|
||||
const existingIconIndex = currentIcons?.findIndex(
|
||||
(currentIcon) => currentIcon.name === iconProps.name,
|
||||
)
|
||||
if (existingIconIndex > -1) {
|
||||
currentIcons.splice(existingIconIndex, 1)
|
||||
} else {
|
||||
currentIcons.push(iconProps)
|
||||
}
|
||||
return currentIcons
|
||||
})
|
||||
: view?.name === ArIconTileTypes.COMPACT &&
|
||||
dispatch(
|
||||
notify({
|
||||
show: true,
|
||||
message: `Icon link for ${iconProps.name} copied`,
|
||||
uid: uuid(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ar-IconsList h-100 w-100 overflow-auto position-relative d-flex flex-column">
|
||||
{!icons && (
|
||||
<Loader label="Loading Icons..." type={ArLoaderTypes.SHAPES} />
|
||||
)}
|
||||
<div className="ar-IconsList__header-pagination-search-upload py-2 px-3 mb-2 border">
|
||||
<div className="row">
|
||||
<div className="col d-none d-md-flex align-items-center">
|
||||
<h6 className="mb-0 h-100 flex-center px-3 border-right">
|
||||
<Button
|
||||
variant={ArButtonVariants.LINK}
|
||||
content="Categories"
|
||||
size={ArSizes.SMALL}
|
||||
splitOptions={[
|
||||
{
|
||||
label: "All",
|
||||
},
|
||||
{
|
||||
label: "Business",
|
||||
},
|
||||
{
|
||||
label: "Medical",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</h6>
|
||||
</div>
|
||||
<span className="col flex-v-center justify-content-end">
|
||||
<Popover version="v2">
|
||||
<LoadableIcon
|
||||
classes="ar-IconsList__color-palette cursor-pointer ms-auto hover-shadow me-3"
|
||||
icon="io/IoIosColorPalette"
|
||||
color="orange"
|
||||
slot={ArPopoverSlots.ANCHOR}
|
||||
size="2rem"
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<IconStyleSelector
|
||||
slot={ArPopoverSlots.POPOVER}
|
||||
setIconStyles={(iconStylesUpdater) => {
|
||||
const updatedStyles = iconStylesUpdater(iconStyles)
|
||||
dispatch(setIconStyles(updatedStyles))
|
||||
}}
|
||||
layout="horizontal"
|
||||
/>
|
||||
</Popover>
|
||||
<Button
|
||||
classes="h-100 float-end me-3"
|
||||
content="Create"
|
||||
size={ArSizes.SMALL}
|
||||
variant={ArButtonVariants.LINKHOVEREFFECT}
|
||||
preIcon="io5/IoCreateOutline"
|
||||
onClick={() => toggleSelectMode(true)}
|
||||
/>
|
||||
<Button
|
||||
classes="ar-IconsList__upload-button h-100 float-end"
|
||||
content="Upload"
|
||||
size={ArSizes.SMALL}
|
||||
variant={ArButtonVariants.PRIMARY}
|
||||
/>
|
||||
<SegmentedControl
|
||||
classes="d-none d-sm-inline ms-3"
|
||||
hasUniformSegments
|
||||
segments={[
|
||||
{
|
||||
name: "comfy",
|
||||
icon: "md/MdViewComfy",
|
||||
tooltip: "View Comfortable",
|
||||
},
|
||||
{
|
||||
name: "compact",
|
||||
icon: "md/MdViewCompact",
|
||||
tooltip: "View Compact",
|
||||
},
|
||||
{
|
||||
name: "list",
|
||||
icon: "io5/IoList",
|
||||
tooltip: "Quick Peak",
|
||||
},
|
||||
]}
|
||||
onChange={(view) => setView(view as SegmentData)}
|
||||
/>
|
||||
</span>
|
||||
<div className="col d-none d-md-flex flex-v-center">
|
||||
<SearchField
|
||||
classes="bg-white"
|
||||
placeholder="Search by name, tags, description"
|
||||
onChange={Helper.debounce(
|
||||
(event: ChangeEvent<HTMLInputElement>) =>
|
||||
setSearchText(event.target.value),
|
||||
1000,
|
||||
)}
|
||||
items={
|
||||
icons
|
||||
? icons.map(
|
||||
(icon): SearchItem => ({
|
||||
label: icon.name,
|
||||
data: icon,
|
||||
}),
|
||||
)
|
||||
: []
|
||||
}
|
||||
hidePopup
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`ar-IconList__selection-manager d-flex${
|
||||
isSelectMode ? " mb-2 py-2 px-3 border" : " hide"
|
||||
}`}
|
||||
>
|
||||
{selectedIcons && selectedIcons.length > 0 && (
|
||||
<Pillbox
|
||||
data={(selectedIcons || []).map((icon) => ({
|
||||
label: icon.name,
|
||||
deletable: true,
|
||||
}))}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
)}
|
||||
<div className="ms-auto d-flex">
|
||||
<Button
|
||||
classes="me-3"
|
||||
content="Cancel"
|
||||
variant={ArButtonVariants.SECONDARY}
|
||||
size={ArSizes.SMALL}
|
||||
onClick={() => {
|
||||
toggleSelectMode(false)
|
||||
setSelectedIcons(undefined)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
content="Done"
|
||||
variant={ArButtonVariants.PRIMARY}
|
||||
size={ArSizes.SMALL}
|
||||
onClick={() =>
|
||||
navigate("/icons/merge-icons", { state: selectedIcons })
|
||||
}
|
||||
disabled={!selectedIcons || selectedIcons.length === 0}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{page && (
|
||||
<div className="ar-IconsList__icon-tile-container-wrapper d-flex flex-grow-1 justify-content-between">
|
||||
<div
|
||||
className="ar-IconsList__icon-tile-container py-2 px-3 border d-grid w-100"
|
||||
style={{
|
||||
gridTemplateColumns: "repeat(10, minmax(8rem, auto))",
|
||||
gridAutoRows: "max(8rem)",
|
||||
}}
|
||||
>
|
||||
{loading && (
|
||||
<Loader label="Applying filters..." type={ArLoaderTypes.CIRCLE} />
|
||||
)}
|
||||
{page.map((icon, index) => (
|
||||
<Popover
|
||||
trigger={ArPopoverTriggers.HOVER}
|
||||
key={"icon-tile-popover-" + index}
|
||||
classes="mb-3"
|
||||
version="v2"
|
||||
>
|
||||
{!view || view.name !== ArIconTileTypes.LIST ? (
|
||||
<span slot={ArPopoverSlots.POPOVER}>
|
||||
<b className="d-block">{icon.group}</b>
|
||||
{icon.name
|
||||
?.match(/[A-Z][a-z]+/g)
|
||||
?.slice(1)
|
||||
.join(" ")}
|
||||
</span>
|
||||
) : null}
|
||||
<IconTile
|
||||
type={
|
||||
view && view.name
|
||||
? (view.name as ArIconTileTypes)
|
||||
: ArIconTileTypes.COMFY
|
||||
}
|
||||
hideBorder={view?.name !== ArIconTileTypes.LIST}
|
||||
classes={view?.name}
|
||||
slot={ArPopoverSlots.ANCHOR}
|
||||
key={icon + "-" + index}
|
||||
icon={icon}
|
||||
iconSize={
|
||||
view?.name === ArIconTileTypes.LIST ? "1rem" : "2rem"
|
||||
}
|
||||
onClick={onIconTileClick}
|
||||
tools={[
|
||||
{
|
||||
iconProps: {
|
||||
icon: "sl.SlOptions",
|
||||
color: "white",
|
||||
hoverColor: "lightblue",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
iconProps: {
|
||||
icon: "io.IoIosShareAlt",
|
||||
hoverColor: "lightblue",
|
||||
},
|
||||
name: "share",
|
||||
onClick: () => {},
|
||||
},
|
||||
],
|
||||
name: "more-options",
|
||||
onClick: () => {},
|
||||
},
|
||||
{
|
||||
iconProps: {
|
||||
icon: "ai/AiFillLike",
|
||||
color: "white",
|
||||
hoverColor: "lightblue",
|
||||
},
|
||||
name: "like",
|
||||
onClick: () => {},
|
||||
},
|
||||
{
|
||||
iconProps: {
|
||||
icon: "md/MdFavorite",
|
||||
hoverColor: "lightblue",
|
||||
toggleColor: "red",
|
||||
color: "white",
|
||||
toggled: false,
|
||||
},
|
||||
name: "favorite",
|
||||
onClick: () => {
|
||||
const favorite = favorites.find(
|
||||
(searchedIcon: IconTileProps) =>
|
||||
icon.name === searchedIcon.icon.name,
|
||||
)
|
||||
if (!favorites || !favorite) {
|
||||
dispatch(setFavorites({ icon }))
|
||||
dispatch(
|
||||
notify({
|
||||
message: "Icon Added to your favorites",
|
||||
show: true,
|
||||
uid: uuid(),
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
dispatch(removeFavorite({ icon }))
|
||||
dispatch(
|
||||
notify({
|
||||
message: "Icon removed from your favorites",
|
||||
show: true,
|
||||
uid: uuid(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
hideFooter={
|
||||
(view && (view.name as ArIconTileTypes)) !==
|
||||
ArIconTileTypes.LIST
|
||||
}
|
||||
selectable={isSelectMode}
|
||||
fillColor={iconStyles?.fillColor}
|
||||
strokeColor={iconStyles?.strokeColor}
|
||||
strokeWidth={iconStyles?.strokeWidth}
|
||||
/>
|
||||
</Popover>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* {slices && ( */}
|
||||
<Pagination
|
||||
classes="my-3 flex-center"
|
||||
data={icons}
|
||||
maxPillsToShow={5}
|
||||
pageSetter={setPage}
|
||||
// trigger={ArPageTriggers.SCROLL}
|
||||
count={1}
|
||||
load={100}
|
||||
dataFetcher={(load, count) =>
|
||||
fetchIconsPage(load, count, {}, setIcons, setPage, setLoading)
|
||||
}
|
||||
/>
|
||||
{/* )} */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconsList
|
||||
@@ -1,3 +0,0 @@
|
||||
import IconsList from "./IconsList"
|
||||
|
||||
export default IconsList
|
||||
@@ -1,3 +0,0 @@
|
||||
.ar-LandingContent {
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import LandingContent from "./LandingContent"
|
||||
|
||||
describe("LandingContent", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,87 +0,0 @@
|
||||
import { ArPlacement, ArThemes, LandingContentProps } from "@armco/types"
|
||||
import {
|
||||
Hero,
|
||||
ProductDescriptionTile,
|
||||
getCurrentTheme,
|
||||
useAppSelector,
|
||||
} from ".."
|
||||
import "./LandingContent.component.scss"
|
||||
|
||||
const toolsAndProducts = [
|
||||
{
|
||||
name: "Icons & Fonts",
|
||||
description: [
|
||||
"Dive into a world of creativity with our diverse icon assets and fonts.",
|
||||
],
|
||||
image: "",
|
||||
imagePlacement: ArPlacement.RIGHT,
|
||||
},
|
||||
{
|
||||
name: "Task Manager and Config Manager",
|
||||
description: [
|
||||
"Streamline project management and configuration tasks effortlessly.",
|
||||
"Master project management and configurations with our intuitive tools.",
|
||||
],
|
||||
image: "",
|
||||
imagePlacement: ArPlacement.RIGHT,
|
||||
},
|
||||
{
|
||||
name: "Components Library",
|
||||
description: [
|
||||
"Indulge in Unmatched Creativity with Our Opinionated and highly customizable Component Library",
|
||||
"Discover a component library like no other, meticulously crafted and opinionated for optimal performance. Our library isn't just about components; it's a playground for creators. Immerse yourself in the richness of our carefully curated elements, each designed with a distinct perspective on aesthetics and functionality.",
|
||||
"Explore a library that doesn't just follow trends but sets them. Elevate your projects with components that speak your language, and let your creativity soar in a space that celebrates individuality. Welcome to a component library where opinion meets innovation, and your designs become a masterpiece.",
|
||||
],
|
||||
image: "",
|
||||
imagePlacement: ArPlacement.RIGHT,
|
||||
},
|
||||
{
|
||||
name: "Analytics Collector and Viewer",
|
||||
description: [
|
||||
"Showcase the analytics capabilities for data-driven decisions.",
|
||||
"Turn data into insights with our integrated analytics tools.",
|
||||
],
|
||||
image: "",
|
||||
imagePlacement: ArPlacement.RIGHT,
|
||||
},
|
||||
{
|
||||
name: "IAM",
|
||||
description: [
|
||||
"Explain the identity and access management features.",
|
||||
"Manage identities and access with ease using our robust IAM features.",
|
||||
],
|
||||
image: "",
|
||||
imagePlacement: ArPlacement.RIGHT,
|
||||
},
|
||||
{
|
||||
name: "APIs",
|
||||
description: [
|
||||
"Highlight the flexibility of APIs for integration into various workflows.",
|
||||
"Harness the flexibility of APIs for seamless integration into your development workflow.",
|
||||
],
|
||||
image: "",
|
||||
imagePlacement: ArPlacement.RIGHT,
|
||||
},
|
||||
]
|
||||
|
||||
const LandingContent = (props: LandingContentProps): JSX.Element => {
|
||||
const theme = useAppSelector<ArThemes>(getCurrentTheme)
|
||||
return (
|
||||
<div className="ar-LandingContent">
|
||||
<Hero classes="mb-3" theme={theme} />
|
||||
<div className="ar-LandingContent__tiles-container container">
|
||||
{toolsAndProducts.map((item) => {
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<ProductDescriptionTile classes="mb-3" {...item} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LandingContent
|
||||
@@ -1,3 +0,0 @@
|
||||
import LandingContent from "./LandingContent"
|
||||
|
||||
export default LandingContent
|
||||
@@ -1,10 +0,0 @@
|
||||
.ar-LottieEditor {
|
||||
.ar-LottieEditor__header-download {
|
||||
background-color: var(--ar-bg);
|
||||
color: var(--ar-colora);
|
||||
}
|
||||
.ar-LottieEditor__lottie-preview {
|
||||
background-color: var(--ar-bg);
|
||||
color: var(--ar-color);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import LottieEditor from "./LottieEditor"
|
||||
|
||||
describe("LottieEditor", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,111 +0,0 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { useLocation } from "react-router-dom"
|
||||
import { Controls, Player } from "@lottiefiles/react-lottie-player"
|
||||
import { Animation, ArButtonVariants, LottieEditorProps } from "@armco/types"
|
||||
import {
|
||||
API_CONFIG,
|
||||
Button,
|
||||
ENDPOINTS,
|
||||
Helper,
|
||||
LottieHelper,
|
||||
Network,
|
||||
} from ".."
|
||||
import "./LottieEditor.component.scss"
|
||||
|
||||
const LottieEditor = (props: LottieEditorProps): JSX.Element => {
|
||||
const { selectedPresets } = props
|
||||
const location = useLocation()
|
||||
const icon = location.state?.icon
|
||||
const iconStyles = location.state?.iconStyles
|
||||
const [lottieJson, setLottieJson] = useState<Animation>()
|
||||
|
||||
useEffect(() => {
|
||||
const base = LottieHelper.renderStaticImage(icon, iconStyles)
|
||||
if (!selectedPresets || selectedPresets.length === 0) {
|
||||
setLottieJson(base)
|
||||
} else {
|
||||
selectedPresets.forEach(
|
||||
(preset) =>
|
||||
preset.property &&
|
||||
preset.raw &&
|
||||
LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: preset.property,
|
||||
juice: preset.raw,
|
||||
base,
|
||||
}),
|
||||
)
|
||||
setLottieJson(base)
|
||||
}
|
||||
}, [selectedPresets])
|
||||
|
||||
return (
|
||||
<div className="ar-LottieEditor h-100 w-100 overflow-auto position-relative d-flex flex-column">
|
||||
<div className="ar-LottieEditor__header-download py-2 px-3 mb-2 border">
|
||||
<div className="row">
|
||||
<div className="col d-none d-md-flex align-items-center">
|
||||
<Button
|
||||
classes="ms-auto h-100"
|
||||
content="Download"
|
||||
variant={ArButtonVariants.WARNING}
|
||||
splitOptions={[
|
||||
{
|
||||
label: "MP4",
|
||||
onClick: () => {
|
||||
Network.post(
|
||||
API_CONFIG.STATIC_HOST[process.env.NODE_ENV] +
|
||||
ENDPOINTS.STATIC.LOTTIE.ROOT +
|
||||
ENDPOINTS.STATIC.LOTTIE.TOMP4,
|
||||
{ lottie: lottieJson, name: location.state?.name },
|
||||
).then((res) => {
|
||||
if (res.status === 200) {
|
||||
Helper.download(res.body, location.state?.name + ".mp4")
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "GIF",
|
||||
onClick: () => {
|
||||
Network.post(
|
||||
API_CONFIG.STATIC_HOST[process.env.NODE_ENV] +
|
||||
ENDPOINTS.STATIC.LOTTIE.ROOT +
|
||||
ENDPOINTS.STATIC.LOTTIE.TOGIF,
|
||||
{ lottie: lottieJson, name: location.state?.name },
|
||||
).then((res) => {
|
||||
if (res.status === 200) {
|
||||
Helper.download(res.body, location.state?.name + ".gif")
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "PNG",
|
||||
},
|
||||
{
|
||||
label: "JSON",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ar-LottieEditor__lottie-preview-wrapper d-flex flex-grow-1 justify-content-between">
|
||||
<div className="ar-LottieEditor__lottie-preview py-2 px-3 border flex-center w-100">
|
||||
<Player
|
||||
autoplay
|
||||
loop
|
||||
src={lottieJson as object}
|
||||
style={{ height: "300px", width: "300px" }}
|
||||
>
|
||||
<Controls
|
||||
visible={true}
|
||||
buttons={["play", "repeat", "frame", "debug"]}
|
||||
/>
|
||||
</Player>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LottieEditor
|
||||
@@ -1,3 +0,0 @@
|
||||
import LottieEditor from "./LottieEditor"
|
||||
|
||||
export default LottieEditor
|
||||
@@ -1,18 +0,0 @@
|
||||
.ar-LottieEditorTools {
|
||||
width: calc(20rem + 2px + 1rem); // add border and padding
|
||||
|
||||
.ar-LottieEditorTools__header__close-button {
|
||||
font-size: 0.7rem !important;
|
||||
color: rgb(154, 92, 92) !important;
|
||||
transition: color 0.4s;
|
||||
|
||||
&:hover {
|
||||
color: rgb(242, 51, 51) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ar-LottieEditorTools__tab-content {
|
||||
height: calc(100% - 2rem - 1px - 8px); // subtract header height, bottom border and bottom margin
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import LottieEditorTools from "./LottieEditorTools"
|
||||
|
||||
describe("LottieEditorTools", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,336 +0,0 @@
|
||||
import { ReactNode, useEffect, useState } from "react"
|
||||
import { v4 as uuid } from "uuid"
|
||||
import {
|
||||
AccordionExpansionPanelProps,
|
||||
ArAccordionStyles,
|
||||
ArAccordionVariants,
|
||||
ArAnimationProperty,
|
||||
ArButtonVariants,
|
||||
ArSizes,
|
||||
ArTabType,
|
||||
LottieEditorToolsProps,
|
||||
PresetDefinition,
|
||||
TabProps,
|
||||
} from "@armco/types"
|
||||
import {
|
||||
Accordion,
|
||||
Button,
|
||||
fadeOut,
|
||||
LottieHelper,
|
||||
LottieTile,
|
||||
Pillbox,
|
||||
scaleDown,
|
||||
scaleUp,
|
||||
slideInFromTop,
|
||||
slideInFromLeft,
|
||||
setRightPanelContent,
|
||||
slideOutToLeft,
|
||||
slideOutToTop,
|
||||
slideInFromBottom,
|
||||
slideOutToBottom,
|
||||
slideInFromRight,
|
||||
slideOutToRight,
|
||||
fadeIn,
|
||||
rotateClockWise,
|
||||
rotateAntiClockWise,
|
||||
TabBar,
|
||||
useAppDispatch,
|
||||
} from ".."
|
||||
import "./LottieEditorTools.component.scss"
|
||||
|
||||
const editorToolsTabItems = [
|
||||
{
|
||||
label: "Customize",
|
||||
id: uuid(),
|
||||
},
|
||||
{
|
||||
label: "Presets",
|
||||
id: uuid(),
|
||||
isActive: true,
|
||||
},
|
||||
]
|
||||
|
||||
export const presets: Array<{
|
||||
id: string
|
||||
title: string
|
||||
items: Array<PresetDefinition>
|
||||
content: ReactNode
|
||||
}> = [
|
||||
{
|
||||
id: uuid(),
|
||||
title: "Slide In",
|
||||
content: null,
|
||||
items: [
|
||||
{
|
||||
name: "slideinfromleft",
|
||||
from: "left",
|
||||
raw: slideInFromLeft,
|
||||
property: ArAnimationProperty.POSITION,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.POSITION,
|
||||
juice: slideInFromLeft,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "slideinfromright",
|
||||
from: "right",
|
||||
raw: slideInFromRight,
|
||||
property: ArAnimationProperty.POSITION,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.POSITION,
|
||||
juice: slideInFromRight,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "slideinfromtop",
|
||||
from: "top",
|
||||
raw: slideInFromTop,
|
||||
property: ArAnimationProperty.POSITION,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.POSITION,
|
||||
juice: slideInFromTop,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "slideinfrombottom",
|
||||
from: "bottom",
|
||||
raw: slideInFromBottom,
|
||||
property: ArAnimationProperty.POSITION,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.POSITION,
|
||||
juice: slideInFromBottom,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: uuid(),
|
||||
title: "Slide Out",
|
||||
content: null,
|
||||
items: [
|
||||
{
|
||||
name: "slideouttoleft",
|
||||
to: "left",
|
||||
raw: slideOutToLeft,
|
||||
property: ArAnimationProperty.POSITION,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.POSITION,
|
||||
juice: slideOutToLeft,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "slideouttoright",
|
||||
to: "right",
|
||||
raw: slideOutToRight,
|
||||
property: ArAnimationProperty.POSITION,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.POSITION,
|
||||
juice: slideOutToRight,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "slideouttotop",
|
||||
to: "top",
|
||||
raw: slideOutToTop,
|
||||
property: ArAnimationProperty.POSITION,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.POSITION,
|
||||
juice: slideOutToTop,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "slideouttobottom",
|
||||
to: "bottom",
|
||||
raw: slideOutToBottom,
|
||||
property: ArAnimationProperty.POSITION,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.POSITION,
|
||||
juice: slideOutToBottom,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: uuid(),
|
||||
title: "Fade",
|
||||
content: null,
|
||||
items: [
|
||||
{
|
||||
name: "fadein",
|
||||
raw: fadeIn,
|
||||
property: ArAnimationProperty.OPACITY,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.OPACITY,
|
||||
juice: fadeIn,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "fadeout",
|
||||
raw: fadeOut,
|
||||
property: ArAnimationProperty.OPACITY,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.OPACITY,
|
||||
juice: fadeOut,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: uuid(),
|
||||
title: "Rotate",
|
||||
content: null,
|
||||
items: [
|
||||
{
|
||||
name: "rotateClockWise",
|
||||
raw: rotateClockWise,
|
||||
property: ArAnimationProperty.ROTATE,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.ROTATE,
|
||||
juice: rotateClockWise,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "rotateAntiClockWise",
|
||||
raw: rotateAntiClockWise,
|
||||
property: ArAnimationProperty.ROTATE,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.ROTATE,
|
||||
juice: rotateAntiClockWise,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: uuid(),
|
||||
title: "Scale",
|
||||
content: null,
|
||||
items: [
|
||||
{
|
||||
name: "expand",
|
||||
raw: scaleUp,
|
||||
property: ArAnimationProperty.SCALE,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.SCALE,
|
||||
juice: scaleUp,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "shrink",
|
||||
raw: scaleDown,
|
||||
property: ArAnimationProperty.SCALE,
|
||||
lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
property: ArAnimationProperty.SCALE,
|
||||
juice: scaleDown,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// name: "slidein",
|
||||
// lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
// property: "",
|
||||
// juice: slideInFromLeft,
|
||||
// }),
|
||||
// },
|
||||
// {
|
||||
// name: "slidein",
|
||||
// lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
// property: "",
|
||||
// juice: slideInFromLeft,
|
||||
// }),
|
||||
// },
|
||||
// {
|
||||
// name: "slidein",
|
||||
// lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
// property: "",
|
||||
// juice: slideInFromLeft,
|
||||
// }),
|
||||
// },
|
||||
// {
|
||||
// name: "slidein",
|
||||
// lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
// property: "",
|
||||
// juice: slideInFromLeft,
|
||||
// }),
|
||||
// },
|
||||
// {
|
||||
// name: "slidein",
|
||||
// lottie: LottieHelper.injectAnimationInBaseLottieBasic({
|
||||
// property: "",
|
||||
// juice: slideInFromLeft,
|
||||
// }),
|
||||
// },
|
||||
]
|
||||
|
||||
const LottieEditorTools = (props: LottieEditorToolsProps): JSX.Element => {
|
||||
const { setSelectedPresets } = props
|
||||
const [lottieTools, setLottieTools] = useState<
|
||||
Array<AccordionExpansionPanelProps>
|
||||
>([])
|
||||
const dispatch = useAppDispatch()
|
||||
const [activeTab, setActiveTab] = useState<TabProps>(editorToolsTabItems[1])
|
||||
|
||||
useEffect(() => {
|
||||
setLottieTools([
|
||||
{
|
||||
id: uuid(),
|
||||
title: "Effects",
|
||||
content: <Pillbox data={[]} onChange={() => {}} />,
|
||||
},
|
||||
])
|
||||
}, [])
|
||||
|
||||
presets.forEach(
|
||||
(preset) =>
|
||||
(preset.content = (
|
||||
<div className="py-2">
|
||||
{preset.items.map((preset, index) => (
|
||||
<LottieTile
|
||||
key={"lottie-preset-" + index}
|
||||
preset={preset as PresetDefinition}
|
||||
setSelectedPresets={setSelectedPresets}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)),
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="ar-LottieEditorTools h-100 px-2">
|
||||
<h6 className="ar-LottieEditorTools__header border-bottom d-flex">
|
||||
<TabBar
|
||||
classes="ar-LottieEditorTools__header__tab-bar"
|
||||
data={editorToolsTabItems}
|
||||
variant={ArTabType.MINIMAL}
|
||||
onTabSelected={(id, data) => setActiveTab(data)}
|
||||
/>
|
||||
<Button
|
||||
classes="ar-LottieEditorTools__header__close-button ms-auto"
|
||||
size={ArSizes.SMALL}
|
||||
variant={ArButtonVariants.LINK}
|
||||
content="Close"
|
||||
onClick={() => dispatch(setRightPanelContent({ name: "" }))}
|
||||
/>
|
||||
</h6>
|
||||
<div className="ar-LottieEditorTools__tab-content">
|
||||
{activeTab?.id === editorToolsTabItems[1].id ? (
|
||||
<Accordion
|
||||
data={presets}
|
||||
appearance={ArAccordionStyles.STACKED}
|
||||
variant={ArAccordionVariants.LIMITED}
|
||||
firstExpanded
|
||||
/>
|
||||
) : (
|
||||
<Accordion
|
||||
data={lottieTools}
|
||||
appearance={ArAccordionStyles.STACKED}
|
||||
variant={ArAccordionVariants.LIMITED}
|
||||
firstExpanded
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LottieEditorTools
|
||||
@@ -1,3 +0,0 @@
|
||||
import LottieEditorTools from "./LottieEditorTools"
|
||||
|
||||
export default LottieEditorTools
|
||||
@@ -1,3 +0,0 @@
|
||||
.ar-LottieLayers {
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import LottieLayers from "./LottieLayers"
|
||||
|
||||
describe("LottieLayers", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,32 +0,0 @@
|
||||
import {
|
||||
ArButtonVariants,
|
||||
ArPopoverSlots,
|
||||
ArPopoverTriggers,
|
||||
ArSizes,
|
||||
LottieLayersProps,
|
||||
} from "@armco/types"
|
||||
import { Button, Popover } from ".."
|
||||
import TreeList from "../atoms/TreeList"
|
||||
import "./LottieLayers.component.scss"
|
||||
|
||||
const LottieLayers = (props: LottieLayersProps): JSX.Element => {
|
||||
return (
|
||||
<div className="ar-LottieLayers py-1 px-2 w-100">
|
||||
{/* <Popover trigger={ArPopoverTriggers.HOVER}>
|
||||
<Button
|
||||
slot={ArPopoverSlots.ANCHOR}
|
||||
content="Add"
|
||||
variant={ArButtonVariants.SUCCESS}
|
||||
size={ArSizes.XSMALL}
|
||||
/>
|
||||
<div slot={ArPopoverSlots.POPOVER}>
|
||||
Add a new layer or one of the lottie configurations from the panel
|
||||
towards the right
|
||||
</div>
|
||||
</Popover> */}
|
||||
<TreeList data={[]} noSearch />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LottieLayers
|
||||
@@ -1,3 +0,0 @@
|
||||
import LottieLayers from "./LottieLayers"
|
||||
|
||||
export default LottieLayers
|
||||
@@ -1,25 +0,0 @@
|
||||
.ar-Main {
|
||||
.ar-Drawer {
|
||||
transition: width 0.3s;
|
||||
width: 15%;
|
||||
&.collapsed {
|
||||
width: 3.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 576px) {
|
||||
.ar-Drawer {
|
||||
position: fixed !important;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 75%;
|
||||
z-index: 1;
|
||||
background-color: var(--ar-bg);
|
||||
|
||||
&.collapsed {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import Main from "./Main"
|
||||
|
||||
describe("Main", () => {
|
||||
it("renders without error", () => {})
|
||||
})
|
||||
@@ -1,53 +0,0 @@
|
||||
import { ArPlacement, MainProps } from "@armco/types"
|
||||
import { ErrorBoundary, SidePanel, Content, Drawer } from ".."
|
||||
import "./Main.component.scss"
|
||||
|
||||
const Main = (props: MainProps): JSX.Element => {
|
||||
const {
|
||||
contentClasses,
|
||||
drawerContent,
|
||||
mainContent,
|
||||
hideSidepanelCloseButton,
|
||||
rightPanelContent,
|
||||
leftPanelContent,
|
||||
rightPanelHeader,
|
||||
leftPanelHeader,
|
||||
} = props
|
||||
return (
|
||||
<main className="ar-Main d-flex flex-grow-1 w-100">
|
||||
{drawerContent && (
|
||||
<Drawer classes="d-flex h-100" isCollapsible>
|
||||
{drawerContent}
|
||||
</Drawer>
|
||||
)}
|
||||
<SidePanel
|
||||
key="left-panel"
|
||||
header={leftPanelHeader || "Header Name"}
|
||||
placement={ArPlacement.LEFT}
|
||||
componentName={leftPanelContent ? leftPanelContent.name : ""}
|
||||
componentProps={leftPanelContent ? leftPanelContent.props : {}}
|
||||
component={leftPanelContent ? leftPanelContent.component : null}
|
||||
hideCloseButton={hideSidepanelCloseButton}
|
||||
/>
|
||||
<ErrorBoundary>
|
||||
<Content
|
||||
classes={`flex-center flex-grow-1 position-relative overflow-auto${
|
||||
contentClasses ? " " + contentClasses : ""
|
||||
}`}
|
||||
>
|
||||
{mainContent}
|
||||
</Content>
|
||||
</ErrorBoundary>
|
||||
<SidePanel
|
||||
key="right-panel"
|
||||
header={rightPanelHeader}
|
||||
componentName={rightPanelContent ? rightPanelContent.name : ""}
|
||||
componentProps={rightPanelContent ? rightPanelContent.props : {}}
|
||||
component={rightPanelContent ? rightPanelContent.component : null}
|
||||
hideCloseButton={hideSidepanelCloseButton}
|
||||
/>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export default Main
|
||||
@@ -1,3 +0,0 @@
|
||||
import Main from "./Main"
|
||||
|
||||
export default Main
|
||||
@@ -1,9 +0,0 @@
|
||||
.ar-NamespaceInfoBox {
|
||||
.ar-NamespaceInfoBox__namespace-link {
|
||||
border: var(--ar-border);
|
||||
background-color: var(--ar-bg-hover);
|
||||
border-radius: 3px;
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react"
|
||||
import NamespaceInfoBox from "./NamespaceInfoBox"
|
||||
|
||||
describe("NamespaceInfoBox", () => {
|
||||
it("renders without error", () => {
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,128 +0,0 @@
|
||||
import { useRef, useState } from "react"
|
||||
import { v4 as uuid } from "uuid"
|
||||
import hljs from "highlight.js/lib/core"
|
||||
import javascript from "highlight.js/lib/languages/javascript"
|
||||
import { NamespaceInfoBoxProps } from "@armco/types"
|
||||
import {
|
||||
Alert,
|
||||
API_CONFIG,
|
||||
ENDPOINTS,
|
||||
Helper,
|
||||
Link,
|
||||
LoadableIcon,
|
||||
Text,
|
||||
} from ".."
|
||||
import "highlight.js/styles/github.css"
|
||||
import "./NamespaceInfoBox.component.scss"
|
||||
|
||||
hljs.registerLanguage("javascript", javascript)
|
||||
|
||||
const NamespaceInfoBox = (props: NamespaceInfoBoxProps): JSX.Element => {
|
||||
const { namespace } = props
|
||||
const codeRef = useRef<HTMLPreElement>(null)
|
||||
const [displayed, show] = useState<{ displayed: boolean }>()
|
||||
const [message, setMessage] = useState<string>()
|
||||
const helpText =
|
||||
"Now that your namespace has been created you can integrate it with your project using the below endpoint"
|
||||
const nsLink =
|
||||
API_CONFIG.CONFIG[process.env.NODE_ENV] +
|
||||
ENDPOINTS.NAMESPACE.ROOT +
|
||||
ENDPOINTS.NAMESPACE.FETCH +
|
||||
namespace._id
|
||||
const nsAllLink =
|
||||
API_CONFIG.CONFIG[process.env.NODE_ENV] +
|
||||
ENDPOINTS.NAMESPACE.ROOT +
|
||||
ENDPOINTS.NAMESPACE.FETCHALL
|
||||
const codeStr = `
|
||||
// API to fetch configurations for the namespace ${namespace.name}
|
||||
fetch("${nsLink}")
|
||||
.then((res) => {
|
||||
if (res.status === 200) {
|
||||
console.log(res.body.namespace);
|
||||
}
|
||||
})
|
||||
`
|
||||
const code = hljs.highlight("javascript", codeStr).value
|
||||
|
||||
return (
|
||||
<div className="ar-NamespaceInfoBox w-100">
|
||||
<div className="ar-NamespaceInfoBox__content d-flex flex-1 flex-column h-100">
|
||||
<header className="ar-NamespaceInfoBox__header w-100 border-bottom px-3 py-2 fw-bold f4 flex-v-center">
|
||||
{namespace.name}
|
||||
</header>
|
||||
<main className="ar-NamespaceInfoBox__body flex-1 overflow-auto">
|
||||
<div className="ar-NameSpaceModal__body p-3 h-100 flex-center flex-column">
|
||||
<Text
|
||||
classes="ar-help-text mb-3 w-100 text-wrap"
|
||||
descriptor={{
|
||||
id: "temp-text-id",
|
||||
order: 0,
|
||||
name: "Text",
|
||||
text: helpText,
|
||||
chunks: {},
|
||||
}}
|
||||
/>
|
||||
<Link
|
||||
to={nsLink}
|
||||
label={nsLink}
|
||||
classes="ar-NamespaceInfoBox__namespace-link px-3 py-2 mb-3"
|
||||
onClick={() => {
|
||||
Helper.copyOrPrompt(nsLink, () => {
|
||||
show({ displayed: true })
|
||||
setMessage("Link Copied!")
|
||||
})
|
||||
}}
|
||||
preemptNavigation
|
||||
/>
|
||||
<small className="ar-help-text mb-3">
|
||||
Or to fetch all of your namespaces
|
||||
</small>
|
||||
<Link
|
||||
to={nsAllLink}
|
||||
label={nsAllLink}
|
||||
classes="ar-NamespaceInfoBox__namespace-link px-3 py-2 mb-3"
|
||||
onClick={() => {
|
||||
Helper.copyOrPrompt(nsAllLink, () => {
|
||||
show({ displayed: true })
|
||||
setMessage("Link Copied!")
|
||||
})
|
||||
}}
|
||||
preemptNavigation
|
||||
/>
|
||||
<div className="ar-NamespaceInfoBox__sample-code border p-2 w-100 overflow-auto position-relative">
|
||||
<LoadableIcon
|
||||
classes="ar-NamespaceInfoBox__copy-button position-absolute"
|
||||
icon="md/MdContentCopy"
|
||||
color="grey"
|
||||
hoverColor="black"
|
||||
onClick={() => {
|
||||
const code = codeRef.current?.innerText
|
||||
Helper.copyOrPrompt(code, () => {
|
||||
show({ displayed: true })
|
||||
setMessage(
|
||||
"Copied code for integrating your configurations",
|
||||
)
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<pre>
|
||||
<code
|
||||
dangerouslySetInnerHTML={{ __html: code }}
|
||||
ref={codeRef}
|
||||
/>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Alert
|
||||
show={displayed?.displayed}
|
||||
message={message || "Link Copied!"}
|
||||
uid={uuid()}
|
||||
closeable
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NamespaceInfoBox
|
||||
@@ -1,3 +0,0 @@
|
||||
import NamespaceInfoBox from "./NamespaceInfoBox"
|
||||
|
||||
export default NamespaceInfoBox
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user