-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #692 from contember/feat/blockrepeater
block repeater
- Loading branch information
Showing
50 changed files
with
1,405 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
## API Report File for "@contember/react-block-repeater" | ||
|
||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). | ||
```ts | ||
|
||
import { Context } from 'react'; | ||
import { EntityAccessor } from '@contember/react-binding'; | ||
import { JSX as JSX_2 } from 'react/jsx-runtime'; | ||
import { NamedExoticComponent } from 'react'; | ||
import { ReactElement } from 'react'; | ||
import { ReactNode } from 'react'; | ||
import { RepeaterAddItemIndex } from '@contember/react-repeater'; | ||
import { RepeaterProps } from '@contember/react-repeater'; | ||
import { SugaredRelativeSingleField } from '@contember/react-binding'; | ||
|
||
// @public (undocumented) | ||
export const Block: NamedExoticComponent<BlockProps>; | ||
|
||
// @public (undocumented) | ||
export interface BlockProps { | ||
// (undocumented) | ||
children: ReactNode; | ||
// (undocumented) | ||
form?: ReactNode; | ||
// (undocumented) | ||
label?: ReactNode; | ||
// (undocumented) | ||
name: string; | ||
} | ||
|
||
// @public (undocumented) | ||
export const BlockRepeater: NamedExoticComponent<BlockRepeaterProps>; | ||
|
||
// @public (undocumented) | ||
export const BlockRepeaterAddItemTrigger: ({ preprocess, index, type, ...props }: BlockRepeaterAddItemTriggerProps) => JSX_2.Element; | ||
|
||
// @public (undocumented) | ||
export interface BlockRepeaterAddItemTriggerProps { | ||
// (undocumented) | ||
children: ReactElement; | ||
// (undocumented) | ||
index?: RepeaterAddItemIndex; | ||
// (undocumented) | ||
preprocess?: EntityAccessor.BatchUpdatesHandler; | ||
// (undocumented) | ||
type: string; | ||
} | ||
|
||
// @internal (undocumented) | ||
export const BlockRepeaterConfigContext: Context< { | ||
discriminatedBy: SugaredRelativeSingleField['field']; | ||
blocks: BlocksMap; | ||
}>; | ||
|
||
// @public (undocumented) | ||
export type BlockRepeaterProps = { | ||
sortableBy: RepeaterProps['sortableBy']; | ||
discriminationField: SugaredRelativeSingleField['field']; | ||
} & RepeaterProps; | ||
|
||
// @public (undocumented) | ||
export type BlocksMap = Record<string, BlockProps>; | ||
|
||
// @public (undocumented) | ||
export const useBlockRepeaterConfig: () => { | ||
discriminatedBy: SugaredRelativeSingleField['field']; | ||
blocks: BlocksMap; | ||
}; | ||
|
||
// @public (undocumented) | ||
export const useBlockRepeaterCurrentBlock: () => BlockProps | undefined; | ||
|
||
// (No @packageDocumentation comment for this package) | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
import { Binding, PersistButton } from '../../lib/components/binding' | ||
import { Slots } from '../../lib/components/slots' | ||
import * as React from 'react' | ||
import { EntitySubTree, EntityView, Field, HasOne, StaticRender } from '@contember/interface' | ||
import { DefaultBlockRepeater } from '../../lib/components/block-repeater' | ||
import { ImageField, InputField, RadioEnumField, TextareaField } from '../../lib/components/form' | ||
import { UploadedImageView } from '../../lib/components/upload' | ||
import { Block } from '@contember/react-block-repeater' | ||
import { AlertOctagonIcon, ImageIcon, TextIcon } from 'lucide-react' | ||
import { cn } from '../../lib/utils/cn' | ||
|
||
export default () => <> | ||
<Binding> | ||
<Slots.Actions> | ||
<PersistButton /> | ||
</Slots.Actions> | ||
<EntitySubTree entity={'BlockList(unique=unique)'} setOnCreate={'(unique=unique)'}> | ||
<DefaultBlockRepeater field={'blocks'} sortableBy="order" discriminationField="type"> | ||
<Block | ||
name="text" | ||
label={<><TextIcon /> Text</>} | ||
form={<> | ||
<InputField field={'title'} label={'Title'} /> | ||
<TextareaField field={'content'} label={'Content'} /> | ||
</>} | ||
children={<> | ||
<div className="flex"> | ||
<div className="w-64 space-y-2"> | ||
<h2 className="text-xl font-bold"> | ||
<Field field={'title'} /> | ||
</h2> | ||
<p> | ||
<Field field={'content'} /> | ||
</p> | ||
</div> | ||
</div> | ||
</> | ||
} | ||
/> | ||
<Block | ||
name="image" | ||
label={<><ImageIcon /> Image</>} | ||
form={<> | ||
<InputField field={'title'} label={'Title'} /> | ||
<ImageField baseField={'image'} urlField="url" label={'Image'} /> | ||
</>} | ||
children={<> | ||
<div className="flex"> | ||
<div className="flex flex-col gap-2"> | ||
<div className="space-y-2"> | ||
<h2 className="text-xl font-bold"> | ||
<Field field={'title'} /> | ||
</h2> | ||
<div className="border"> | ||
<HasOne field="image"> | ||
<UploadedImageView urlField={'url'} /> | ||
</HasOne> | ||
</div> | ||
|
||
</div> | ||
</div> | ||
</div> | ||
</>} | ||
/> | ||
<Block | ||
name="textWithImage" | ||
label={<><span className="inline-flex gap-1"><ImageIcon /> <TextIcon /></span> Image with text</>} | ||
form={<> | ||
<InputField field={'title'} label={'Title'} /> | ||
<TextareaField field={'content'} label={'Content'} /> | ||
<ImageField baseField={'image'} urlField="url" label={'Image'} /> | ||
<RadioEnumField field={'imagePosition'} options={{ left: 'Left', right: 'Right' }} /> | ||
</>} | ||
children={<> | ||
|
||
<EntityView render={it => { | ||
return ( | ||
<div className="flex"> | ||
<div className={cn('border', it.getField('imagePosition').value === 'right' ? 'order-2' : '')}> | ||
<HasOne field="image"> | ||
<UploadedImageView urlField={'url'} /> | ||
</HasOne> | ||
</div> | ||
<div className="w-64 px-4 space-y-2"> | ||
<h2 className="text-xl font-bold"> | ||
<Field field={'title'} /> | ||
</h2> | ||
<p> | ||
<Field field={'content'} /> | ||
</p> | ||
</div> | ||
</div> | ||
) | ||
}} /> | ||
</>} | ||
/> | ||
<Block | ||
name="hero" | ||
label={<><AlertOctagonIcon /> Hero</>} | ||
form={<> | ||
<InputField field={'title'} label={'Title'} /> | ||
<TextareaField field={'content'} label={'Content'} /> | ||
<InputField field={'color'} label={'Color'} inputProps={{ type: 'color' }} /> | ||
</>} | ||
children={<> | ||
<StaticRender> | ||
<Field field={'color'} /> | ||
</StaticRender> | ||
<EntityView render={it => { | ||
return ( | ||
<div className="flex"> | ||
<div className="w-96 p-4 gap-2 flex flex-col items-center" style={{ | ||
backgroundColor: it.getField<string>('color').value ?? undefined, | ||
color: getTextColor(it.getField<string>('color').value ?? ''), | ||
}}> | ||
<h2 className="text-4xl font-bold"> | ||
<Field field={'title'} /> | ||
</h2> | ||
<p className="text-xl"> | ||
<Field field={'content'} /> | ||
</p> | ||
</div> | ||
</div> | ||
) | ||
}} /> | ||
</>} | ||
/> | ||
</DefaultBlockRepeater> | ||
</EntitySubTree> | ||
</Binding> | ||
</> | ||
|
||
function getTextColor(backgroundColor: string) { | ||
if (!backgroundColor) { | ||
return 'black' | ||
} | ||
// Extract RGB values from a color in hex format | ||
const r = parseInt(backgroundColor.slice(1, 3), 16) | ||
const g = parseInt(backgroundColor.slice(3, 5), 16) | ||
const b = parseInt(backgroundColor.slice(5, 7), 16) | ||
|
||
// Calculate the luminance | ||
const luminance = 0.2126 * (r / 255) ** 2.2 + | ||
0.7152 * (g / 255) ** 2.2 + | ||
0.0722 * (b / 255) ** 2.2 | ||
|
||
// Use a luminance threshold of 0.179 to decide on text color | ||
return luminance > 0.179 ? 'black' : 'white' | ||
} | ||
|
||
|
||
export const withoutDualRender = () => <> | ||
<Binding> | ||
<Slots.Actions> | ||
<PersistButton /> | ||
</Slots.Actions> | ||
<EntitySubTree entity={'BlockList(unique=unique)'} setOnCreate={'(unique=unique)'}> | ||
<DefaultBlockRepeater field={'blocks'} sortableBy="order" discriminationField="type"> | ||
<Block | ||
name="text" | ||
label={<><TextIcon /> Text</>} | ||
> | ||
<InputField field={'title'} label={'Title'} /> | ||
<TextareaField field={'content'} label={'Content'} /> | ||
</Block> | ||
<Block | ||
name="image" | ||
label={<><ImageIcon /> Image</>} | ||
> | ||
<InputField field={'title'} label={'Title'} /> | ||
<ImageField baseField={'image'} urlField="url" label={'Image'} /> | ||
</Block> | ||
<Block | ||
name="textWithImage" | ||
label={<><span className="inline-flex gap-1"><ImageIcon /> <TextIcon /></span> Image with text</>} | ||
> | ||
<InputField field={'title'} label={'Title'} /> | ||
<TextareaField field={'content'} label={'Content'} /> | ||
<ImageField baseField={'image'} urlField="url" label={'Image'} /> | ||
<RadioEnumField field={'imagePosition'} options={{ left: 'Left', right: 'Right' }} /> | ||
</Block> | ||
<Block | ||
name="hero" | ||
label={<><AlertOctagonIcon /> Hero</>} | ||
> | ||
<InputField field={'title'} label={'Title'} /> | ||
<TextareaField field={'content'} label={'Content'} /> | ||
<InputField field={'color'} label={'Color'} inputProps={{ type: 'color' }} /> | ||
</Block> | ||
</DefaultBlockRepeater> | ||
</EntitySubTree> | ||
</Binding> | ||
</> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.