Refactor imports, update dependencies, add Jenkinsfile
Some checks failed
armco-org/components-viewer/pipeline/head There was a failure building this commit
Some checks failed
armco-org/components-viewer/pipeline/head There was a failure building this commit
This commit is contained in:
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Dependencies
|
||||
node_modules
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
# Swap the comments on the following lines if you don't wish to use zero-installs
|
||||
# Documentation here: https://yarnpkg.com/features/zero-installs
|
||||
!.yarn/cache
|
||||
#.pnp.*
|
||||
|
||||
# Testing
|
||||
coverage
|
||||
|
||||
# Production
|
||||
build
|
||||
|
||||
# Miscellaneous
|
||||
*.local
|
||||
.DS_Store
|
||||
|
||||
analyticsrc.json
|
||||
3
Jenkinsfile
vendored
Normal file
3
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
@Library('jenkins-shared@main') _
|
||||
|
||||
kanikoPipeline(repoName: 'components-viewer', branch: env.BRANCH_NAME ?: 'main')
|
||||
1
README.md
Normal file
1
README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Armco Template for the tech stack: React, TS, Dart Sass, Redux Tookkit, react-redux, react browser routing, TS based plop generator
|
||||
36
build-tools/build.sh
Executable file
36
build-tools/build.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the directory of the current script
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Default values
|
||||
DEV_FLAG=""
|
||||
|
||||
# Parse arguments
|
||||
for arg in "$@"
|
||||
do
|
||||
case $arg in
|
||||
--dev)
|
||||
DEV_FLAG="--dev"
|
||||
shift # Remove --dev from processing
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "[BUILD:SH] Dev flag is: $DEV_FLAG"
|
||||
echo "[BUILD:SH] Removing build if exists"
|
||||
rm -rf build
|
||||
echo "[BUILD:SH] Checking TS Types"
|
||||
npx tsc
|
||||
echo "[BUILD:SH] Initiating build..."
|
||||
# Conditionally use vite-dev.config.ts if --dev flag is present
|
||||
if [ "$DEV_FLAG" == "--dev" ]; then
|
||||
vite build --config vite-dev.config.ts
|
||||
else
|
||||
vite build
|
||||
fi
|
||||
|
||||
echo "[BUILD:SH] Running post processor scripts..."
|
||||
# Run Post processors: Update style imports in .js files, create component modules
|
||||
node "$SCRIPT_DIR/post-processor.js" build/cjs $DEV_FLAG
|
||||
node "$SCRIPT_DIR/post-processor.js" build/es $DEV_FLAG
|
||||
35
build-tools/generate-module.js
Normal file
35
build-tools/generate-module.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import { promises as fs } from "fs"
|
||||
import { dirname, resolve } from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
|
||||
async function generateModule(fileName, isDev) {
|
||||
if (
|
||||
fileName.endsWith(".js") &&
|
||||
fileName.indexOf("-chunk") === -1 &&
|
||||
fileName !== "index.js"
|
||||
) {
|
||||
const dir = fileName.slice(0, -3)
|
||||
const name = `@armco/layout/${dir}`
|
||||
const packageJsonContent = {
|
||||
name,
|
||||
main: `../${isDev ? "build/" : ""}cjs/${dir}.js`,
|
||||
module: `../${isDev ? "build/" : ""}es/${dir}.js`,
|
||||
types: `../${isDev ? "build/" : ""}types/${dir}.d.ts`,
|
||||
}
|
||||
const dirPath = resolve(__dirname, `../${isDev ? "" : "build/"}${dir}`)
|
||||
try {
|
||||
await fs.mkdir(dirPath, { recursive: true })
|
||||
await fs.writeFile(
|
||||
resolve(dirPath, "package.json"),
|
||||
JSON.stringify(packageJsonContent, null, 2),
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(`Error processing directory ${dirPath}:`, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default generateModule
|
||||
24
build-tools/post-processor.js
Normal file
24
build-tools/post-processor.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { readdir } from "fs/promises"
|
||||
import generateModule from "./generate-module.js"
|
||||
|
||||
async function postProcessor(dir, isDev) {
|
||||
try {
|
||||
const files = await readdir(dir)
|
||||
await Promise.all(
|
||||
files.map(async (file) => {
|
||||
await generateModule(file, isDev)
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(`Error processing directory ${dir}:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
const targetDir = process.argv[2]
|
||||
|
||||
if (targetDir) {
|
||||
postProcessor(targetDir, process.argv.includes("--dev"))
|
||||
} else {
|
||||
console.error("Please provide the build directory to run post processor on.")
|
||||
process.exit(1)
|
||||
}
|
||||
14
index.html
Normal file
14
index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!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>React Redux App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
8185
package-lock.json
generated
Normal file
8185
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
137
package.json
Normal file
137
package.json
Normal file
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"name": "@armco/components-viewer",
|
||||
"description": "Components Viewer/Tester for React Components Library",
|
||||
"version": "0.0.49",
|
||||
"type": "module",
|
||||
"author": "Armco (@restruct-corporate-advantage)",
|
||||
"types": "./build/types/ComponentsViewer.d.ts",
|
||||
"main": "./build/cjs/ComponentsViewer.js",
|
||||
"module": "./build/es/ComponentsViewer.js",
|
||||
"scripts": {
|
||||
"dev": "vite --config vite-dev.config.ts",
|
||||
"start": "NODE_ENV=production vite",
|
||||
"build": "rm -rf build && tsc && vite build",
|
||||
"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",
|
||||
"publish:sh": "./publish.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lottiefiles/react-lottie-player": "^3.5.3",
|
||||
"@tanstack/react-table": "^8.21.2",
|
||||
"d3": "^7.9.0",
|
||||
"highcharts": "^11.2.0",
|
||||
"highcharts-react-official": "^3.2.1",
|
||||
"highlight.js": "^11.8.0",
|
||||
"lottie-react": "^2.4.0",
|
||||
"lottie-web": "^5.12.2",
|
||||
"moment": "^2.29.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@armco/components": "^0.0.60",
|
||||
"@armco/configs": "^0.0.11",
|
||||
"@armco/types": "^0.0.18",
|
||||
"@armco/utils": "^0.0.29",
|
||||
"@reduxjs/toolkit": "^1.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react-app-polyfill": "^3.0.0",
|
||||
"react-bootstrap": "^2.7.4",
|
||||
"react-dev-utils": "^12.0.1",
|
||||
"react-dnd": ">=16.0.0",
|
||||
"react-dnd-html5-backend": ">=16.0.0",
|
||||
"react-dnd-touch-backend": ">=16.0.0",
|
||||
"react-draggable": "^4.4.6",
|
||||
"react-redux": "^8.0.1",
|
||||
"react-resizable": "^3.0.5",
|
||||
"react-router-dom": "^6.13.0",
|
||||
"resize-observer-polyfill": "^1.5.1"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest",
|
||||
"plugin:storybook/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"react/jsx-no-target-blank": "off"
|
||||
}
|
||||
},
|
||||
"prettier": "prettier-config-nick",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ReStruct-Corporate-Advantage/components-viewer.git"
|
||||
},
|
||||
"keywords": [
|
||||
"react",
|
||||
"vite",
|
||||
"reduxToolkit",
|
||||
"sass",
|
||||
"components",
|
||||
"atomic",
|
||||
"building-blocks",
|
||||
"foundation"
|
||||
],
|
||||
"files": [
|
||||
"build"
|
||||
],
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ReStruct-Corporate-Advantage/components-viewer/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ReStruct-Corporate-Advantage/components-viewer#readme",
|
||||
"devDependencies": {
|
||||
"@armco/avatar": "^0.0.6",
|
||||
"@armco/calendar": "^0.0.8",
|
||||
"@armco/components": "^0.0.60",
|
||||
"@armco/configs": "^0.0.15",
|
||||
"@armco/imageeditor": "^0.0.8",
|
||||
"@armco/sam-editor": "^0.0.5",
|
||||
"@armco/shared-components": "^0.0.61",
|
||||
"@armco/types": "^0.0.18",
|
||||
"@armco/uploader": "^0.0.6",
|
||||
"@armco/utils": "^0.0.31",
|
||||
"@reduxjs/toolkit": "^2.11.2",
|
||||
"@types/node": "^24.0.0",
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@vitejs/plugin-react": "^5.1.0",
|
||||
"ajv": "^8.12.0",
|
||||
"glob": "^11.0.3",
|
||||
"react": "^19.2.3",
|
||||
"react-bootstrap": "^2.10.10",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dnd-touch-backend": "^16.0.1",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-draggable": "^4.5.0",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-resizable": "^3.1.3",
|
||||
"react-router-dom": "^7.11.0",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"sass-embedded": "^1.89.2",
|
||||
"typescript": "^5.9.3",
|
||||
"uuid": "^13.0.0",
|
||||
"vite": "^7.3.0",
|
||||
"vite-plugin-dts": "^4.5.4",
|
||||
"vite-plugin-externalize-deps": "^0.10.0",
|
||||
"vite-plugin-lib-inject-css": "^2.2.2",
|
||||
"vitest": "^4.0.8"
|
||||
}
|
||||
}
|
||||
34
publish.sh
Executable file
34
publish.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
|
||||
semver=${1:-patch}
|
||||
|
||||
set -e
|
||||
npm --no-git-tag-version version ${semver}
|
||||
npm run build
|
||||
cp package.json build/
|
||||
|
||||
# Use Node.js for portable package.json normalization
|
||||
PKG_PATH="$(pwd)/build/package.json" node - <<'EOF'
|
||||
const fs = require('fs');
|
||||
const path = process.env.PKG_PATH;
|
||||
const pkg = JSON.parse(fs.readFileSync(path, 'utf8'));
|
||||
pkg.private = false;
|
||||
delete pkg.scripts;
|
||||
delete pkg.devDependencies;
|
||||
|
||||
if (!pkg.files) pkg.files = ['*'];
|
||||
else pkg.files = pkg.files.map(x => x === 'build' ? '*' : x);
|
||||
|
||||
['main','module','types'].forEach(k => {
|
||||
if (pkg[k]) {
|
||||
pkg[k] = pkg[k]
|
||||
.replace(/^\.?\/build\//, '')
|
||||
.replace(/^build\//, '');
|
||||
}
|
||||
});
|
||||
|
||||
fs.writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n');
|
||||
EOF
|
||||
|
||||
cd build
|
||||
npm publish --access public --loglevel verbose
|
||||
132
src/ComponentList.tsx
Executable file
132
src/ComponentList.tsx
Executable file
@@ -0,0 +1,132 @@
|
||||
import { ReactNode, useEffect, useState } from "react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { TreeListData } from "./types"
|
||||
import { stringifyUrl } from "@armco/utils/network"
|
||||
import { useTheme } from "@armco/utils/hooks"
|
||||
import { ErrorBoundary, TreeList, TextInput } from "@armco/shared-components"
|
||||
import * as components from "@armco/shared-components"
|
||||
import COMPONENTS from "./components"
|
||||
import { adaptToTreeFromComponentConfig, loadScript } from "./helper"
|
||||
|
||||
const Components: any = { ...components }
|
||||
|
||||
const LIBS: { [key: string]: () => Promise<any> } = {
|
||||
"@armco/components": () => import("@armco/components"),
|
||||
antd: () =>
|
||||
loadScript(
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/antd/5.21.4/antd.min.js",
|
||||
"andtd",
|
||||
),
|
||||
material: () =>
|
||||
loadScript(
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/material-ui/4.12.4/index.js",
|
||||
"MaterialUI",
|
||||
),
|
||||
"ng-bootstrap": () =>
|
||||
loadScript(
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/ng-bootstrap/10.0.0/ng-bootstrap.umd.min.js",
|
||||
"",
|
||||
),
|
||||
"react-bootstrap": () =>
|
||||
loadScript(
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/react-bootstrap/2.10.5/react-bootstrap.min.js",
|
||||
"",
|
||||
),
|
||||
}
|
||||
|
||||
interface ComponentListProps {
|
||||
acceptLibInput?: boolean
|
||||
}
|
||||
|
||||
let formattedTreeData: Array<TreeListData> =
|
||||
adaptToTreeFromComponentConfig(COMPONENTS)
|
||||
|
||||
const ComponentList = (props: ComponentListProps): ReactNode => {
|
||||
const { acceptLibInput } = props
|
||||
const navigate = useNavigate()
|
||||
const { theme } = useTheme()
|
||||
const [components, setComponents] = useState<Array<string>>()
|
||||
|
||||
// TODO: Test code
|
||||
useEffect(() => loadComponents("@armco/components"), [])
|
||||
|
||||
const handleComponentSelect = (treeNode: TreeListData) => {
|
||||
const params = treeNode.data?.props
|
||||
treeNode.data?.component &&
|
||||
navigate(stringifyUrl(`/components/${treeNode.data?.component}`, params))
|
||||
}
|
||||
|
||||
const loadComponents = (lib: string) => {
|
||||
if (LIBS[lib])
|
||||
LIBS[lib]()?.then((module) => {
|
||||
const importedComponents = Object.keys(module)
|
||||
importedComponents.sort()
|
||||
formattedTreeData = adaptToTreeFromComponentConfig(
|
||||
COMPONENTS,
|
||||
importedComponents,
|
||||
)
|
||||
setComponents(importedComponents)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ar-ComponentList h-100 w-100 px-2">
|
||||
{!components && acceptLibInput ? (
|
||||
<span className="h-100 flex-center flex-column">
|
||||
<div className="flex-center flex-column">
|
||||
<div className="mb-4 fs-6 lh-base">
|
||||
Provide a library of which you want to view the components.
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
If you've installed your library and can see it in node_modules,
|
||||
just provide the name of the library or provide URL of library if
|
||||
it's hosted on a CDN.
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
const lib = (
|
||||
(e.target as HTMLFormElement).elements[
|
||||
"ar-component-id" as any
|
||||
] as HTMLInputElement
|
||||
).value
|
||||
loadComponents(lib)
|
||||
}}
|
||||
>
|
||||
<TextInput
|
||||
id="ar-component-id"
|
||||
placeholder="Load a library..."
|
||||
action={loadComponents}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</span>
|
||||
) : (
|
||||
<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
|
||||
37
src/ComponentsViewer.tsx
Normal file
37
src/ComponentsViewer.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { ReactNode } from "react"
|
||||
import { DndProvider } from "react-dnd"
|
||||
import { HTML5Backend } from "react-dnd-html5-backend"
|
||||
import { TouchBackend } from "react-dnd-touch-backend"
|
||||
import { ErrorBoundary, Main, Modal } from "@armco/shared-components"
|
||||
import { isMobile as checkMobile } from "@armco/utils/helper"
|
||||
import { useModalState, usePanelContent } from "@armco/utils/hooks"
|
||||
import ComponentList from "./ComponentList"
|
||||
import Editor from "./Editor"
|
||||
|
||||
const isMobile = checkMobile()
|
||||
|
||||
const ComponentsViewer = (): ReactNode => {
|
||||
const { panelContent: rightPanelContent } = usePanelContent(false)
|
||||
const { modalState } = useModalState()
|
||||
const backend = isMobile ? TouchBackend : HTML5Backend
|
||||
|
||||
return (
|
||||
<div className="ar-ComponentsViewer d-flex flex-column h-100">
|
||||
<DndProvider backend={backend}>
|
||||
<Main
|
||||
contentClasses="p-2 h-100"
|
||||
drawerContent={<ComponentList acceptLibInput />}
|
||||
mainContent={
|
||||
<ErrorBoundary>
|
||||
<Editor />
|
||||
</ErrorBoundary>
|
||||
}
|
||||
rightPanelContent={rightPanelContent}
|
||||
/>
|
||||
<Modal {...modalState} />
|
||||
</DndProvider>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ComponentsViewer
|
||||
41
src/Editor.scss
Executable file
41
src/Editor.scss
Executable file
@@ -0,0 +1,41 @@
|
||||
.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%;
|
||||
}
|
||||
}
|
||||
250
src/Editor.tsx
Executable file
250
src/Editor.tsx
Executable file
@@ -0,0 +1,250 @@
|
||||
import { useEffect, useRef, useState, ReactNode, ComponentType } from "react"
|
||||
import { useLocation, useNavigate } from "react-router-dom"
|
||||
import {
|
||||
ArDropdownVariants,
|
||||
ArSizes,
|
||||
ArThemes,
|
||||
FrameContentDefinition,
|
||||
FrameContentProps,
|
||||
PRIMITIVES,
|
||||
} from "./types"
|
||||
import * as components from "@armco/shared-components"
|
||||
import { Toggle, Dropdown } from "@armco/shared-components"
|
||||
import { stringifyUrl } from "@armco/utils/network"
|
||||
import { toReadable } from "@armco/utils/helper"
|
||||
import { useTheme } from "@armco/utils/hooks"
|
||||
import COMPONENTS from "./components"
|
||||
import { findComponentDescription } from "./helper"
|
||||
import { DUMMY_CHILDREN, DUMMIES } from "./dummies"
|
||||
import "./Editor.scss"
|
||||
|
||||
const Components: { [key: string]: any } = { ...components }
|
||||
|
||||
const FrameContent = (
|
||||
props: FrameContentProps,
|
||||
): ReactNode => {
|
||||
const { contentDefinition, contentRef } = props
|
||||
const { component, props: componentProps, data } = contentDefinition || {}
|
||||
const background: string = data?.test?.background
|
||||
const SelectedComponent = component as ComponentType<any> | undefined
|
||||
let childRenders: Array<ReactNode> = []
|
||||
if (data?.type === "HOC") {
|
||||
childRenders = data.children.map(
|
||||
(name: string) => DUMMY_CHILDREN[name as keyof object],
|
||||
)
|
||||
}
|
||||
const parsedComponentprops: typeof componentProps = {}
|
||||
componentProps &&
|
||||
Object.keys(componentProps).forEach((key) => {
|
||||
try {
|
||||
parsedComponentprops[key] = JSON.parse(componentProps[key] as string)
|
||||
} catch {
|
||||
parsedComponentprops[key] = componentProps[key]
|
||||
}
|
||||
})
|
||||
return SelectedComponent ? (
|
||||
<div
|
||||
className={`ar-Editor__frame__main h-100 w-100 overflow-auto p-5 d-flex flex-column position-relative${background ? " background" : ""
|
||||
}`}
|
||||
style={{
|
||||
// @ts-ignore
|
||||
backgroundImage: background ? `url(${background})` : "",
|
||||
}}
|
||||
ref={contentRef}
|
||||
id="root"
|
||||
>
|
||||
{data && data.type === "HOC" ? (
|
||||
<SelectedComponent {...parsedComponentprops}>
|
||||
{childRenders}
|
||||
</SelectedComponent>
|
||||
) : (
|
||||
<SelectedComponent {...parsedComponentprops} />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex-center h-100 fs-3">
|
||||
Please use the left panel to view content here
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Editor = (): ReactNode => {
|
||||
const contentRef = useRef<HTMLDivElement | null>(null)
|
||||
const [selectedComponentDefinition, setSelectedComponentDefinition] =
|
||||
useState<FrameContentDefinition | undefined>(undefined)
|
||||
const { theme, setTheme } = useTheme()
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
|
||||
useEffect(() => {
|
||||
if (theme) {
|
||||
document.getElementsByTagName("html")[0].setAttribute("ar-theme", theme)
|
||||
if (selectedComponentDefinition) {
|
||||
if (!selectedComponentDefinition.props) {
|
||||
selectedComponentDefinition.props = {}
|
||||
}
|
||||
selectedComponentDefinition.props.theme = theme
|
||||
setSelectedComponentDefinition({ ...selectedComponentDefinition })
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [theme])
|
||||
|
||||
useEffect(() => {
|
||||
const locationPathArr = location.pathname
|
||||
.split("/")
|
||||
.filter((p: string) => p)
|
||||
const componentName =
|
||||
locationPathArr.length > 1 && locationPathArr[locationPathArr.length - 1]
|
||||
if (componentName) {
|
||||
const { selectedItem } = findComponentDescription(
|
||||
componentName,
|
||||
COMPONENTS,
|
||||
)
|
||||
if (selectedItem) {
|
||||
let props: { [key: string]: string | number | boolean | undefined } = {
|
||||
demo: true,
|
||||
theme,
|
||||
}
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const dummyProps = DUMMIES[componentName]
|
||||
dummyProps && Object.assign(props, dummyProps)
|
||||
if (
|
||||
!selectedComponentDefinition ||
|
||||
selectedComponentDefinition.componentName !== componentName ||
|
||||
JSON.stringify(selectedComponentDefinition.props) !==
|
||||
JSON.stringify(props)
|
||||
) {
|
||||
const SelectedComponent = Components[componentName]
|
||||
setSelectedComponentDefinition({
|
||||
componentName,
|
||||
component: SelectedComponent,
|
||||
props,
|
||||
data: selectedItem,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [location])
|
||||
|
||||
const updateProps = (selectedComponentDefinition: FrameContentDefinition) => {
|
||||
if (selectedComponentDefinition) {
|
||||
const { demo, theme, ...selectedProps } =
|
||||
selectedComponentDefinition.props || {}
|
||||
const nonObjectProps: { [key: string]: any } = {}
|
||||
Object.keys(selectedProps).forEach(
|
||||
(p) =>
|
||||
typeof selectedProps[p] !== "object" &&
|
||||
(nonObjectProps[p] = selectedProps[p]),
|
||||
)
|
||||
const newUrl = stringifyUrl("/", nonObjectProps).slice(1)
|
||||
newUrl !== location.search && navigate(newUrl)
|
||||
}
|
||||
}
|
||||
|
||||
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={toReadable(prop)}
|
||||
onChange={(isChecked: boolean) =>
|
||||
selectedComponentDefinition?.componentName &&
|
||||
updateProps({
|
||||
...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 &&
|
||||
updateProps({
|
||||
...selectedComponentDefinition,
|
||||
props: {
|
||||
...selectedComponentDefinition.props,
|
||||
[prop]: obj.value,
|
||||
},
|
||||
})
|
||||
}}
|
||||
variant={ArDropdownVariants.SELECTIONSASPILLS}
|
||||
placeholder={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}
|
||||
contentRef={contentRef}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Editor
|
||||
1166
src/components.ts
Normal file
1166
src/components.ts
Normal file
File diff suppressed because it is too large
Load Diff
134
src/dummies.tsx
Normal file
134
src/dummies.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import { v4 as uuid } from "uuid"
|
||||
import { Button } from "@armco/shared-components"
|
||||
import { ArPlacement } from "./types"
|
||||
|
||||
// Mock navigator data for Tools & Services demo
|
||||
interface AppNavigatorDataType {
|
||||
label: string
|
||||
items: Array<{ label: string; icon: string; url?: string; description?: string[] }>
|
||||
icon?: string
|
||||
color?: string
|
||||
}
|
||||
|
||||
const tnsNavigator: AppNavigatorDataType | undefined = {
|
||||
label: "Tools & Services",
|
||||
items: [
|
||||
{ label: "Analytics", icon: "analytics", url: "/analytics" },
|
||||
{ label: "Settings", icon: "settings", url: "/settings" },
|
||||
],
|
||||
}
|
||||
|
||||
export const DUMMIES: { [key: string]: { [key: string]: any } } = {
|
||||
Accordion: {
|
||||
data: [
|
||||
{
|
||||
id: uuid(),
|
||||
title: "Slide In",
|
||||
content:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut laoreet tellus ante, et gravida massa egestas ac. Maecenas venenatis dui a eros maximus, vitae fringilla metus ultrices. Etiam at lacinia magna. Vestibulum sagittis felis sed diam ullamcorper, vel pharetra quam facilisis. Suspendisse bibendum ante id risus interdum, vel sodales felis egestas. Mauris vitae dui gravida, dictum sem id, accumsan tellus. Aliquam sodales quam efficitur, congue justo a, consectetur ante. Nunc euismod augue ac ante condimentum tincidunt. Quisque bibendum semper velit. Cras sit amet imperdiet dolor, ac aliquet mi. Morbi elementum magna eros, in venenatis nunc laoreet eget. Etiam at lorem suscipit, interdum augue id, scelerisque risus. Suspendisse at nisi lorem. Morbi libero erat, vestibulum at pulvinar at, ultrices ac turpis. Etiam ac leo gravida, semper quam in, tempus ipsum. Phasellus finibus rhoncus cursus. Maecenas dapibus ex ut congue eleifend. Sed luctus consectetur quam, at malesuada enim condimentum non.",
|
||||
},
|
||||
{
|
||||
id: uuid(),
|
||||
title: "Slide Out",
|
||||
content: (
|
||||
<input
|
||||
type="text"
|
||||
className="p-3 m-3"
|
||||
title="dummy"
|
||||
placeholder="Accordion content..."
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: uuid(),
|
||||
title: "Fade",
|
||||
content:
|
||||
"Donec varius lorem vel orci aliquam, nec venenatis turpis commodo. Ut ac diam nisi. Integer at lorem ac ligula vehicula porta. Proin tempor lorem a nunc auctor, rhoncus porta elit auctor. Duis imperdiet dictum luctus. Sed eget orci mattis, sagittis quam ac, congue arcu. Praesent blandit mattis sem, eget rutrum neque auctor id. Vestibulum eu augue ut nisl tempus iaculis. Nullam id tortor sed ante tincidunt consectetur et eu odio. Aliquam accumsan turpis ac dictum interdum.",
|
||||
},
|
||||
{
|
||||
id: uuid(),
|
||||
title: "Rotate",
|
||||
content:
|
||||
"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Donec metus augue, tempus vitae neque in, accumsan faucibus velit. Quisque felis lorem, tristique nec finibus consequat, viverra quis nibh. Donec arcu mi, placerat eget pretium non, molestie vitae leo. Nunc cursus id mi quis malesuada.",
|
||||
},
|
||||
{
|
||||
id: uuid(),
|
||||
title: "Scale",
|
||||
content:
|
||||
"Integer dapibus aliquet bibendum. Quisque id lobortis dolor, in bibendum enim. Cras mollis iaculis metus et tincidunt. Maecenas fermentum porttitor faucibus. Suspendisse feugiat ac enim nec vulputate. Aliquam egestas ante ac tempor blandit. Etiam vehicula, dui sed facilisis rhoncus, quam tortor efficitur sapien, eu imperdiet nisi magna et dui. Nulla a justo odio.",
|
||||
},
|
||||
],
|
||||
},
|
||||
ArViz: {
|
||||
data: [
|
||||
{ source: "Item 1", val: 1350, color: "#C9D6DF" },
|
||||
{ source: "Item 2", val: 2500, color: "#F7EECF" },
|
||||
{ source: "Item 3", val: 5700, color: "#E3E1B2" },
|
||||
{ source: "Item 4", val: 30000, color: "#F9CAC8" },
|
||||
{ source: "Item 5", val: 47500, color: "#D1C2E0" },
|
||||
],
|
||||
},
|
||||
AppAndToolsSelector: {
|
||||
data: navigator,
|
||||
},
|
||||
ProductDescriptionTile: {
|
||||
name: "Icons & Fonts",
|
||||
description: [
|
||||
"Dive into a world of creativity with our diverse icon assets and fonts.",
|
||||
],
|
||||
image: "",
|
||||
imagePlacement: ArPlacement.RIGHT,
|
||||
},
|
||||
Tiles: {
|
||||
data: tnsNavigator,
|
||||
},
|
||||
}
|
||||
|
||||
export const DUMMY_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" />,
|
||||
}
|
||||
90
src/helper.ts
Normal file
90
src/helper.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import {
|
||||
ComponentDescription,
|
||||
ComponentList,
|
||||
RecusionConditionTypes,
|
||||
TreeListData,
|
||||
} from "./types"
|
||||
import { injectIds } from "@armco/utils/recursionHelper"
|
||||
|
||||
export function findComponentDescription(
|
||||
componentName: string,
|
||||
components: ComponentList,
|
||||
) {
|
||||
let selectedItem = components.find(
|
||||
(item: ComponentDescription) =>
|
||||
(typeof item === "string" ? item : item.name) === componentName,
|
||||
)
|
||||
return {
|
||||
selectedItem: selectedItem
|
||||
? (JSON.parse(JSON.stringify(selectedItem)) as ComponentDescription)
|
||||
: { name: componentName },
|
||||
component: componentName,
|
||||
}
|
||||
}
|
||||
|
||||
export function adaptToTreeFromComponentConfig(
|
||||
componentConfig: Array<ComponentDescription>,
|
||||
module?: Array<string>,
|
||||
): Array<TreeListData> {
|
||||
const returnTreeList: Array<TreeListData> = []
|
||||
let components
|
||||
if (module) {
|
||||
components = module
|
||||
} else {
|
||||
components = componentConfig
|
||||
components.sort((c1, c2) => (c1.name > c2.name ? 1 : -1))
|
||||
}
|
||||
components.forEach((item: any) => {
|
||||
const config =
|
||||
typeof item === "string" &&
|
||||
componentConfig.find((config) => config.name === item)
|
||||
if (config) {
|
||||
item = config
|
||||
}
|
||||
const treeItem: TreeListData = {
|
||||
label: typeof item === "string" ? item : item.name,
|
||||
data: {
|
||||
component: typeof item === "string" ? item : item.name,
|
||||
item: typeof item === "string" ? { name: item } : item,
|
||||
},
|
||||
}
|
||||
const children: Array<any> = []
|
||||
treeItem.children = children
|
||||
if (item.variants && Object.keys(item.variants.length > 0)) {
|
||||
Object.keys(item.variants).forEach((variantKey) => {
|
||||
const variants = item.variants[variantKey]
|
||||
treeItem.children &&
|
||||
treeItem.children.push({
|
||||
label: variantKey,
|
||||
data: { name: typeof item === "string" ? item : item.name },
|
||||
children: variants.map((v: string) => ({
|
||||
label: v,
|
||||
data: {
|
||||
component: typeof item === "string" ? item : item.name,
|
||||
props: { [variantKey]: v },
|
||||
},
|
||||
})),
|
||||
})
|
||||
})
|
||||
}
|
||||
returnTreeList.push(treeItem)
|
||||
})
|
||||
|
||||
injectIds({
|
||||
data: returnTreeList,
|
||||
condition: { type: RecusionConditionTypes.KEY_EXISTS, key: "label" },
|
||||
iterateOn: "children",
|
||||
})
|
||||
return returnTreeList
|
||||
}
|
||||
|
||||
export const loadScript = (src: string, globalVar: string): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement("script")
|
||||
script.src = src
|
||||
script.async = true
|
||||
script.onload = () => resolve(window[globalVar as any])
|
||||
script.onerror = () => reject(new Error(`Failed to load script: ${src}`))
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
}
|
||||
17
src/index.tsx
Normal file
17
src/index.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from "react"
|
||||
import ReactDOM from "react-dom/client"
|
||||
import { BrowserRouter } from "react-router-dom"
|
||||
import { ArProvider } from "@armco/utils/providers"
|
||||
import ComponentsViewer from "./ComponentsViewer"
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement)
|
||||
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<BrowserRouter>
|
||||
<ArProvider>
|
||||
<ComponentsViewer />
|
||||
</ArProvider>
|
||||
</BrowserRouter>
|
||||
</React.StrictMode>,
|
||||
)
|
||||
71
src/react-app-env.d.ts
vendored
Normal file
71
src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="react" />
|
||||
/// <reference types="react-dom" />
|
||||
|
||||
declare namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
readonly NODE_ENV: "development" | "production"
|
||||
readonly PUBLIC_URL: string
|
||||
}
|
||||
}
|
||||
|
||||
declare module "*.avif" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.bmp" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.gif" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.jpg" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.jpeg" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.png" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.webp" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.svg" {
|
||||
import * as React from "react"
|
||||
|
||||
export const ReactComponent: React.FunctionComponent<
|
||||
React.SVGProps<SVGSVGElement> & { title?: string }
|
||||
>
|
||||
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.module.css" {
|
||||
const classes: { readonly [key: string]: string }
|
||||
export default classes
|
||||
}
|
||||
|
||||
declare module "*.module.scss" {
|
||||
const classes: { readonly [key: string]: string }
|
||||
export default classes
|
||||
}
|
||||
|
||||
declare module "*.module.sass" {
|
||||
const classes: { readonly [key: string]: string }
|
||||
export default classes
|
||||
}
|
||||
28
src/types.d.ts
vendored
Normal file
28
src/types.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
interface FrameContentDefinition {
|
||||
componentName: string
|
||||
component: ReactNode
|
||||
props?: {
|
||||
theme?: string
|
||||
demo?: boolean
|
||||
[key: string]:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| { [key: string]: any }
|
||||
| undefined
|
||||
}
|
||||
data: { [key: string]: any }
|
||||
}
|
||||
|
||||
export interface ComponentDescription {
|
||||
name: string
|
||||
type?: string
|
||||
children?: Array<string>
|
||||
props?: Array<any>
|
||||
propValues?: { [key: string]: string }
|
||||
variants?: { [key: string]: Array<string | boolean | number> }
|
||||
test?: { [key: string]: any }
|
||||
uses?: Array<{ [key: string]: string }>
|
||||
}
|
||||
|
||||
export type ComponentList = Array<ComponentDescription>
|
||||
69
src/types.ts
Normal file
69
src/types.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { ReactNode } from "react"
|
||||
|
||||
// Re-exports from @armco/types
|
||||
export type { ObjectType, FunctionType, ArrayType, PRIMITIVES } from "@armco/types"
|
||||
|
||||
// Re-exports from @armco/shared-components
|
||||
export type { TreeListData } from "@armco/shared-components/entity"
|
||||
export {
|
||||
ArButtonVariants,
|
||||
ArDropdownVariants,
|
||||
ArLabelValueVariants,
|
||||
ArLoaderTypes,
|
||||
ArPlacement,
|
||||
ArSizes,
|
||||
ArWebPageLayout,
|
||||
RecusionConditionTypes,
|
||||
} from "@armco/shared-components/enums"
|
||||
|
||||
// Local enums (not exported from shared packages)
|
||||
export enum ArThemes {
|
||||
LIGHT1 = "th-light-1",
|
||||
DARK1 = "th-dark-1",
|
||||
}
|
||||
|
||||
export enum ArDateMasks {
|
||||
DATE = "YYYY-MM-DD",
|
||||
DATETIME = "YYYY-MM-DD HH:mm:ss",
|
||||
TIME = "HH:mm:ss",
|
||||
SHORT_DATE = "MM/DD/YYYY",
|
||||
LONG_DATE = "MMMM D, YYYY",
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// components-viewer local types
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
export interface FrameContentDefinition {
|
||||
componentName?: string
|
||||
component?: ReactNode | React.ComponentType<any>
|
||||
props?: {
|
||||
theme?: string
|
||||
demo?: boolean
|
||||
[key: string]:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| { [key: string]: any }
|
||||
| undefined
|
||||
}
|
||||
data?: { [key: string]: any }
|
||||
}
|
||||
|
||||
export interface FrameContentProps {
|
||||
contentDefinition?: FrameContentDefinition
|
||||
contentRef?: React.RefObject<HTMLDivElement | null>
|
||||
}
|
||||
|
||||
export interface ComponentDescription {
|
||||
name: string
|
||||
type?: string
|
||||
children?: Array<string>
|
||||
props?: Array<any>
|
||||
propValues?: { [key: string]: string }
|
||||
variants?: { [key: string]: Array<string | boolean | number> }
|
||||
test?: { [key: string]: any }
|
||||
uses?: Array<{ [key: string]: string }>
|
||||
}
|
||||
|
||||
export type ComponentList = Array<ComponentDescription>
|
||||
55
src/utils.ts
Normal file
55
src/utils.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { injectIds } from "@armco/utils/recursionHelper"
|
||||
import { RecusionConditionTypes, TreeListData, ComponentDescription } from "./types"
|
||||
|
||||
export function adaptToTreeFromComponentConfig(data: any): Array<TreeListData> {
|
||||
const returnTreeList: Array<TreeListData> = []
|
||||
Object.keys(data).forEach((key) => {
|
||||
const groupConfig = data[key as keyof any]
|
||||
const components = groupConfig.components
|
||||
components.sort((c1: ComponentDescription, c2: ComponentDescription) =>
|
||||
(c1.name as any) > (c2.name as any) ? 1 : -1,
|
||||
)
|
||||
const obj: TreeListData = {
|
||||
label: groupConfig.label,
|
||||
children: [],
|
||||
data: { ...groupConfig },
|
||||
}
|
||||
components.forEach((item: any) => {
|
||||
const treeItem: TreeListData = {
|
||||
label: typeof item === "string" ? item : item.name,
|
||||
data: {
|
||||
component: typeof item === "string" ? item : item.name,
|
||||
hierarchy: key,
|
||||
},
|
||||
}
|
||||
const children: Array<any> = []
|
||||
treeItem.children = children
|
||||
if (item.variants && Object.keys(item.variants.length > 0)) {
|
||||
Object.keys(item.variants).forEach((variantKey) => {
|
||||
const variants = item.variants[variantKey]
|
||||
treeItem.children &&
|
||||
treeItem.children.push({
|
||||
label: variantKey,
|
||||
data: { name: typeof item === "string" ? item : item.name },
|
||||
children: variants.map((v: string) => ({
|
||||
label: v,
|
||||
data: {
|
||||
component: typeof item === "string" ? item : item.name,
|
||||
props: { [variantKey]: v },
|
||||
hierarchy: key,
|
||||
},
|
||||
})),
|
||||
})
|
||||
})
|
||||
}
|
||||
obj.children && obj.children.push(treeItem)
|
||||
})
|
||||
returnTreeList.push(obj)
|
||||
})
|
||||
injectIds({
|
||||
data: returnTreeList,
|
||||
condition: { type: RecusionConditionTypes.KEY_EXISTS, key: "label" },
|
||||
iterateOn: "children",
|
||||
})
|
||||
return returnTreeList
|
||||
}
|
||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
26
tsconfig.json
Normal file
26
tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
20
vite-dev.config.ts
Normal file
20
vite-dev.config.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { defineConfig } from "vitest/config"
|
||||
import react from "@vitejs/plugin-react"
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
open: true,
|
||||
},
|
||||
build: {
|
||||
outDir: "build",
|
||||
sourcemap: true,
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
environment: "jsdom",
|
||||
setupFiles: "src/setupTests",
|
||||
mockReset: true,
|
||||
},
|
||||
})
|
||||
41
vite.config.ts
Normal file
41
vite.config.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { resolve } from "path"
|
||||
import { glob } from "glob"
|
||||
import { defineConfig } from "vitest/config"
|
||||
import react from "@vitejs/plugin-react"
|
||||
import dts from "vite-plugin-dts"
|
||||
import { libInjectCss } from "vite-plugin-lib-inject-css"
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), libInjectCss(), dts({ outDir: "build/types" })],
|
||||
build: {
|
||||
outDir: "build",
|
||||
lib: {
|
||||
entry: glob.sync(resolve(__dirname, "src/**/!(*.d|index).{ts,tsx}")),
|
||||
},
|
||||
rollupOptions: {
|
||||
treeshake: true,
|
||||
external: [
|
||||
new RegExp("react*"),
|
||||
new RegExp("highcharts*"),
|
||||
new RegExp("@armco/*"),
|
||||
"d3",
|
||||
"uuid",
|
||||
],
|
||||
output: [
|
||||
{
|
||||
format: "es",
|
||||
dir: "build/es",
|
||||
entryFileNames: "[name].js",
|
||||
chunkFileNames: "[name]-chunk.js",
|
||||
},
|
||||
{
|
||||
format: "cjs",
|
||||
dir: "build/cjs",
|
||||
entryFileNames: "[name].js",
|
||||
chunkFileNames: "[name]-chunk.js",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user