Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
matej21 committed Mar 6, 2024
1 parent 0e23dbc commit e6390c2
Show file tree
Hide file tree
Showing 124 changed files with 3,898 additions and 350 deletions.
2 changes: 2 additions & 0 deletions ee/admin-server/Dockerfile
Expand Up @@ -47,6 +47,8 @@ COPY ./packages/react-richtext-renderer/package.json ././packages/react-richtext
COPY ./packages/react-routing/package.json ././packages/react-routing/package.json
COPY ./packages/react-select/package.json ././packages/react-select/package.json
COPY ./packages/react-slots/package.json ././packages/react-slots/package.json
COPY ./packages/react-uploader/package.json ././packages/react-uploader/package.json
COPY ./packages/react-uploader-dropzone/package.json ././packages/react-uploader-dropzone/package.json
COPY ./packages/react-utils/package.json ././packages/react-utils/package.json
COPY ./packages/ui/package.json ././packages/ui/package.json
COPY ./packages/utilities/package.json ././packages/utilities/package.json
Expand Down
6 changes: 3 additions & 3 deletions packages/admin-sandbox/package.json
Expand Up @@ -20,9 +20,9 @@
"graphql": "^16.8.1"
},
"dependencies": {
"@contember/schema": "^1.3.6",
"@contember/schema-definition": "^1.3.6",
"@contember/schema-utils": "^1.3.6",
"@contember/schema": "^1.4.0-beta.1",
"@contember/schema-definition": "^1.4.0-beta.1",
"@contember/schema-utils": "^1.4.0-beta.1",
"lucide-react": "^0.302.0",
"react": "^18.2.0",
"react-content-loader": "^6.2.1",
Expand Down
3 changes: 2 additions & 1 deletion packages/admin/package.json
Expand Up @@ -72,14 +72,15 @@
"@contember/react-leaflet-fields-ui": "workspace:*",
"@contember/react-multipass-rendering": "workspace:*",
"@contember/react-routing": "workspace:*",
"@contember/react-uploader": "workspace:*",
"@contember/react-utils": "workspace:*",
"@contember/ui": "workspace:*",
"@contember/utilities": "workspace:*",
"blueimp-md5": "2.19.0",
"is-hotkey": "0.2.0",
"lucide-react": "^0.302.0",
"qrcode-generator": "^1.4.4",
"react-dropzone": "10.2.2",
"react-dropzone": "^14.2.3",
"react-error-boundary": "4.0.12",
"react-sortable-hoc": "2.0.0",
"slate": "0.73.1",
Expand Down
@@ -1,6 +1,5 @@
import type { EntityAccessor, Environment } from '@contember/react-binding'
import type { EntityAccessor, Environment, ErrorAccessorHolder } from '@contember/react-binding'
import type { ReactNode } from 'react'
import { AccessorErrorsHolder } from '@contember/react-binding-ui'

export interface FileDataExtractorPopulateFieldsOptions<
ExtractedData = unknown,
Expand Down Expand Up @@ -40,5 +39,5 @@ export interface FileDataExtractor<ExtractedData = unknown, UploadResult = unkno
populateFields: (
options: FileDataExtractorPopulateFieldsOptions<ExtractedData, UploadResult, AcceptArtifacts>,
) => void
getErrorsHolders?: (options: FileDataExtractorGetErrorsOptions) => AccessorErrorsHolder[]
getErrorsHolders?: (options: FileDataExtractorGetErrorsOptions) => ErrorAccessorHolder[]
}
@@ -1,6 +1,5 @@
import { EntityAccessor, Environment } from '@contember/react-binding'
import { EntityAccessor, Environment, ErrorAccessorHolder } from '@contember/react-binding'
import { ReactElement } from 'react'
import { AccessorErrorsHolder } from '@contember/react-binding-ui'
import { AcceptFileOptions, FullFileKind } from '../fileKinds'

