diff --git a/packages/noya-compiler/src/index.ts b/packages/noya-compiler/src/index.ts index 3d0395e0e..32f180900 100644 --- a/packages/noya-compiler/src/index.ts +++ b/packages/noya-compiler/src/index.ts @@ -276,7 +276,7 @@ function Frame(props: React.ComponentProps) { name: 'app', dependencies: { react: '^18', - '@chakra-ui/icons': '^2', + '@chakra-ui/icons': '^1', '@chakra-ui/react': '^1', '@emotion/react': '^11', '@emotion/styled': '^11', diff --git a/packages/noya-file-format/src/types.ts b/packages/noya-file-format/src/types.ts index 407ccbd34..58ac9fd68 100644 --- a/packages/noya-file-format/src/types.ts +++ b/packages/noya-file-format/src/types.ts @@ -1183,6 +1183,7 @@ export type OverrideValue = { overrideName: OverrideName; value: boolean | string | Uuid | FileRef | DataRef | ResolvedBlockData; }; + /** * Slice layers allow the content beneath their frame to be exported */ diff --git a/packages/noya-state/src/reducers/interactionReducer.ts b/packages/noya-state/src/reducers/interactionReducer.ts index 45618c27a..4d8b956b4 100644 --- a/packages/noya-state/src/reducers/interactionReducer.ts +++ b/packages/noya-state/src/reducers/interactionReducer.ts @@ -67,6 +67,7 @@ export type BlockDefinition = { placeholderText?: string; editorVersion?: number; usesResolver?: boolean; + isPassthrough?: boolean; }; export type InteractionMethod = 'mouse' | 'keyboard'; diff --git a/packages/site/src/__tests__/__snapshots__/editor.test.ts.snap b/packages/site/src/__tests__/__snapshots__/editor.test.ts.snap index 0bcb146ec..d486261c2 100644 --- a/packages/site/src/__tests__/__snapshots__/editor.test.ts.snap +++ b/packages/site/src/__tests__/__snapshots__/editor.test.ts.snap @@ -38,6 +38,18 @@ Array [ "symbolId": "cdf2346b-cb21-4f23-8d93-7c4fb2e3a5a0", "type": "paragraph", }, + Object { + "children": Array [ + Object { + "text": "", + }, + ], + "label": "Link", + "layerId": "eee85c94-7361-4bcf-8afb-f59c6e8661f7", + "placeholder": "Learn more #text-slate-800 #icon-arrow-forward", + "symbolId": "5be80b87-7bd6-4090-8947-316d54064544", + "type": "paragraph", + }, Object { "children": Array [ Object { diff --git a/packages/site/src/__tests__/__snapshots__/renderBlock.test.ts.snap b/packages/site/src/__tests__/__snapshots__/renderBlock.test.ts.snap index d0e89fc01..4841315d5 100644 --- a/packages/site/src/__tests__/__snapshots__/renderBlock.test.ts.snap +++ b/packages/site/src/__tests__/__snapshots__/renderBlock.test.ts.snap @@ -61,7 +61,7 @@ export { App }; \\"name\\": \\"app\\", \\"dependencies\\": { \\"react\\": \\"^18\\", - \\"@chakra-ui/icons\\": \\"^2\\", + \\"@chakra-ui/icons\\": \\"^1\\", \\"@chakra-ui/react\\": \\"^1\\", \\"@emotion/react\\": \\"^11\\", \\"@emotion/styled\\": \\"^11\\" @@ -85,15 +85,22 @@ exports[`hero default 1`] = ` {'Turn great ideas into new possibilities.'} - + + + ; " `; -exports[`hero left aligned default 1`] = ` +exports[`hero left aligned 1`] = ` " {'Turn great ideas into new possibilities.'} - + + + ; " `; -exports[`hero with bg default 1`] = ` +exports[`hero with bg 1`] = ` " {'Turn great ideas into new possibilities.'} - + + + ; " @@ -181,6 +197,15 @@ exports[`icon with color 1`] = ` " `; +exports[`link with icon 1`] = ` +" + + {'Test'} + +; +" +`; + exports[`text default 1`] = ` " diff --git a/packages/site/src/__tests__/renderBlock.test.ts b/packages/site/src/__tests__/renderBlock.test.ts index aa38b6864..6f9489609 100644 --- a/packages/site/src/__tests__/renderBlock.test.ts +++ b/packages/site/src/__tests__/renderBlock.test.ts @@ -18,6 +18,7 @@ import { heroSymbolV2Id, iconSymbolId, imageSymbolId, + linkSymbolId, textSymbolId, } from '../ayon/blocks/symbolIds'; @@ -69,6 +70,22 @@ describe('text', () => { }); }); +describe('link', () => { + // TODO: Icon doesn't get added to exported code + test('with icon', () => { + const symbol = SketchModel.symbolInstance({ + symbolID: linkSymbolId, + blockText: 'Test #icon-arrow-right', + frame: SketchModel.rect({ + width: 100, + height: 40, + }), + }); + + expect(generate(symbol)).toMatchSnapshot(); + }); +}); + describe('icon', () => { test('default', () => { const symbol = SketchModel.symbolInstance({ @@ -108,10 +125,8 @@ describe('hero', () => { expect(generate(symbol)).toMatchSnapshot(); }); -}); -describe('hero left aligned', () => { - test('default', () => { + test('left aligned', () => { const symbol = SketchModel.symbolInstance({ symbolID: heroSymbolV2Id, frame: SketchModel.rect({ @@ -123,10 +138,8 @@ describe('hero left aligned', () => { expect(generate(symbol)).toMatchSnapshot(); }); -}); -describe('hero with bg', () => { - test('default', () => { + test('with bg', () => { const symbol = SketchModel.symbolInstance({ symbolID: heroSymbolV2Id, frame: SketchModel.rect({ diff --git a/packages/site/src/ayon/Widget.tsx b/packages/site/src/ayon/Widget.tsx index 43dddb70d..d2e1b4aef 100644 --- a/packages/site/src/ayon/Widget.tsx +++ b/packages/site/src/ayon/Widget.tsx @@ -340,11 +340,31 @@ export const Widget = forwardRef(function Widget( }, }; - const { container: containerBlockProps, children: childrenBlockProps } = - getRenderableBlockProps({ - props: blockProps, - block: Block, - }); + const { + container: containerBlockProps, + children: childrenBlockProps, + extraParameters, + } = getRenderableBlockProps({ + props: blockProps, + block: Block, + }); + + const flattenedChildrenBlockProps = childrenBlockProps.flatMap( + (childProps) => { + const block = blockProps.getBlock(childProps.symbolId); + + if (block.isPassthrough) { + return getRenderableBlockProps({ + props: childProps, + block: block, + extraParameters, + overrideValues: blockProps.layer?.overrideValues, + }).children; + } + + return [childProps]; + }, + ); const containerLayer = SketchModel.symbolInstance({ symbolID: boxSymbolId, @@ -354,7 +374,7 @@ export const Widget = forwardRef(function Widget( const actions: Action[] = []; - const layers = childrenBlockProps.flatMap((child) => { + const layers = flattenedChildrenBlockProps.flatMap((child) => { if (!child.dataSet) return []; const element = document.querySelector( diff --git a/packages/site/src/ayon/blocks/LinkBlock.tsx b/packages/site/src/ayon/blocks/LinkBlock.tsx new file mode 100644 index 000000000..b13e89750 --- /dev/null +++ b/packages/site/src/ayon/blocks/LinkBlock.tsx @@ -0,0 +1,63 @@ +import { ArrowForwardIcon } from '@chakra-ui/icons'; +import { Link } from '@chakra-ui/react'; +import { BlockDefinition } from 'noya-state'; +import React from 'react'; +import { parseBlock } from '../parse'; +import { linkSymbol } from './symbols'; +import { + getBlockClassName, + hasClassGroup, + tailwindTextClasses, +} from './tailwind'; + +const placeholderText = 'Read More'; + +const parser = 'regular'; + +export const LinkBlock: BlockDefinition = { + symbol: linkSymbol, + parser, + hashtags: [ + 'icon-arrow-forward', + 'left', + 'center', + 'right', + ...tailwindTextClasses, + 'flex-1', + ], + placeholderText, + infer: ({ frame, blockText }) => 0, + render: (props) => { + const { content, parameters } = parseBlock(props.blockText, parser, { + placeholder: placeholderText, + }); + + const hashtags = Object.keys(parameters); + const hasColor = hasClassGroup('textColor', hashtags); + + return ( + + {content} + {parameters['icon-arrow-forward'] && ( + <> + {' '} + + + )} + + ); + }, +}; diff --git a/packages/site/src/ayon/blocks/SpacerBlock.tsx b/packages/site/src/ayon/blocks/SpacerBlock.tsx index 820a53934..f48ccbfcc 100644 --- a/packages/site/src/ayon/blocks/SpacerBlock.tsx +++ b/packages/site/src/ayon/blocks/SpacerBlock.tsx @@ -10,6 +10,7 @@ export const SpacerBlock: BlockDefinition = { parser: 'regular', hashtags: tailwindBlockClasses, infer: ({ frame, blockText }) => 0, + isPassthrough: true, render: (props) => { const { parameters } = parseBlock(props.blockText, 'regular'); const hashtags = Object.keys(parameters); diff --git a/packages/site/src/ayon/blocks/blockMetadata.ts b/packages/site/src/ayon/blocks/blockMetadata.ts index 7a04c9951..dc6344a9f 100644 --- a/packages/site/src/ayon/blocks/blockMetadata.ts +++ b/packages/site/src/ayon/blocks/blockMetadata.ts @@ -16,6 +16,7 @@ import { iconSymbolId, imageSymbolId, inputSymbolId, + linkSymbolId, radioSymbolId, selectSymbolId, sidebarSymbolId, @@ -71,6 +72,11 @@ export const blockMetadata: Record = { category: 'element', preferredSize: { width: 200, height: 40 }, }, + [linkSymbolId]: { + name: 'Link', + category: 'element', + preferredSize: { width: 200, height: 40 }, + }, [boxSymbolId]: { name: 'Box', category: 'element', diff --git a/packages/site/src/ayon/blocks/blocks.ts b/packages/site/src/ayon/blocks/blocks.ts index 04ce26cf8..7e1105465 100644 --- a/packages/site/src/ayon/blocks/blocks.ts +++ b/packages/site/src/ayon/blocks/blocks.ts @@ -1,3 +1,4 @@ +import Sketch from 'noya-file-format'; import { BlockDefinition } from 'noya-state'; import { AvatarBlock } from './AvatarBlock'; import { BoxBlock } from './BoxBlock'; @@ -18,18 +19,39 @@ import { HeroBlockV1 } from './HeroBlockV1'; import { IconBlock } from './IconBlock'; import { ImageBlock } from './ImageBlock'; import { InputBlock } from './InputBlock'; +import { LinkBlock } from './LinkBlock'; import { RadioBlock } from './RadioBlock'; +import { renderStack } from './render'; import { SelectBlock } from './SelectBlock'; import { SidebarBlock } from './SidebarBlock'; import { SignInBlock } from './SignInBlock'; import { SpacerBlock } from './SpacerBlock'; import { SwitchBlock } from './SwitchBlock'; +import { heroButtonRowSymbol } from './symbols'; import { TableBlock } from './TableBlock'; import { TextareaBlock } from './TextareaBlock'; import { TextBlock } from './TextBlock'; import { TileCardBlock } from './TileCardBlock'; import { WriteBlock } from './WriteBlock'; +function createPassthroughBlock(symbol: Sketch.SymbolMaster): BlockDefinition { + return { + symbol, + infer: () => 0, + parser: 'regular', + isPassthrough: true, + editorVersion: 2, + render: (props) => + renderStack({ + props, + block: { + placeholderText: '', + symbol, + }, + }), + }; +} + export const Blocks: Record = { [AvatarBlock.symbol.symbolID]: AvatarBlock, [ButtonBlock.symbol.symbolID]: ButtonBlock, @@ -59,11 +81,13 @@ export const Blocks: Record = { [CardBlock.symbol.symbolID]: CardBlock, [TileCardBlock.symbol.symbolID]: TileCardBlock, [SignInBlock.symbol.symbolID]: SignInBlock, + [LinkBlock.symbol.symbolID]: LinkBlock, + [heroButtonRowSymbol.symbolID]: createPassthroughBlock(heroButtonRowSymbol), }; export const allInsertableBlocks = Object.values(Blocks).filter( - ({ symbol }) => - symbol.name !== 'Spacer' && /update/i.test(symbol.name) === false, + ({ isPassthrough, symbol }) => + !isPassthrough && /update/i.test(symbol.name) === false, ); export const allInsertableSymbols = allInsertableBlocks.map( diff --git a/packages/site/src/ayon/blocks/flattenPassthroughLayers.tsx b/packages/site/src/ayon/blocks/flattenPassthroughLayers.tsx new file mode 100644 index 000000000..fb33559f7 --- /dev/null +++ b/packages/site/src/ayon/blocks/flattenPassthroughLayers.tsx @@ -0,0 +1,19 @@ +import Sketch from 'noya-file-format'; +import { Layers } from 'noya-state'; +import { Blocks } from './blocks'; + +export function flattenPassthroughLayers( + symbolMaster: Sketch.SymbolMaster, +): Sketch.SymbolInstance[] { + return symbolMaster.layers + .filter(Layers.isSymbolInstance) + .flatMap((layer) => { + const block = Blocks[layer.symbolID]; + + if (block && block.isPassthrough) { + return flattenPassthroughLayers(block.symbol); + } + + return layer; + }); +} diff --git a/packages/site/src/ayon/blocks/render.tsx b/packages/site/src/ayon/blocks/render.tsx index 3a8a416b8..b377d3432 100644 --- a/packages/site/src/ayon/blocks/render.tsx +++ b/packages/site/src/ayon/blocks/render.tsx @@ -1,3 +1,4 @@ +import Sketch from 'noya-file-format'; import { applyOverrides, BlockDefinition, @@ -17,7 +18,9 @@ import { boxSymbol } from './symbols'; interface BlockRenderOptions { props: BlockProps; - block: Pick; + block: Pick; + overrideValues?: Sketch.OverrideValue[]; + extraParameters?: ParsedBlockItemParameters; } export function getContainerBlockProps({ @@ -42,13 +45,10 @@ export function getChildrenBlockProps({ props, block, extraParameters, -}: BlockRenderOptions & { - extraParameters?: ParsedBlockItemParameters; -}): BlockProps[] { - if (!props.layer) return []; - + overrideValues = [], +}: BlockRenderOptions): BlockProps[] { const master = applyOverrides({ - overrideValues: props.layer.overrideValues, + overrideValues, symbolMaster: block.symbol, }); @@ -62,7 +62,7 @@ export function getChildrenBlockProps({ return []; } - const override = props.layer?.overrideValues.find( + const override = overrideValues.find( (override) => override.overrideName === Overrides.encodeName([layer.do_objectID], 'blockText'), @@ -103,7 +103,12 @@ export function getChildrenBlockProps({ ); } -export function getRenderableBlockProps({ props, block }: BlockRenderOptions) { +export function getRenderableBlockProps({ + props, + block, + overrideValues, + extraParameters: inputExtraParameters, +}: BlockRenderOptions) { const container = getContainerBlockProps({ props, block }); const { parameters } = parseBlock(container.blockText, 'regular'); @@ -119,27 +124,64 @@ export function getRenderableBlockProps({ props, block }: BlockRenderOptions) { const textAlign = getTextAlign(hashtags); + const extraParameters = inputExtraParameters ?? { + ...(textAlign && { [textAlign]: true }), + ...((parameters.dark || darkBackground) && { + 'text-white': true, + light: true, + }), + }; + const children = getChildrenBlockProps({ props, block, - extraParameters: { - ...(textAlign && { [textAlign]: true }), - ...((parameters.dark || darkBackground) && { - 'text-white': true, - light: true, - }), - }, + overrideValues: props.layer?.overrideValues ?? overrideValues, + extraParameters, }); - return { container, children }; + return { container, children, extraParameters }; } -export const renderStack = ({ props, block }: BlockRenderOptions) => { - const { container, children } = getRenderableBlockProps({ props, block }); +export const renderStack = ({ + props, + block, + overrideValues, + extraParameters: inputExtraParameters, +}: BlockRenderOptions) => { + const { + container, + children, + extraParameters: outputExtraParameters, + } = getRenderableBlockProps({ + props, + block, + overrideValues, + extraParameters: inputExtraParameters, + }); + + container.children = children.map((childProps) => { + const block = props.getBlock(childProps.symbolId); - container.children = children.map((childProps) => - props.getBlock(childProps.symbolId).render(childProps), - ); + if (block.isPassthrough) { + return renderStack({ + props: childProps, + block, + overrideValues: props.layer?.overrideValues, + extraParameters: outputExtraParameters, + }); + } + + return block.render(childProps); + }); + + // We don't render empty passthrough blocks + if ( + block.isPassthrough && + Array.isArray(container.children) && + container.children.length === 0 + ) { + return null; + } return props.getBlock(boxSymbol.symbolID).render(container); }; diff --git a/packages/site/src/ayon/blocks/symbolIds.ts b/packages/site/src/ayon/blocks/symbolIds.ts index 137479948..6a6160c29 100644 --- a/packages/site/src/ayon/blocks/symbolIds.ts +++ b/packages/site/src/ayon/blocks/symbolIds.ts @@ -28,3 +28,4 @@ export const spacerSymbolId = '15907162-13f5-4366-81af-19ced2192245'; export const cardSymbolId = '8bbffbff-efc8-4d9b-9972-73a655adaf47'; export const tileCardSymbolId = '09dcb31d-5bb2-43f2-a30a-0a9e00d92cde'; export const signInSymbolId = '78481f59-0db6-4de6-b38c-f4e4d9354fa9'; +export const linkSymbolId = '5be80b87-7bd6-4090-8947-316d54064544'; diff --git a/packages/site/src/ayon/blocks/symbols.ts b/packages/site/src/ayon/blocks/symbols.ts index 8a7f5fbef..df9f10fce 100644 --- a/packages/site/src/ayon/blocks/symbols.ts +++ b/packages/site/src/ayon/blocks/symbols.ts @@ -17,6 +17,7 @@ import { iconSymbolId, imageSymbolId, inputSymbolId, + linkSymbolId, radioSymbolId, selectSymbolId, sidebarSymbolId, @@ -35,6 +36,11 @@ export const buttonSymbol = SketchModel.symbolMaster({ name: 'Button', }); +export const linkSymbol = SketchModel.symbolMaster({ + symbolID: linkSymbolId, + name: 'Link', +}); + export const avatarSymbol = SketchModel.symbolMaster({ symbolID: avatarSymbolId, name: 'Avatar', @@ -115,6 +121,25 @@ export const heroSymbol = SketchModel.symbolMaster({ name: 'Hero V1 (update available)', }); +export const heroButtonRowSymbol = SketchModel.symbolMaster({ + symbolID: '83d2fdeb-6f4d-4948-a677-fe1f2aac64d5', + name: 'Hero Button Row', + defaultBlockText: '#bg-transparent #flex-row #items-center #gap-6', + layers: [ + SketchModel.symbolInstance({ + do_objectID: '6b386c69-d6cf-4c2f-ae06-c92af43268d5', + symbolID: buttonSymbolId, + blockText: 'Get started #primary #md', + }), + SketchModel.symbolInstance({ + do_objectID: 'eee85c94-7361-4bcf-8afb-f59c6e8661f7', + symbolID: linkSymbolId, + blockText: 'Learn more #text-slate-800 #icon-arrow-forward', + isVisible: false, + }), + ], +}); + export const heroSymbolV2 = SketchModel.symbolMaster({ symbolID: heroSymbolV2Id, name: 'Hero', @@ -131,9 +156,9 @@ export const heroSymbolV2 = SketchModel.symbolMaster({ blockText: 'Turn great ideas into new possibilities. #mb-4', }), SketchModel.symbolInstance({ - do_objectID: '6b386c69-d6cf-4c2f-ae06-c92af43268d5', - symbolID: buttonSymbolId, - blockText: 'Get started #primary #md', + do_objectID: '83d2fdeb-6f4d-4948-a677-fe1f2aac64d5', + symbolID: heroButtonRowSymbol.symbolID, + blockText: '', }), ], }); diff --git a/packages/site/src/ayon/blocks/zipWithoutSpacers.tsx b/packages/site/src/ayon/blocks/zipWithoutSpacers.tsx deleted file mode 100644 index 8f99092f0..000000000 --- a/packages/site/src/ayon/blocks/zipWithoutSpacers.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import Sketch from 'noya-file-format'; -import { Layers } from 'noya-state'; -import { ParsedBlockItem } from '../parse'; -import { spacerSymbolId } from './symbolIds'; - -export function layersWithoutSpacers( - symbolMaster: Sketch.SymbolMaster, -): Sketch.SymbolInstance[] { - return symbolMaster.layers.filter( - (layer): layer is Sketch.SymbolInstance => - Layers.isSymbolInstance(layer) && layer.symbolID !== spacerSymbolId, - ); -} - -export function zipWithoutSpacers( - layers: Sketch.AnyLayer[], - items: ParsedBlockItem[], -) { - let pairs: [Sketch.AnyLayer, ParsedBlockItem][] = []; - - let layersIndex = 0; - let itemsIndex = 0; - - while (layersIndex < layers.length && itemsIndex < items.length) { - const layer = layers[layersIndex]; - const item = items[itemsIndex]; - - if (!Layers.isSymbolInstance(layer)) continue; - - if (layer.symbolID === spacerSymbolId) { - pairs.push([ - layer, - { - content: '', - parameters: {}, - }, - ]); - layersIndex++; - continue; - } - - pairs.push([layer, item]); - - itemsIndex++; - layersIndex++; - } - - return pairs; -} diff --git a/packages/site/src/ayon/editor/BlockEditor.tsx b/packages/site/src/ayon/editor/BlockEditor.tsx index c27b31f11..98905b0f0 100644 --- a/packages/site/src/ayon/editor/BlockEditor.tsx +++ b/packages/site/src/ayon/editor/BlockEditor.tsx @@ -35,7 +35,7 @@ import { withReact, } from 'slate-react'; import { allInsertableSymbols, Blocks } from '../blocks/blocks'; -import { layersWithoutSpacers } from '../blocks/zipWithoutSpacers'; +import { flattenPassthroughLayers } from '../blocks/flattenPassthroughLayers'; import { InferredBlockTypeResult } from '../types'; import { CompletionItem, useCompletionMenu } from '../useCompletionMenu'; import { BLOCK_TYPE_SHORTCUTS, textCommand, textShortcut } from './commands'; @@ -300,7 +300,7 @@ export const BlockEditor = forwardRef(function BlockEditor( const layerVisibility = useMemo(() => { const visibility: Record = {}; - const children = layersWithoutSpacers(blockDefinition.symbol); + const children = flattenPassthroughLayers(blockDefinition.symbol); for (const child of children) { const isVisible = Overrides.getOverrideValue( @@ -309,7 +309,7 @@ export const BlockEditor = forwardRef(function BlockEditor( 'isVisible', ); - visibility[child.do_objectID] = isVisible ?? true; + visibility[child.do_objectID] = isVisible ?? child.isVisible; } return visibility; diff --git a/packages/site/src/ayon/editor/serialization.tsx b/packages/site/src/ayon/editor/serialization.tsx index 0f711157e..da9e273e8 100644 --- a/packages/site/src/ayon/editor/serialization.tsx +++ b/packages/site/src/ayon/editor/serialization.tsx @@ -3,14 +3,14 @@ import { SketchModel } from 'noya-sketch-model'; import { BlockContent, Overrides } from 'noya-state'; import { Descendant, Node } from 'slate'; import { Blocks } from '../blocks/blocks'; -import { layersWithoutSpacers } from '../blocks/zipWithoutSpacers'; +import { flattenPassthroughLayers } from '../blocks/flattenPassthroughLayers'; import { ParagraphElement } from './types'; export function toContent( symbol: Sketch.SymbolMaster, nodes: Descendant[], ): BlockContent { - const layers = layersWithoutSpacers(symbol); + const layers = flattenPassthroughLayers(symbol); const overrides: Sketch.OverrideValue[] = []; for (let i = 0; i < layers.length; i++) { @@ -35,7 +35,7 @@ export function fromSymbol( symbol: Sketch.SymbolMaster, instance: Sketch.SymbolInstance, ): Descendant[] { - const layers = layersWithoutSpacers(symbol); + const layers = flattenPassthroughLayers(symbol); const layerNodes = layers.map((layer): ParagraphElement => { const block = layer ? Blocks[layer.symbolID] : undefined; diff --git a/packages/site/src/ayon/editor/withLayout.tsx b/packages/site/src/ayon/editor/withLayout.tsx index 88c495231..681990897 100644 --- a/packages/site/src/ayon/editor/withLayout.tsx +++ b/packages/site/src/ayon/editor/withLayout.tsx @@ -1,6 +1,6 @@ import { Element as SlateElement, Node, Transforms } from 'slate'; import { Blocks } from '../blocks/blocks'; -import { layersWithoutSpacers } from '../blocks/zipWithoutSpacers'; +import { flattenPassthroughLayers } from '../blocks/flattenPassthroughLayers'; import { CustomEditor, ParagraphElement } from './types'; export function withLayout(initialSymbolId: string, editor: CustomEditor) { @@ -11,7 +11,7 @@ export function withLayout(initialSymbolId: string, editor: CustomEditor) { editor.normalizeNode = ([node, path]) => { const symbol = Blocks[editor.symbolId].symbol; - const layers = layersWithoutSpacers(symbol); + const layers = flattenPassthroughLayers(symbol); // If this is the editor if (path.length === 0) { diff --git a/packages/site/src/docs/BlockGrid.tsx b/packages/site/src/docs/BlockGrid.tsx index 384f302cc..1fb661b42 100644 --- a/packages/site/src/docs/BlockGrid.tsx +++ b/packages/site/src/docs/BlockGrid.tsx @@ -22,6 +22,7 @@ export function BlockGrid({ category }: { category: BlockCategory }) { {Object.entries(blockMetadata) .filter(([, metadata]) => metadata.category === category) .filter(([, metadata]) => !metadata.hideInDocs) + .sort(([, a], [, b]) => a.name.localeCompare(b.name)) .map(([blockId, metadata]) => ( + +## Configuration + +The text block accepts many `#` commands for styling text, such as +`#text-red-600 #font-bold`. You can also use the `#icon-arrow-forward` command +to add an arrow icon: + +