feat: update deps, switch to biome.js

This commit is contained in:
Nicola Turcato 2025-03-03 09:41:58 +01:00
parent be27f20238
commit d18d0ef461
17 changed files with 595 additions and 3287 deletions

View File

@ -1,35 +0,0 @@
{
"env": {
"es6": true,
"node": true,
"browser": true
},
"extends": [
"plugin:react/recommended",
"standard-with-typescript",
"plugin:prettier/recommended"
],
"ignorePatterns": [
"**/dist/**",
"**/node_modules/**",
".eslintrc.json",
"tsconfig.json",
"package.json"
],
"overrides": [],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module",
"project": "tsconfig.json"
},
"plugins": [
"react",
"prettier",
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/strict-boolean-expressions": 0,
"prettier/prettier": "error"
}
}

82
.vscode/settings.json vendored
View File

@ -1,42 +1,42 @@
{ {
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
"editor.formatOnSave": true, "editor.formatOnSave": true,
"[typescript]": { "[typescript]": {
"editor.formatOnSave": false, "editor.formatOnSave": false,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit" "source.fixAll.eslint": "explicit"
} }
}, },
"[javascript]": { "[javascript]": {
"editor.formatOnSave": false, "editor.formatOnSave": false,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit" "source.fixAll.eslint": "explicit"
} }
}, },
"[typescriptreact]": { "[typescriptreact]": {
"editor.formatOnSave": false, "editor.formatOnSave": false,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit" "source.fixAll.eslint": "explicit"
} }
}, },
"[json]": { "[json]": {
"editor.formatOnSave": false, "editor.formatOnSave": false,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit" "source.fixAll.eslint": "explicit"
} }
}, },
"files.exclude": { "files.exclude": {
"**/.git": true, "**/.git": true,
"**/.svn": true, "**/.svn": true,
"**/.hg": true, "**/.hg": true,
"**/CVS": true, "**/CVS": true,
"**/.DS_Store": true, "**/.DS_Store": true,
"**/Thumbs.db": true, "**/Thumbs.db": true,
"**/node_modules": true "**/node_modules": true
}, },
"[javascript][json][typescript][typescriptreact]": { "[javascript][json][typescript][typescriptreact]": {
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit" "source.fixAll.eslint": "explicit"
} }
} }
} }

151
biome.json Normal file
View File

@ -0,0 +1,151 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false },
"files": { "ignoreUnknown": false, "ignore": [] },
"formatter": {
"enabled": true,
"useEditorconfig": true,
"formatWithErrors": false,
"indentStyle": "tab",
"indentWidth": 2,
"lineEnding": "lf",
"lineWidth": 80,
"attributePosition": "auto",
"bracketSpacing": true,
"ignore": ["**/dist"]
},
"organizeImports": { "enabled": true },
"linter": {
"enabled": true,
"rules": {
"recommended": false,
"a11y": { "noBlankTarget": "error" },
"complexity": {
"noBannedTypes": "error",
"noExtraBooleanCast": "error",
"noMultipleSpacesInRegularExpressionLiterals": "error",
"noStaticOnlyClass": "error",
"noUselessCatch": "error",
"noUselessConstructor": "error",
"noUselessLoneBlockStatements": "error",
"noUselessRename": "error",
"noUselessTernary": "error",
"noUselessThisAlias": "error",
"noUselessTypeConstraint": "error",
"noUselessUndefinedInitialization": "error",
"noVoid": "error",
"noWith": "error",
"useArrowFunction": "off",
"useLiteralKeys": "error",
"useOptionalChain": "error",
"useRegexLiterals": "error"
},
"correctness": {
"noChildrenProp": "error",
"noConstAssign": "error",
"noConstantCondition": "error",
"noEmptyCharacterClassInRegex": "error",
"noEmptyPattern": "error",
"noGlobalObjectCalls": "error",
"noInvalidConstructorSuper": "error",
"noInvalidUseBeforeDeclaration": "error",
"noNewSymbol": "error",
"noPrecisionLoss": "error",
"noSelfAssign": "error",
"noSwitchDeclarations": "error",
"noUndeclaredVariables": "off",
"noUnreachable": "error",
"noUnreachableSuper": "error",
"noUnsafeFinally": "error",
"noUnusedVariables": "error",
"useArrayLiterals": "off",
"useIsNan": "error",
"useJsxKeyInIterable": "error"
},
"security": {
"noDangerouslySetInnerHtmlWithChildren": "error",
"noGlobalEval": "error"
},
"style": {
"noCommaOperator": "error",
"noNamespace": "error",
"noNonNullAssertion": "error",
"noVar": "warn",
"noYodaExpression": "error",
"useBlockStatements": "error",
"useConsistentArrayType": {
"level": "error",
"options": { "syntax": "shorthand" }
},
"useConsistentBuiltinInstantiation": "error",
"useConst": "error",
"useExportType": "error",
"useImportType": "error",
"useNamingConvention": {
"level": "off",
"options": {
"strictCase": false
}
},
"useShorthandFunctionType": "error",
"useSingleVarDeclarator": "error",
"useThrowOnlyError": "off"
},
"suspicious": {
"noAssignInExpressions": "error",
"noAsyncPromiseExecutor": "error",
"noCatchAssign": "error",
"noClassAssign": "error",
"noCommentText": "error",
"noCompareNegZero": "error",
"noConfusingLabels": "error",
"noConfusingVoidType": "error",
"noControlCharactersInRegex": "error",
"noDebugger": "error",
"noDoubleEquals": "error",
"noDuplicateCase": "error",
"noDuplicateClassMembers": "error",
"noDuplicateJsxProps": "error",
"noDuplicateObjectKeys": "error",
"noDuplicateParameters": "error",
"noEmptyBlockStatements": "error",
"noEmptyInterface": "error",
"noExtraNonNullAssertion": "error",
"noFallthroughSwitchClause": "error",
"noFunctionAssign": "error",
"noGlobalAssign": "error",
"noImportAssign": "error",
"noMisleadingCharacterClass": "error",
"noMisleadingInstantiator": "error",
"noPrototypeBuiltins": "error",
"noRedeclare": "error",
"noSelfCompare": "error",
"noShadowRestrictedNames": "error",
"noSparseArray": "error",
"noUnsafeNegation": "error",
"useDefaultSwitchClauseLast": "error",
"useValidTypeof": "error"
}
},
"ignore": [
"**/dist/**",
"**/node_modules/**",
"**/tsconfig.json",
"**/package.json"
]
},
"javascript": {
"formatter": {
"jsxQuoteStyle": "double",
"quoteProperties": "asNeeded",
"trailingCommas": "all",
"semicolons": "asNeeded",
"arrowParentheses": "always",
"bracketSameLine": false,
"quoteStyle": "single",
"attributePosition": "auto",
"bracketSpacing": true
},
"globals": ["document", "navigator", "window"]
}
}