export interface AcceptedFile<AcceptArtifacts = any> {
Expand All @@ -12,7 +11,7 @@ export type ResolvedFileEntity =
& {
parentEntity: EntityAccessor
destroy?: () => void
getErrorHolders: () => AccessorErrorsHolder[]
getErrorHolders: () => ErrorAccessorHolder[]
}
& (
| {
Expand Down
@@ -0,0 +1,23 @@
import { BindingError, EntityAccessor, Environment, QueryLanguage } from '@contember/react-binding'
import { EntityConnectorFactory } from '../internalComponents/hooks/useConnectSelectedEntities'

export const createEntityConnectorFactory = (env: Environment, ...baseEntity: (string | undefined)[]): EntityConnectorFactory => {
if (!baseEntity[baseEntity.length - 1]) {
throw new BindingError('Cannot use "fileSelectionComponent" when "baseField" prop is not set. For more information, please consult the documentation.')
}
const path = baseEntity.filter(
(it): it is string => it !== undefined)
.map(it => QueryLanguage.desugarRelativeSingleEntity(it, env))
.flatMap(it => it.hasOneRelationPath)

return selected => {
const connector = (parent: EntityAccessor) => {
const parentEntity = parent.getRelativeSingleEntity({
hasOneRelationPath: path.slice(0, -1),
})
parentEntity.connectEntityAtField(path[path.length - 1].field, selected)
}
connector.entity = selected
return connector
}
}
Expand Up @@ -131,7 +131,7 @@ export const useNormalizedUploadState = ({
const dropzoneState = useDropzone({
onDrop,
disabled: isMutating,
accept: fileHandler.acceptedMimes ?? undefined,
// accept: fileHandler.acceptedMimes ?? undefined, todo
multiple: isMultiple,
noKeyboard: true, // This would normally be absolutely henious but there is a keyboard-focusable button inside.
})
Expand Down
1 change: 1 addition & 0 deletions packages/admin/src/tsconfig.json
Expand Up @@ -21,6 +21,7 @@
{ "path": "../../react-multipass-rendering/src" },
{ "path": "../../react-routing/src" },
{ "path": "../../react-utils/src" },
{ "path": "../../react-uploader/src" },
{ "path": "../../utilities/src" },
{ "path": "../../ui/src" }
]
Expand Down
4 changes: 4 additions & 0 deletions packages/binding/src/accessors/EntityListAccessor.ts
Expand Up @@ -129,6 +129,10 @@ class EntityListAccessor implements Errorable {
public getChildEntityById(id: EntityId): EntityAccessor {
return this.operations.getChildEntityById(this.state, id)
}

public getParent(): EntityAccessor | undefined {
return this.state.blueprint.parent?.getAccessor()
}
}

namespace EntityListAccessor {
Expand Down
4 changes: 4 additions & 0 deletions packages/binding/src/accessors/ErrorAccessor.ts
Expand Up @@ -37,3 +37,7 @@ namespace ErrorAccessor {
} : error
}
export { ErrorAccessor }

export interface ErrorAccessorHolder {
readonly errors: ErrorAccessor | undefined
}
5 changes: 5 additions & 0 deletions packages/binding/src/accessors/FieldAccessor.ts
Expand Up @@ -6,6 +6,7 @@ import type { BatchUpdatesOptions } from './BatchUpdatesOptions'
import type { Errorable } from './Errorable'
import { ErrorAccessor } from './ErrorAccessor'
import type { SchemaColumn } from '../core/schema/SchemaColumn'
import { EntityAccessor } from './EntityAccessor'

class FieldAccessor<Value extends FieldValue = FieldValue> implements Errorable {
constructor(
Expand Down Expand Up @@ -69,6 +70,10 @@ class FieldAccessor<Value extends FieldValue = FieldValue> implements Errorable
public get asUuid(): UuidFieldHelper {
return new UuidFieldHelper(this.getAccessor as FieldAccessor.GetFieldAccessor<any>)
}

public getParent(): EntityAccessor {
return this.state.parent.getAccessor()
}
}
namespace FieldAccessor {
export const userAgent = 'user'
Expand Down
@@ -0,0 +1,53 @@
import { ComponentType, ReactNode, useCallback } from 'react'
import { Slot } from '@radix-ui/react-slot'
import { BindingError, EntityListAccessor, ErrorPersistResult, SuccessfulPersistResult, SugaredRelativeSingleEntity, useEntity, useMutationState, usePersist } from '@contember/react-binding'


const SlotButton = Slot as ComponentType<React.ButtonHTMLAttributes<HTMLButtonElement>>

export interface DisconnectEntityTriggerProps {
immediatePersist?: true
children: ReactNode
onPersistSuccess?: (result: SuccessfulPersistResult) => void
onPersistError?: (result: ErrorPersistResult) => void
field?: SugaredRelativeSingleEntity['field']
}

export const DisconnectEntityTrigger = ({ immediatePersist, onPersistError, onPersistSuccess, field, ...props }: DisconnectEntityTriggerProps) => {
const entity = useEntity()
const triggerPersist = usePersist()
const isMutating = useMutationState()
const onClick = useCallback(() => {
if (field) {
entity.disconnectEntityAtField({ field })
} else {
const parent = entity.getParent()
if (!parent) {
throw new BindingError('Cannot disconnect root entity')
}
if (parent instanceof EntityListAccessor) {
parent.disconnectEntity(entity)
} else {
const subTreeNode = entity.environment.getSubTreeNode()
if (subTreeNode.type !== 'entity') {
throw new BindingError('Cannot disconnect root entity')
}
parent.disconnectEntityAtField({ field: subTreeNode.field.name })
}
}

if (immediatePersist) {
triggerPersist()
.then(onPersistSuccess)
.catch(onPersistError)
}
}, [field, immediatePersist, entity, triggerPersist, onPersistSuccess, onPersistError])

return (
<SlotButton
onClick={onClick}
disabled={isMutating ? true : undefined}
{...props}
/>
)
}
1 change: 1 addition & 0 deletions packages/interface/src/components/binding/index.ts
@@ -1,5 +1,6 @@
export * from './ClearFieldTrigger'
export * from './DeleteEntityTrigger'
export * from './DisconnectEntityTrigger'
export * from './PersistTrigger'
export * from './EntityBeforePersist'
export * from './RedirectOnPersist'
13 changes: 12 additions & 1 deletion packages/playground/admin/app/components/navigation.tsx
@@ -1,4 +1,4 @@
import { ArchiveIcon, BrushIcon, FormInputIcon, GripVertical, HomeIcon, KanbanIcon, TableIcon } from 'lucide-react'
import { ArchiveIcon, BrushIcon, FormInputIcon, GripVertical, HomeIcon, KanbanIcon, TableIcon, UploadIcon } from 'lucide-react'
import { MenuList } from '../../lib/components/ui/menu'


Expand Down Expand Up @@ -107,6 +107,17 @@ export const Navigation = () => {
},
],
},
{
icon: <UploadIcon size={16} />,
label: 'Upload',
subItems: [
{
icon: line,
label: 'Image upload',
to: 'upload/image',
},
],
},
]}
/>
</div>
Expand Down
10 changes: 7 additions & 3 deletions packages/playground/admin/app/pages/index.tsx
@@ -1,3 +1,7 @@
export default (<>
Hello!
</>)
import * as React from 'react'

export default () => {
debugger
const foo = React.useMemo(() => 1, [])
return <>Hello!</>
}
2 changes: 1 addition & 1 deletion packages/playground/admin/app/pages/ui/toast.tsx
Expand Up @@ -12,7 +12,7 @@ const ToastTrigger = ({ type, description, title, ...props }: {
const showToast = useShowToast()
const onClick = () => {
showToast(
<ToastContent title={title} description={description} />,
<ToastContent title={title} children={description} />,
{
type,
},
Expand Down
80 changes: 80 additions & 0 deletions packages/playground/admin/app/pages/upload.tsx
@@ -0,0 +1,80 @@
import { EntitySubTree } from '@contember/react-binding'
import * as React from 'react'
import { Binding, PersistButton } from '../../lib/components/binding'
import { Slots } from '../../lib/components/slots'
import { ImageField } from '../../lib/components/form'


export const image = () => <>

<Binding>
<Slots.Actions><PersistButton /></Slots.Actions>
<EntitySubTree entity="UploadRoot(unique = unique)" setOnCreate="(unique = unique)">
<ImageField
baseField="image"
urlField="url"
widthField="width"
heightField="height"
fileNameField="meta.fileName"
fileSizeField="meta.fileSize"
fileTypeField="meta.fileType"
lastModifiedField="meta.lastModified"
label="Image file"
description="Some description of the image file."
/>
</EntitySubTree>
</Binding>
</>


export const imageList = () => <>

{/*<MultiUploader field={'multipleBasicImageList'}*/}
{/* sortableBy={'order'}*/}
{/* fileHandler={new SingleKindFileHandler(getStockImageFileKind({*/}
{/* baseField: 'image',*/}
{/* urlField: 'url',*/}
{/* uploader: s3FileUploader,*/}
{/* }))}>*/}
{/* {Dropzone}*/}
{/* <UploaderSortable>*/}

{/* <div style={{ display: 'flex' }}>*/}
{/* <UploaderSortableEachItem>*/}
{/* <div style={{ width: '25%' }}>*/}
{/* <div style={{ position: 'relative' }}>*/}
{/* <UploaderSortableDropIndicator position={'before'}>*/}
{/* {dropIndicatorEl}*/}
{/* </UploaderSortableDropIndicator>*/}
{/* </div>*/}
{/* <UploaderSortableItemNode>*/}
{/* <Box>*/}
{/* <UploaderSortableItemActivator>*/}
{/* <div>move</div>*/}
{/* </UploaderSortableItemActivator>*/}

{/* <UploaderFileEntity>*/}
{/* <Field field={'url'} />*/}
{/* /!*{FilePreview}*!/*/}
{/* </UploaderFileEntity>*/}
{/* /!*{UploadingState}*!/*/}
{/* </Box>*/}
{/* </UploaderSortableItemNode>*/}
{/* <div style={{ position: 'relative' }}>*/}
{/* <UploaderSortableDropIndicator position={'after'}>*/}
{/* {dropIndicatorEl}*/}
{/* </UploaderSortableDropIndicator>*/}
{/* </div>*/}
{/* </div>*/}
{/* </UploaderSortableEachItem>*/}
{/* </div>*/}
{/* <UploaderSortableDragOverlay>*/}
{/* <Box>*/}
{/* <UploaderFileEntity>*/}
{/* Dragging file <Field field={'url'} />*/}
{/* </UploaderFileEntity>*/}
{/* </Box>*/}
{/* </UploaderSortableDragOverlay>*/}
{/* </UploaderSortable>*/}
{/*</MultiUploader>*/}
</>
4 changes: 2 additions & 2 deletions packages/playground/admin/lib/components/binding/persist.tsx
Expand Up @@ -37,15 +37,15 @@ export const usePersistErrorHandler = () => {
showToast(
<ToastContent
title={dict.persist.invalidInputError}
description={<ul>{errorList.map((it, i) => <li key={i}>{it}</li>)}</ul>}
children={<ul>{errorList.map((it, i) => <li key={i}>{it}</li>)}</ul>}
/>, {
type: 'error',
})
} else if (result.type === 'invalidResponse') {
showToast(
<ToastContent
title={dict.persist.invalidResponseError}
description={dict.persist.invalidResponseErrorDetails}
children={dict.persist.invalidResponseErrorDetails}
/>, {
type: 'error',
},
Expand Down
6 changes: 3 additions & 3 deletions packages/playground/admin/lib/components/dev/login-panel.tsx
@@ -1,6 +1,6 @@
import { SyntheticEvent, useCallback, useState } from 'react'
import { ToastContent, useShowToast } from '../ui/toast'
import { FieldContainer } from '../form'
import { StandaloneFormContainer } from '../form'
import { Input } from '../ui/input'
import { Button } from '../ui/button'
import { useSessionTokenWithMeta, useSetSessionToken } from '@contember/react-client'
Expand Down Expand Up @@ -40,9 +40,9 @@ export const LoginWithEmail = () => {
return <>
<form onSubmit={submit}>
<div>
<FieldContainer label={'E-mail'}>
<StandaloneFormContainer label={'E-mail'}>
<Input value={email} onChange={e => setEmail(e.target.value)} />
</FieldContainer>
</StandaloneFormContainer>
<Button type="submit" disabled={isSubmitting}>Login</Button>
</div>
</form>
Expand Down

0 comments on commit e6390c2

Please sign in to comment.