View File

@ -1,5 +1,5 @@
import { type PlainClientAPI } from 'contentful-management'; import type { PlainClientAPI } from 'contentful-management';
import { type PageAppSDK } from 'contentful-ui-extensions-sdk'; import type { PageAppSDK } from 'contentful-ui-extensions-sdk';
import { type ReactElement } from 'react'; import { type ReactElement } from 'react';
export interface ContentTreeProps { export interface ContentTreeProps {
sdkInstance: PageAppSDK; sdkInstance: PageAppSDK;

View File

@ -1,5 +1,5 @@
import { type PlainClientAPI } from 'contentful-management'; import type { PlainClientAPI } from 'contentful-management';
import { type PageAppSDK } from 'contentful-ui-extensions-sdk'; import type { PageAppSDK } from 'contentful-ui-extensions-sdk';
import { type ReactElement } from 'react'; import { type ReactElement } from 'react';
import { type ContentTreeNodeProps } from './ContentTreeNode'; import { type ContentTreeNodeProps } from './ContentTreeNode';
export interface ContentTreeRootProps { export interface ContentTreeRootProps {

View File

@ -1,4 +1,4 @@
import { type EntryProps, type KeyValueMap } from 'contentful-management'; import type { EntryProps, KeyValueMap } from 'contentful-management';
import { type ContentTreeNodeProps } from './ContentTreeNode'; import type { ContentTreeNodeProps } from './ContentTreeNode';
export declare const emptyNodeProps: () => ContentTreeNodeProps; export declare const emptyNodeProps: () => ContentTreeNodeProps;
export declare const cfEntriesToNodes: (entries: Array<EntryProps<KeyValueMap>>, titleFields: string[], stLocale: string, locales: string[], nodeContentTypes: string[], iconRegistry?: Record<string, string>, parentId?: string) => ContentTreeNodeProps[]; export declare const cfEntriesToNodes: (entries: EntryProps<KeyValueMap>[], titleFields: string[], stLocale: string, locales: string[], nodeContentTypes: string[], iconRegistry?: Record<string, string>, parentId?: string) => ContentTreeNodeProps[];

View File

@ -1,6 +1,6 @@
{ {
"name": "@foomo/contentfultree", "name": "@foomo/contentfultree",
"version": "0.1.28", "version": "0.2.0",
"main": "dist/index.js", "main": "dist/index.js",
"author": "foomo", "author": "foomo",
"license": "MIT", "license": "MIT",
@ -8,41 +8,24 @@
"clean": "rm -f ./tsconfig.tsbuildinfo && rm -rvf dist", "clean": "rm -f ./tsconfig.tsbuildinfo && rm -rvf dist",
"build": "pnpm clean && tsc -b -v .", "build": "pnpm clean && tsc -b -v .",
"dev": "pnpm tsc -b tsconfig.json -w", "dev": "pnpm tsc -b tsconfig.json -w",
"lint": "eslint --ext=js,jsx,ts,tsx,json .", "lint": "biome check --write ."
"lint:fix": "eslint --ext=js,jsx,ts,tsx,json . --fix"
},
"prettier": {
"useTabs": true,
"tabWidth": 2,
"singleQuote": true
}, },
"devDependencies": { "devDependencies": {
"@types/react": "19.0.7", "@biomejs/biome": "1.9.4",
"@types/react": "19.0.10",
"@types/styled-components": "5.1.34", "@types/styled-components": "5.1.34",
"@typescript-eslint/eslint-plugin": "8.20.0",
"@typescript-eslint/parser": "8.20.0",
"eslint": "9.18.0",
"eslint-config-love": "117.0.0",
"eslint-config-prettier": "10.0.1",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-json": "4.0.1",
"eslint-plugin-n": "17.15.1",
"eslint-plugin-prettier": "5.2.2",
"eslint-plugin-promise": "7.2.1",
"eslint-plugin-react": "7.37.4",
"install-peers": "1.0.4", "install-peers": "1.0.4",
"prettier": "3.4.2", "typescript": "5.8.2"
"typescript": "5.7.3"
}, },
"peerDependencies": { "peerDependencies": {
"install-peers": "1.0.4" "install-peers": "1.0.4"
}, },
"optionalDependencies": { "optionalDependencies": {
"react": "19.0.0", "react": "19.0.0",
"styled-components": "6.1.14" "styled-components": "6.1.15"
}, },
"dependencies": { "dependencies": {
"contentful-management": "11.42.0", "contentful-management": "11.47.3",
"contentful-ui-extensions-sdk": "4.29.3" "contentful-ui-extensions-sdk": "4.29.5"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
import styled from 'styled-components'; import styled from 'styled-components'
export interface UiPalette { export interface UiPalette {
publishingStatusBg?: string; publishingStatusBg?: string
publishingStatusFg?: string; publishingStatusFg?: string
} }
export const StyledContentTreeTable = styled.table` export const StyledContentTreeTable = styled.table`
@ -28,14 +28,14 @@ export const StyledContentTreeTable = styled.table`
font-weight: bold !important; font-weight: bold !important;
color: black !important; color: black !important;
} }
`; `
export const StyledContentTreeTableNodeCell = styled.td<{ $depth?: number }>` export const StyledContentTreeTableNodeCell = styled.td<{ $depth?: number }>`
padding-left: ${(props) => props.$depth}em !important; padding-left: ${(props) => props.$depth}em !important;
padding-right: 2em !important; padding-right: 2em !important;
color: black !important; color: black !important;
min-width: 450px !important; min-width: 450px !important;
`; `
export const StyledContentTreeNodeWedge = styled.div` export const StyledContentTreeNodeWedge = styled.div`
display: inline-block; display: inline-block;
@ -46,7 +46,7 @@ export const StyledContentTreeNodeWedge = styled.div`
font-size: 130%; font-size: 130%;
line-height: 100%; line-height: 100%;
} }
`; `
export const StyledContentTreeNodeName = styled.div` export const StyledContentTreeNodeName = styled.div`
display: inline-block; display: inline-block;
@ -56,7 +56,7 @@ export const StyledContentTreeNodeName = styled.div`
a:hover { a:hover {
text-decoration: underline; text-decoration: underline;
} }
`; `
const getPublishingStatusColors = (status: string): UiPalette | undefined => { const getPublishingStatusColors = (status: string): UiPalette | undefined => {
switch (status) { switch (status) {
@ -64,22 +64,22 @@ const getPublishingStatusColors = (status: string): UiPalette | undefined => {
return { return {
publishingStatusBg: 'rgb(253, 229, 192)', publishingStatusBg: 'rgb(253, 229, 192)',
publishingStatusFg: 'rgb(177, 45, 0)', publishingStatusFg: 'rgb(177, 45, 0)',
}; }
case 'changed': case 'changed':
return { return {
publishingStatusBg: 'rgb(206, 236, 255)', publishingStatusBg: 'rgb(206, 236, 255)',
publishingStatusFg: 'rgb(0, 89, 200)', publishingStatusFg: 'rgb(0, 89, 200)',
}; }
case 'published': case 'published':
return { return {
publishingStatusBg: 'rgb(205, 243, 198)', publishingStatusBg: 'rgb(205, 243, 198)',
publishingStatusFg: 'rgb(0, 109, 35)', publishingStatusFg: 'rgb(0, 109, 35)',
}; }
} }
}; }
export const StyledContentTreeNodePublishingStatus = styled.div<{ export const StyledContentTreeNodePublishingStatus = styled.div<{
$status: string; $status: string
}>` }>`
display: inline-block; display: inline-block;
font-family: font-family:
@ -102,7 +102,7 @@ export const StyledContentTreeNodePublishingStatus = styled.div<{
getPublishingStatusColors(props.$status)?.publishingStatusFg} !important; getPublishingStatusColors(props.$status)?.publishingStatusFg} !important;
background-color: ${(props) => background-color: ${(props) =>
getPublishingStatusColors(props.$status)?.publishingStatusBg} !important; getPublishingStatusColors(props.$status)?.publishingStatusBg} !important;
`; `
export const StyledSpinner = styled.div` export const StyledSpinner = styled.div`
display: inline-block; display: inline-block;
@ -116,4 +116,4 @@ export const StyledSpinner = styled.div`
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
`; `

View File

@ -1,18 +1,18 @@
import { type PlainClientAPI } from 'contentful-management'; import type { PlainClientAPI } from 'contentful-management'
import { type PageAppSDK } from 'contentful-ui-extensions-sdk'; import type { PageAppSDK } from 'contentful-ui-extensions-sdk'
import React, { type ReactElement, useEffect, useState } from 'react'; import React, { type ReactElement, useEffect, useState } from 'react'
import { ContentTreeRoot } from './ContentTreeRoot'; import { ContentTreeRoot } from './ContentTreeRoot'
import { emptyNodeProps, cfEntriesToNodes } from './ContentTreeUtils'; import { cfEntriesToNodes, emptyNodeProps } from './ContentTreeUtils'
export interface ContentTreeProps { export interface ContentTreeProps {
sdkInstance: PageAppSDK; sdkInstance: PageAppSDK
cma: PlainClientAPI; cma: PlainClientAPI
rootType: string; rootType: string
nodeContentTypes: string[]; nodeContentTypes: string[]
titleFields: string[]; titleFields: string[]
locales: string[]; // the first is the default locale locales: string[] // the first is the default locale
indentation?: number; indentation?: number
iconRegistry?: Record<string, string>; iconRegistry?: Record<string, string>
} }
export const ContentTree = ({ export const ContentTree = ({
@ -25,21 +25,21 @@ export const ContentTree = ({
indentation = 1, indentation = 1,
iconRegistry, iconRegistry,
}: ContentTreeProps): ReactElement => { }: ContentTreeProps): ReactElement => {
const [stLocale] = useState(locales[0]); const [stLocale] = useState(locales[0])
const [rootNodes, setRootNodes] = useState([emptyNodeProps()]); const [rootNodes, setRootNodes] = useState([emptyNodeProps()])
useEffect(() => { useEffect(() => {
if (sdkInstance) { if (sdkInstance) {
loadData().catch((err: ErrorOptions) => { loadData().catch((err: ErrorOptions) => {
throw new Error('loadRootData', err); throw new Error('loadRootData', err)
}); })
} }
}, [sdkInstance]); }, [sdkInstance])
const loadData = async (): Promise<void> => { const loadData = async (): Promise<void> => {
const CfRootData = await cma.entry.getMany({ const CfRootData = await cma.entry.getMany({
query: { content_type: rootType }, query: { content_type: rootType },
}); })
const nodes = cfEntriesToNodes( const nodes = cfEntriesToNodes(
CfRootData.items, CfRootData.items,
titleFields, titleFields,
@ -47,9 +47,9 @@ export const ContentTree = ({
locales, locales,
nodeContentTypes, nodeContentTypes,
iconRegistry, iconRegistry,
); )
setRootNodes(nodes); setRootNodes(nodes)
}; }
return ( return (
<> <>
@ -67,5 +67,5 @@ export const ContentTree = ({
/> />
))} ))}
</> </>
); )
}; }

View File

@ -1,4 +1,4 @@
import React, { type ReactElement, useState } from 'react'; import React, { type ReactElement, useState } from 'react'
import { import {
StyledContentTreeNodeName, StyledContentTreeNodeName,
@ -6,49 +6,49 @@ import {
StyledContentTreeNodeWedge, StyledContentTreeNodeWedge,
StyledContentTreeTableNodeCell, StyledContentTreeTableNodeCell,
StyledSpinner, StyledSpinner,
} from './ContentTree.styled'; } from './ContentTree.styled'
import { Icon } from './Icons'; import { Icon } from './Icons'
export interface ContentTreeNodeProps { export interface ContentTreeNodeProps {
id: string; id: string
name: string; name: string
contentType?: string; contentType?: string
icon?: string; icon?: string
expand: boolean; expand: boolean
parentId?: string; parentId?: string
childNodes?: ContentTreeNodeProps[]; childNodes?: ContentTreeNodeProps[]
hasChildNodes?: boolean; hasChildNodes?: boolean
publishingStatus?: string; publishingStatus?: string
updatedAt?: string; updatedAt?: string
publishedAt?: string; publishedAt?: string
} }
const ContentTreeNode = (props: { const ContentTreeNode = (props: {
node: ContentTreeNodeProps; node: ContentTreeNodeProps
depth?: number; depth?: number
addChildNodes: (node: ContentTreeNodeProps) => Promise<void>; addChildNodes: (node: ContentTreeNodeProps) => Promise<void>
removeChildNodes: (node: ContentTreeNodeProps) => void; removeChildNodes: (node: ContentTreeNodeProps) => void
editEntry: (nodeId: string) => Promise<void>; editEntry: (nodeId: string) => Promise<void>
}): ReactElement => { }): ReactElement => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false)
const addChildren = async (node: ContentTreeNodeProps): Promise<void> => { const addChildren = async (node: ContentTreeNodeProps): Promise<void> => {
setLoading(true); setLoading(true)
await props.addChildNodes(node); await props.addChildNodes(node)
setLoading(false); setLoading(false)
}; }
const handleEditEntry = (): void => { const handleEditEntry = (): void => {
props.editEntry(props.node.id).catch((err: ErrorOptions) => { props.editEntry(props.node.id).catch((err: ErrorOptions) => {
throw new Error('handleEditEntry', err); throw new Error('handleEditEntry', err)
}); })
}; }
const handleAddChildren = (node: ContentTreeNodeProps): void => { const handleAddChildren = (node: ContentTreeNodeProps): void => {
addChildren(node).catch((err: ErrorOptions) => { addChildren(node).catch((err: ErrorOptions) => {
throw new Error('handleAddChildren', err); throw new Error('handleAddChildren', err)
}); })
}; }
return ( return (
<> <>
@ -61,7 +61,7 @@ const ContentTreeNode = (props: {
props.node.expand ? ( props.node.expand ? (
<a <a
onClick={() => { onClick={() => {
handleAddChildren(props.node); handleAddChildren(props.node)
}} }}
> >
+ +
@ -69,7 +69,7 @@ const ContentTreeNode = (props: {
) : ( ) : (
<a <a
onClick={() => { onClick={() => {
props.removeChildNodes(props.node); props.removeChildNodes(props.node)
}} }}
> >
- -
@ -81,7 +81,7 @@ const ContentTreeNode = (props: {
<StyledContentTreeNodeName> <StyledContentTreeNodeName>
<a <a
onClick={() => { onClick={() => {
handleEditEntry(); handleEditEntry()
}} }}
title={props.node.id} title={props.node.id}
> >
@ -110,10 +110,10 @@ const ContentTreeNode = (props: {
removeChildNodes={props.removeChildNodes} removeChildNodes={props.removeChildNodes}
editEntry={props.editEntry} editEntry={props.editEntry}
/> />
); )
})} })}
</> </>
); )
}; }
export default ContentTreeNode; export default ContentTreeNode

View File

@ -1,25 +1,25 @@
import { import type {
type EntryProps, EntryProps,
type KeyValueMap, KeyValueMap,
type Link, Link,
type PlainClientAPI, PlainClientAPI,
} from 'contentful-management'; } from 'contentful-management'
import { type PageAppSDK } from 'contentful-ui-extensions-sdk'; import type { PageAppSDK } from 'contentful-ui-extensions-sdk'
import React, { type ReactElement, useEffect, useState } from 'react'; import React, { type ReactElement, useEffect, useState } from 'react'
import { StyledContentTreeTable } from './ContentTree.styled'; import { StyledContentTreeTable } from './ContentTree.styled'
import ContentTreeNode, { type ContentTreeNodeProps } from './ContentTreeNode'; import ContentTreeNode, { type ContentTreeNodeProps } from './ContentTreeNode'
import { cfEntriesToNodes, emptyNodeProps } from './ContentTreeUtils'; import { cfEntriesToNodes, emptyNodeProps } from './ContentTreeUtils'
export interface ContentTreeRootProps { export interface ContentTreeRootProps {
node: ContentTreeNodeProps; node: ContentTreeNodeProps
sdkInstance: PageAppSDK; sdkInstance: PageAppSDK
cma: PlainClientAPI; cma: PlainClientAPI
nodeContentTypes: string[]; nodeContentTypes: string[]
titleFields: string[]; titleFields: string[]
locales: string[]; // the first is the default locale locales: string[] // the first is the default locale
depth: number; depth: number
iconRegistry?: Record<string, string>; iconRegistry?: Record<string, string>
} }
export const ContentTreeRoot = ({ export const ContentTreeRoot = ({
@ -32,21 +32,21 @@ export const ContentTreeRoot = ({
depth, depth,
iconRegistry, iconRegistry,
}: ContentTreeRootProps): ReactElement => { }: ContentTreeRootProps): ReactElement => {
const [stLocale] = useState(locales[0]); const [stLocale] = useState(locales[0])
const [stRoot, setStRoot] = useState(emptyNodeProps()); const [stRoot, setStRoot] = useState(emptyNodeProps())
useEffect(() => { useEffect(() => {
if (node.id) { if (node.id) {
loadRootData(node).catch((err: ErrorOptions) => { loadRootData(node).catch((err: ErrorOptions) => {
throw new Error('loadRootData', err); throw new Error('loadRootData', err)
}); })
} }
}, [node]); }, [node])
const loadRootData = async ( const loadRootData = async (
rootNode: ContentTreeNodeProps, rootNode: ContentTreeNodeProps,
): Promise<void> => { ): Promise<void> => {
const childEntries = await getContentfulChildEntries(rootNode.id); const childEntries = await getContentfulChildEntries(rootNode.id)
const childNodes = cfEntriesToNodes( const childNodes = cfEntriesToNodes(
childEntries, childEntries,
titleFields, titleFields,
@ -55,34 +55,34 @@ export const ContentTreeRoot = ({
nodeContentTypes, nodeContentTypes,
iconRegistry, iconRegistry,
rootNode.id, rootNode.id,
); )
const nodes = [rootNode, ...childNodes]; const nodes = [rootNode, ...childNodes]
if (nodes.length > 0) { if (nodes.length > 0) {
const newIdPositionMap = nodes.reduce((acc: any, el, i) => { const newIdPositionMap = nodes.reduce((acc: any, el, i) => {
acc[el.id] = i; acc[el.id] = i
return acc; return acc
}, {}); }, {})
let tree: ContentTreeNodeProps = emptyNodeProps(); let tree: ContentTreeNodeProps = emptyNodeProps()
nodes.forEach((node: ContentTreeNodeProps) => { nodes.forEach((node: ContentTreeNodeProps) => {
node.childNodes = []; node.childNodes = []
if (!node.parentId) { if (!node.parentId) {
tree = node; tree = node
return; return
} }
const parentEl = nodes[newIdPositionMap[node.parentId]]; const parentEl = nodes[newIdPositionMap[node.parentId]]
if (parentEl) { if (parentEl) {
parentEl.childNodes = [...(parentEl.childNodes ?? []), node]; parentEl.childNodes = [...(parentEl.childNodes ?? []), node]
parentEl.expand = false; parentEl.expand = false
} }
}); })
console.log('🌴 tree', tree); console.log('🌴 tree', tree)
setStRoot(tree); setStRoot(tree)
} }
}; }
const addChildNodes = async (node: ContentTreeNodeProps): Promise<void> => { const addChildNodes = async (node: ContentTreeNodeProps): Promise<void> => {
let childNodes: ContentTreeNodeProps[] = []; let childNodes: ContentTreeNodeProps[] = []
const cfChildren = await getContentfulChildEntries(node.id); const cfChildren = await getContentfulChildEntries(node.id)
childNodes = cfEntriesToNodes( childNodes = cfEntriesToNodes(
cfChildren, cfChildren,
titleFields, titleFields,
@ -91,21 +91,21 @@ export const ContentTreeRoot = ({
nodeContentTypes, nodeContentTypes,
iconRegistry, iconRegistry,
node.id, node.id,
); )
setStRoot((prevState) => { setStRoot((prevState) => {
const newState = { ...prevState }; const newState = { ...prevState }
recursiveProcessNodes( recursiveProcessNodes(
node.id, node.id,
(targetNode) => { (targetNode) => {
targetNode.childNodes = childNodes; targetNode.childNodes = childNodes
targetNode.expand = false; targetNode.expand = false
}, },
newState, newState,
); )
return newState; return newState
}); })
}; }
const recursiveProcessNodes = ( const recursiveProcessNodes = (
targetNodeId: string, targetNodeId: string,
@ -113,85 +113,85 @@ export const ContentTreeRoot = ({
node: ContentTreeNodeProps, node: ContentTreeNodeProps,
): void => { ): void => {
if (node.id === targetNodeId) { if (node.id === targetNodeId) {
processNode(node); processNode(node)
} }
if (node.childNodes != null) { if (node.childNodes != null) {
for (const targetNode of node.childNodes) { for (const targetNode of node.childNodes) {
recursiveProcessNodes(targetNodeId, processNode, targetNode); recursiveProcessNodes(targetNodeId, processNode, targetNode)
} }
} }
}; }
const editEntry = async (entryId: string): Promise<void> => { const editEntry = async (entryId: string): Promise<void> => {
await sdkInstance.navigator.openEntry(entryId, { slideIn: true }); await sdkInstance.navigator.openEntry(entryId, { slideIn: true })
}; }
const getContentfulChildEntries = async ( const getContentfulChildEntries = async (
parentId: string, parentId: string,
): Promise<Array<EntryProps<KeyValueMap>>> => { ): Promise<EntryProps<KeyValueMap>[]> => {
const parentItem = await cma.entry.get({ entryId: parentId }); const parentItem = await cma.entry.get({ entryId: parentId })
const allChildIds: string[] = []; const allChildIds: string[] = []
for (const key of Object.keys(parentItem.fields)) { for (const key of Object.keys(parentItem.fields)) {
if (nodeContentTypes.includes(key)) { if (nodeContentTypes.includes(key)) {
const childNodeRefs = parentItem.fields[key][stLocale] as const childNodeRefs = parentItem.fields[key][stLocale] as
| Link<string> | Link<string>
| Array<Link<string>>; | Link<string>[]
if (Array.isArray(childNodeRefs)) { if (Array.isArray(childNodeRefs)) {
for (const childNodeRef of childNodeRefs) { for (const childNodeRef of childNodeRefs) {
allChildIds.push(childNodeRef.sys.id); allChildIds.push(childNodeRef.sys.id)
} }
} else { } else {
allChildIds.push(childNodeRefs.sys.id); allChildIds.push(childNodeRefs.sys.id)
} }
} }
} }
const allItems: Array<EntryProps<KeyValueMap>> = []; const allItems: EntryProps<KeyValueMap>[] = []
let done = false; let done = false
let skip = 0; let skip = 0
while (!done) { while (!done) {
const col = await cma.entry.getMany({ const col = await cma.entry.getMany({
query: { query: {
'sys.id[in]': allChildIds.join(','), 'sys.id[in]': allChildIds.join(','),
skip, skip,
}, },
}); })
allItems.push(...col.items); allItems.push(...col.items)
if (allItems.length < col.total) { if (allItems.length < col.total) {
skip += 100; skip += 100
} else { } else {
done = true; done = true
} }
} }
const cfChildren: Array<EntryProps<KeyValueMap>> = []; const cfChildren: EntryProps<KeyValueMap>[] = []
const idPositionMap: Record<string, number> = allItems.reduce( const idPositionMap: Record<string, number> = allItems.reduce(
(acc: any, el, i) => { (acc: any, el, i) => {
acc[el.sys.id] = i; acc[el.sys.id] = i
return acc; return acc
}, },
{}, {},
); )
for (const childId of allChildIds) { for (const childId of allChildIds) {
if (allItems[idPositionMap[childId]]) { if (allItems[idPositionMap[childId]]) {
cfChildren.push(allItems[idPositionMap[childId]]); cfChildren.push(allItems[idPositionMap[childId]])
} }
} }
return cfChildren; return cfChildren
}; }
const removeChildNodes = (node: ContentTreeNodeProps): void => { const removeChildNodes = (node: ContentTreeNodeProps): void => {
setStRoot((prevState) => { setStRoot((prevState) => {
const newState = { ...prevState }; const newState = { ...prevState }
recursiveProcessNodes( recursiveProcessNodes(
node.id, node.id,
(targetNode) => { (targetNode) => {
targetNode.childNodes = []; targetNode.childNodes = []
targetNode.expand = true; targetNode.expand = true
}, },
newState, newState,
); )
return newState; return newState
}); })
}; }
return ( return (
<> <>
@ -214,5 +214,5 @@ export const ContentTreeRoot = ({
</tbody> </tbody>
</StyledContentTreeTable> </StyledContentTreeTable>
</> </>
); )
}; }

View File

@ -1,9 +1,9 @@
import { type EntryProps, type KeyValueMap } from 'contentful-management'; import type { EntryProps, KeyValueMap } from 'contentful-management'
import { type ContentTreeNodeProps } from './ContentTreeNode'; import type { ContentTreeNodeProps } from './ContentTreeNode'
export const emptyNodeProps = (): ContentTreeNodeProps => { export const emptyNodeProps = (): ContentTreeNodeProps => {
return { id: '', name: '', expand: false, parentId: '' }; return { id: '', name: '', expand: false, parentId: '' }
}; }
const cfEntryHasChildren = ( const cfEntryHasChildren = (
entry: EntryProps<KeyValueMap>, entry: EntryProps<KeyValueMap>,
@ -13,25 +13,25 @@ const cfEntryHasChildren = (
for (const nodeContentType of nodeContentTypes) { for (const nodeContentType of nodeContentTypes) {
for (const locale of locales) { for (const locale of locales) {
if (entry.fields[nodeContentType]?.[locale]) { if (entry.fields[nodeContentType]?.[locale]) {
return true; return true
} }
} }
} }
return false; return false
}; }
const cfEntryPublishingStatus = (entry: EntryProps<KeyValueMap>): string => { const cfEntryPublishingStatus = (entry: EntryProps<KeyValueMap>): string => {
if (!entry.sys.publishedVersion) { if (!entry.sys.publishedVersion) {
return 'draft'; return 'draft'
} }
if (entry.sys.version - entry.sys.publishedVersion === 1) { if (entry.sys.version - entry.sys.publishedVersion === 1) {
return 'published'; return 'published'
} }
return 'changed'; return 'changed'
}; }
export const cfEntriesToNodes = ( export const cfEntriesToNodes = (
entries: Array<EntryProps<KeyValueMap>>, entries: EntryProps<KeyValueMap>[],
titleFields: string[], titleFields: string[],
stLocale: string, stLocale: string,
locales: string[], locales: string[],
@ -40,22 +40,22 @@ export const cfEntriesToNodes = (
parentId?: string, parentId?: string,
): ContentTreeNodeProps[] => { ): ContentTreeNodeProps[] => {
if (entries.length === 0) { if (entries.length === 0) {
return []; return []
} }
const nodeArray: ContentTreeNodeProps[] = []; const nodeArray: ContentTreeNodeProps[] = []
entries.forEach((entry) => { entries.forEach((entry) => {
if (!entry) { if (!entry) {
return; return
} }
let name = ''; let name = ''
for (const titleField of titleFields) { for (const titleField of titleFields) {
if (entry.fields[titleField]?.[stLocale]) { if (entry.fields[titleField]?.[stLocale]) {
name = entry.fields[titleField][stLocale]; name = entry.fields[titleField][stLocale]
break; break
} }
} }
if (name === '') { if (name === '') {
name = entry.sys.id; name = entry.sys.id
} }
const node: ContentTreeNodeProps = { const node: ContentTreeNodeProps = {
id: entry.sys.id, id: entry.sys.id,
@ -69,8 +69,8 @@ export const cfEntriesToNodes = (
publishingStatus: cfEntryPublishingStatus(entry), publishingStatus: cfEntryPublishingStatus(entry),
updatedAt: entry.sys.updatedAt, updatedAt: entry.sys.updatedAt,
publishedAt: entry.sys.publishedAt, publishedAt: entry.sys.publishedAt,
}; }
nodeArray.push(node); nodeArray.push(node)
}); })
return nodeArray; return nodeArray
}; }

View File

@ -1,10 +1,10 @@
import React, { type FC, type ReactElement, type SVGProps } from 'react'; import React, { type FC, type ReactElement, type SVGProps } from 'react'
import styled from 'styled-components'; import styled from 'styled-components'
export type SVGIcon = FC<SVGProps<SVGSVGElement>>; export type SVGIcon = FC<SVGProps<SVGSVGElement>>
export interface IconProps { export interface IconProps {
icon?: SVGIcon; icon?: SVGIcon
} }
export const Icon = (props: { id?: string }): ReactElement => { export const Icon = (props: { id?: string }): ReactElement => {
switch (props.id) { switch (props.id) {
@ -21,7 +21,7 @@ export const Icon = (props: { id?: string }): ReactElement => {
<path d="M113.66,23.69q-.345-.435-.72-.87A63.786,63.786,0,0,0,64.92.02C64.61.01,64.31,0,64,0s-.61.01-.92.02A63.789,63.789,0,0,0,14.93,22.98a6.164,6.164,0,0,0-.47.57.138.138,0,0,1-.03.04,63.8,63.8,0,0,0,0,80.82.138.138,0,0,1,.03.04,6.171,6.171,0,0,0,.47.57l.01.01a63.776,63.776,0,0,0,48.14,22.95c.614.02,1.226.02,1.84,0a63.786,63.786,0,0,0,48.02-22.8q.375-.435.72-.87a63.826,63.826,0,0,0,0-80.62ZM99.49,62.25a106.208,106.208,0,0,0-4.07-28.03,69.57,69.57,0,0,0,16.19-7.51,60.158,60.158,0,0,1,12.86,35.54ZM65.75,3.59c10.69,1.1,19.99,12.14,25.36,28.3a100.612,100.612,0,0,1-25.36,3.58Zm-3.5,31.88a100.612,100.612,0,0,1-25.36-3.58c5.37-16.16,14.67-27.2,25.36-28.3Zm0,3.5V62.25H32.01a104.4,104.4,0,0,1,3.85-27.02A104.159,104.159,0,0,0,62.25,38.97Zm0,26.78V89.03a104.161,104.161,0,0,0-26.39,3.74,104.4,104.4,0,0,1-3.85-27.02Zm0,26.78v31.88c-10.69-1.1-19.99-12.14-25.36-28.3A100.612,100.612,0,0,1,62.25,92.53Zm3.5,0a100.612,100.612,0,0,1,25.36,3.58c-5.37,16.16-14.67,27.2-25.36,28.3Zm0-3.5V65.75H95.99a104.477,104.477,0,0,1-3.85,27.03A103.333,103.333,0,0,0,65.75,89.03Zm0-26.78V38.97a103.33,103.33,0,0,0,26.39-3.75,104.478,104.478,0,0,1,3.85,27.03Zm43.6-38.26A66.171,66.171,0,0,1,94.37,30.9C90.53,19.48,84.76,10.4,77.89,5.11A60.6,60.6,0,0,1,109.35,23.99ZM50.11,5.11C43.24,10.4,37.47,19.48,33.63,30.9A67.031,67.031,0,0,1,18.65,24,60.513,60.513,0,0,1,50.11,5.11ZM16.39,26.7a69.605,69.605,0,0,0,16.19,7.53,106.07,106.07,0,0,0-4.07,28.02H3.53A60.188,60.188,0,0,1,16.39,26.7ZM28.51,65.75a106.069,106.069,0,0,0,4.07,28.02v.01a69.183,69.183,0,0,0-16.19,7.52A60.188,60.188,0,0,1,3.53,65.75ZM18.65,104a67.031,67.031,0,0,1,14.98-6.9c3.84,11.42,9.61,20.5,16.48,25.79A60.514,60.514,0,0,1,18.65,104Zm59.24,18.89c6.87-5.29,12.64-14.37,16.48-25.79a66.169,66.169,0,0,1,14.98,6.91A60.6,60.6,0,0,1,77.89,122.89Zm33.72-21.6a69.573,69.573,0,0,0-16.19-7.51,106.208,106.208,0,0,0,4.07-28.03h24.98A60.158,60.158,0,0,1,111.61,101.29Z" /> <path d="M113.66,23.69q-.345-.435-.72-.87A63.786,63.786,0,0,0,64.92.02C64.61.01,64.31,0,64,0s-.61.01-.92.02A63.789,63.789,0,0,0,14.93,22.98a6.164,6.164,0,0,0-.47.57.138.138,0,0,1-.03.04,63.8,63.8,0,0,0,0,80.82.138.138,0,0,1,.03.04,6.171,6.171,0,0,0,.47.57l.01.01a63.776,63.776,0,0,0,48.14,22.95c.614.02,1.226.02,1.84,0a63.786,63.786,0,0,0,48.02-22.8q.375-.435.72-.87a63.826,63.826,0,0,0,0-80.62ZM99.49,62.25a106.208,106.208,0,0,0-4.07-28.03,69.57,69.57,0,0,0,16.19-7.51,60.158,60.158,0,0,1,12.86,35.54ZM65.75,3.59c10.69,1.1,19.99,12.14,25.36,28.3a100.612,100.612,0,0,1-25.36,3.58Zm-3.5,31.88a100.612,100.612,0,0,1-25.36-3.58c5.37-16.16,14.67-27.2,25.36-28.3Zm0,3.5V62.25H32.01a104.4,104.4,0,0,1,3.85-27.02A104.159,104.159,0,0,0,62.25,38.97Zm0,26.78V89.03a104.161,104.161,0,0,0-26.39,3.74,104.4,104.4,0,0,1-3.85-27.02Zm0,26.78v31.88c-10.69-1.1-19.99-12.14-25.36-28.3A100.612,100.612,0,0,1,62.25,92.53Zm3.5,0a100.612,100.612,0,0,1,25.36,3.58c-5.37,16.16-14.67,27.2-25.36,28.3Zm0-3.5V65.75H95.99a104.477,104.477,0,0,1-3.85,27.03A103.333,103.333,0,0,0,65.75,89.03Zm0-26.78V38.97a103.33,103.33,0,0,0,26.39-3.75,104.478,104.478,0,0,1,3.85,27.03Zm43.6-38.26A66.171,66.171,0,0,1,94.37,30.9C90.53,19.48,84.76,10.4,77.89,5.11A60.6,60.6,0,0,1,109.35,23.99ZM50.11,5.11C43.24,10.4,37.47,19.48,33.63,30.9A67.031,67.031,0,0,1,18.65,24,60.513,60.513,0,0,1,50.11,5.11ZM16.39,26.7a69.605,69.605,0,0,0,16.19,7.53,106.07,106.07,0,0,0-4.07,28.02H3.53A60.188,60.188,0,0,1,16.39,26.7ZM28.51,65.75a106.069,106.069,0,0,0,4.07,28.02v.01a69.183,69.183,0,0,0-16.19,7.52A60.188,60.188,0,0,1,3.53,65.75ZM18.65,104a67.031,67.031,0,0,1,14.98-6.9c3.84,11.42,9.61,20.5,16.48,25.79A60.514,60.514,0,0,1,18.65,104Zm59.24,18.89c6.87-5.29,12.64-14.37,16.48-25.79a66.169,66.169,0,0,1,14.98,6.91A60.6,60.6,0,0,1,77.89,122.89Zm33.72-21.6a69.573,69.573,0,0,0-16.19-7.51,106.208,106.208,0,0,0,4.07-28.03h24.98A60.158,60.158,0,0,1,111.61,101.29Z" />
</svg> </svg>
</StyledIcon> </StyledIcon>
); )
case 'PAGE': case 'PAGE':
return ( return (
<StyledIcon> <StyledIcon>
@ -48,7 +48,7 @@ export const Icon = (props: { id?: string }): ReactElement => {
</g> </g>
</svg> </svg>
</StyledIcon> </StyledIcon>
); )
case 'APP': case 'APP':
return ( return (
<StyledIcon> <StyledIcon>
@ -64,7 +64,7 @@ export const Icon = (props: { id?: string }): ReactElement => {
</g> </g>
</svg> </svg>
</StyledIcon> </StyledIcon>
); )
case 'CART': case 'CART':
return ( return (
<StyledIcon> <StyledIcon>
@ -84,7 +84,7 @@ export const Icon = (props: { id?: string }): ReactElement => {
</g> </g>
</svg> </svg>
</StyledIcon> </StyledIcon>
); )
case 'FOLDER': case 'FOLDER':
return ( return (
<StyledIcon> <StyledIcon>
@ -92,7 +92,7 @@ export const Icon = (props: { id?: string }): ReactElement => {
<path d="M64 15v37a5.006 5.006 0 0 1-5 5H5a5.006 5.006 0 0 1-5-5V12a5.006 5.006 0 0 1 5-5h14.116a6.966 6.966 0 0 1 5.466 2.627l5 6.247A2.983 2.983 0 0 0 31.922 17H59a1 1 0 0 1 0 2H31.922a4.979 4.979 0 0 1-3.9-1.876l-5-6.247A4.976 4.976 0 0 0 19.116 9H5a3 3 0 0 0-3 3v40a3 3 0 0 0 3 3h54a3 3 0 0 0 3-3V15a3 3 0 0 0-3-3H30a1 1 0 0 1 0-2h29a5.006 5.006 0 0 1 5 5z" /> <path d="M64 15v37a5.006 5.006 0 0 1-5 5H5a5.006 5.006 0 0 1-5-5V12a5.006 5.006 0 0 1 5-5h14.116a6.966 6.966 0 0 1 5.466 2.627l5 6.247A2.983 2.983 0 0 0 31.922 17H59a1 1 0 0 1 0 2H31.922a4.979 4.979 0 0 1-3.9-1.876l-5-6.247A4.976 4.976 0 0 0 19.116 9H5a3 3 0 0 0-3 3v40a3 3 0 0 0 3 3h54a3 3 0 0 0 3-3V15a3 3 0 0 0-3-3H30a1 1 0 0 1 0-2h29a5.006 5.006 0 0 1 5 5z" />
</svg> </svg>
</StyledIcon> </StyledIcon>
); )
case 'LOVE': case 'LOVE':
return ( return (
<StyledIcon> <StyledIcon>
@ -101,7 +101,7 @@ export const Icon = (props: { id?: string }): ReactElement => {
<path d="M6.346 5a4.39 4.39 0 0 0-2.473 2.928 3.818 3.818 0 0 0 .656 3.272.515.515 0 0 0 .816-.629 2.8 2.8 0 0 1-.472-2.392 3.366 3.366 0 0 1 1.9-2.237A.515.515 0 0 0 6.346 5z" /> <path d="M6.346 5a4.39 4.39 0 0 0-2.473 2.928 3.818 3.818 0 0 0 .656 3.272.515.515 0 0 0 .816-.629 2.8 2.8 0 0 1-.472-2.392 3.366 3.366 0 0 1 1.9-2.237A.515.515 0 0 0 6.346 5z" />
</svg> </svg>
</StyledIcon> </StyledIcon>
); )
case 'SHORTCUT': case 'SHORTCUT':
return ( return (
<StyledIcon> <StyledIcon>
@ -113,7 +113,7 @@ export const Icon = (props: { id?: string }): ReactElement => {
</g> </g>
</svg> </svg>
</StyledIcon> </StyledIcon>
); )
} }
return ( return (
<StyledIcon> <StyledIcon>
@ -121,8 +121,8 @@ export const Icon = (props: { id?: string }): ReactElement => {
<path d="M10.044 0a10.044 10.044 0 1 0 10.044 10.043A10.043 10.043 0 0 0 10.044 0zm.047 15.033a4.99 4.99 0 1 1 4.99-4.99 4.989 4.989 0 0 1-4.99 4.99z" /> <path d="M10.044 0a10.044 10.044 0 1 0 10.044 10.043A10.043 10.043 0 0 0 10.044 0zm.047 15.033a4.99 4.99 0 1 1 4.99-4.99 4.989 4.989 0 0 1-4.99 4.99z" />
</svg> </svg>
</StyledIcon> </StyledIcon>
); )
}; }
export const StyledIcon = styled.span` export const StyledIcon = styled.span`
line-height: 1px; line-height: 1px;
@ -132,4 +132,4 @@ export const StyledIcon = styled.span`
width: 16px; width: 16px;
height: 16px; height: 16px;
margin: 2px 4px; margin: 2px 4px;
`; `

View File

@ -1,3 +1,3 @@
export * from './ContentTree'; export * from './ContentTree'
export * from './ContentTree.styled'; export * from './ContentTree.styled'
export * from './ContentTreeNode'; export * from './ContentTreeNode'

View File

@ -1,23 +1,23 @@
{ {
"compilerOptions": { "compilerOptions": {
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"removeComments": true, "removeComments": true,
"target": "es5", "target": "es5",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"noEmit": false, "noEmit": false,
"esModuleInterop": true, "esModuleInterop": true,
"module": "commonjs", "module": "commonjs",
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "react", "jsx": "react",
"rootDir": "src", "rootDir": "src",
"outDir": "dist", "outDir": "dist",
"declaration": true "declaration": true
}, },
"exclude": ["node_modules"], "exclude": ["node_modules"],
"include": ["src"] "include": ["src"]
} }

View File

@ -1 +1 @@
{"root":["./src/contenttree.styled.tsx","./src/contenttree.tsx","./src/contenttreenode.tsx","./src/contenttreeroot.tsx","./src/contenttreeutils.tsx","./src/icons.tsx","./src/index.ts"],"version":"5.7.3"} {"root":["./src/contenttree.styled.tsx","./src/contenttree.tsx","./src/contenttreenode.tsx","./src/contenttreeroot.tsx","./src/contenttreeutils.tsx","./src/icons.tsx","./src/index.ts"],"version":"5.8.2"}