From 71c8bc44e23ddfeadb64acca01839cabf7bfab3b Mon Sep 17 00:00:00 2001 From: John Otander Date: Tue, 24 May 2022 16:32:40 -0600 Subject: [PATCH 1/3] Add selection state to HTML editor Closes #252 --- .changeset/many-foxes-invent.md | 5 ++ apps/docs/pages/html-editor.tsx | 14 +++-- packages/gui/src/components/html/Provider.tsx | 52 +++++++++++++++++++ packages/gui/src/components/html/Renderer.tsx | 51 ++++++++++++++---- packages/gui/src/components/html/editor.tsx | 12 ++--- packages/gui/src/components/html/types.ts | 3 +- packages/gui/src/index.ts | 1 + 7 files changed, 118 insertions(+), 20 deletions(-) create mode 100644 .changeset/many-foxes-invent.md create mode 100644 packages/gui/src/components/html/Provider.tsx diff --git a/.changeset/many-foxes-invent.md b/.changeset/many-foxes-invent.md new file mode 100644 index 00000000..9bec0dc4 --- /dev/null +++ b/.changeset/many-foxes-invent.md @@ -0,0 +1,5 @@ +--- +'@compai/css-gui': patch +--- + +Add selection state to HTML editor diff --git a/apps/docs/pages/html-editor.tsx b/apps/docs/pages/html-editor.tsx index 4d731749..21f17adc 100644 --- a/apps/docs/pages/html-editor.tsx +++ b/apps/docs/pages/html-editor.tsx @@ -1,4 +1,9 @@ -import { HtmlEditor, HtmlRenderer, htmlToEditorSchema } from '@compai/css-gui' +import { + HtmlEditor, + HtmlRenderer, + HtmlEditorProvider, + htmlToEditorSchema, +} from '@compai/css-gui' import { useState } from 'react' // TODO: Handle style attrs @@ -13,10 +18,13 @@ const initialValue = htmlToEditorSchema(` export default function HtmlEditorExample() { const [html, setHtml] = useState(initialValue) + return (
- - + + + +
) } diff --git a/packages/gui/src/components/html/Provider.tsx b/packages/gui/src/components/html/Provider.tsx new file mode 100644 index 00000000..5acd0441 --- /dev/null +++ b/packages/gui/src/components/html/Provider.tsx @@ -0,0 +1,52 @@ +import { createContext, ReactChild, useContext, useState } from 'react' +import { htmlToEditorSchema } from '../../lib' +import { HtmlNode, ElementPath } from './types' + +const DEFAULT_HTML_EDITOR_VALUE = { + selected: [], + setSelected: () => {}, + value: htmlToEditorSchema(` +
+

Hello, world!

+

Weeee!

+ + I'm a link! +
+ `), +} + +export type HtmlEditor = { + value: HtmlNode + selected: ElementPath + setSelected: (newSelection: ElementPath) => void +} + +export function useHtmlEditor() { + const context = useContext(HtmlEditorContext) + return context +} + +const HtmlEditorContext = createContext(DEFAULT_HTML_EDITOR_VALUE) + +type HtmlEditorProviderProps = HtmlEditor & { + children: ReactChild +} +export function HtmlEditorProvider({ + children, + selected: providedSelected, + ...props +}: HtmlEditorProviderProps) { + const [selected, setSelected] = useState(providedSelected) + + const fullContext = { + ...props, + selected, + setSelected: (newSelection: ElementPath) => setSelected(newSelection), + } + + return ( + + {children} + + ) +} diff --git a/packages/gui/src/components/html/Renderer.tsx b/packages/gui/src/components/html/Renderer.tsx index 918c943e..a630f7f8 100644 --- a/packages/gui/src/components/html/Renderer.tsx +++ b/packages/gui/src/components/html/Renderer.tsx @@ -1,34 +1,67 @@ import { toCSSObject } from '../../lib' -import { ElementData } from './types' +import { ElementData, ElementPath } from './types' import { HTMLFontTags } from './FontTags' +import { useHtmlEditor } from './Provider' -interface Props { +interface HtmlRendererProps { value: ElementData + path?: ElementPath } - -export function HtmlRenderer({ value }: Props) { +export function HtmlRenderer({ value }: HtmlRendererProps) { return ( <> - + ) } -function ElementRenderer({ value }: Props) { - const { tagName, attributes = {}, style = {}, children = [] } = value +interface ElementRendererProps { + value: ElementData + path: ElementPath +} +function ElementRenderer({ value, path }: ElementRendererProps) { + const { selected, setSelected } = useHtmlEditor() + const { attributes = {}, style = {}, children = [] } = value const Tag: any = value.tagName || 'div' + const sx = toCSSObject(style) + + if (selected && isSamePath(path, selected)) { + sx.outline = 'thin solid tomato' + } + return ( <> - + { + e.stopPropagation() + setSelected(path) + }} + > {children.map((child, i) => { if (typeof child === 'string') { return child } - return + return })} ) } + +const cleanAttributes = (attributes: Record) => { + const newAttributes = { ...attributes } + + if (newAttributes.href) { + newAttributes.href = '#' + } + + return newAttributes +} + +const isSamePath = (path1: ElementPath, path2: ElementPath) => { + return path1.join('-') === path2.join('-') +} diff --git a/packages/gui/src/components/html/editor.tsx b/packages/gui/src/components/html/editor.tsx index bc784814..d89fab3c 100644 --- a/packages/gui/src/components/html/editor.tsx +++ b/packages/gui/src/components/html/editor.tsx @@ -1,5 +1,5 @@ import { Editor } from '../Editor' -import { HtmlNode, HTMLTag } from './types' +import { HtmlNode, HTMLTag, ElementPath } from './types' import * as Collapsible from '@radix-ui/react-collapsible' import { Fragment, useState } from 'react' import { isNil } from 'lodash-es' @@ -9,6 +9,7 @@ import { Label, Combobox } from '../primitives' import { SelectInput } from '../inputs/SelectInput' import { AttributeEditor } from './AttributeEditor' import { DEFAULT_STYLES } from './default-styles' +import { useHtmlEditor } from './Provider' const HTML_TAGS = [ HTMLTag.P, @@ -31,15 +32,12 @@ interface EditorProps { onChange(value: HtmlNode): void } -type ElementPath = number[] - /** * An HTML tree-based editor that lets you add HTML nodes and mess around with their styles */ -export function HtmlEditor({ value, onChange }: EditorProps) { - const [selected, setSelected] = useState( - value ? [0] : null - ) +export function HtmlEditor({ onChange }: EditorProps) { + const { value, selected, setSelected } = useHtmlEditor() + return (
Date: Tue, 24 May 2022 16:37:55 -0600 Subject: [PATCH 2/3] Fix types --- packages/gui/src/components/html/Provider.tsx | 21 ++++++++++--------- packages/gui/src/components/html/Renderer.tsx | 2 +- packages/gui/src/components/html/editor.tsx | 9 +++++--- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/packages/gui/src/components/html/Provider.tsx b/packages/gui/src/components/html/Provider.tsx index 5acd0441..b1e35243 100644 --- a/packages/gui/src/components/html/Provider.tsx +++ b/packages/gui/src/components/html/Provider.tsx @@ -1,4 +1,4 @@ -import { createContext, ReactChild, useContext, useState } from 'react' +import { createContext, ReactNode, useContext, useState } from 'react' import { htmlToEditorSchema } from '../../lib' import { HtmlNode, ElementPath } from './types' @@ -17,8 +17,8 @@ const DEFAULT_HTML_EDITOR_VALUE = { export type HtmlEditor = { value: HtmlNode - selected: ElementPath - setSelected: (newSelection: ElementPath) => void + selected: ElementPath | null + setSelected: (newSelection: ElementPath | null) => void } export function useHtmlEditor() { @@ -28,20 +28,21 @@ export function useHtmlEditor() { const HtmlEditorContext = createContext(DEFAULT_HTML_EDITOR_VALUE) -type HtmlEditorProviderProps = HtmlEditor & { - children: ReactChild +type HtmlEditorProviderProps = { + value: HtmlNode + children: ReactNode } export function HtmlEditorProvider({ children, - selected: providedSelected, - ...props + value, }: HtmlEditorProviderProps) { - const [selected, setSelected] = useState(providedSelected) + const [selected, setSelected] = useState([]) const fullContext = { - ...props, + value, selected, - setSelected: (newSelection: ElementPath) => setSelected(newSelection), + setSelected: (newSelection: ElementPath | null) => + setSelected(newSelection), } return ( diff --git a/packages/gui/src/components/html/Renderer.tsx b/packages/gui/src/components/html/Renderer.tsx index a630f7f8..6b484326 100644 --- a/packages/gui/src/components/html/Renderer.tsx +++ b/packages/gui/src/components/html/Renderer.tsx @@ -11,7 +11,7 @@ export function HtmlRenderer({ value }: HtmlRendererProps) { return ( <> - + ) } diff --git a/packages/gui/src/components/html/editor.tsx b/packages/gui/src/components/html/editor.tsx index d89fab3c..a94e8e38 100644 --- a/packages/gui/src/components/html/editor.tsx +++ b/packages/gui/src/components/html/editor.tsx @@ -27,15 +27,14 @@ const HTML_TAGS = [ HTMLTag.Div, ] -interface EditorProps { - value: HtmlNode +interface HtmlEditorProps { onChange(value: HtmlNode): void } /** * An HTML tree-based editor that lets you add HTML nodes and mess around with their styles */ -export function HtmlEditor({ onChange }: EditorProps) { +export function HtmlEditor({ onChange }: HtmlEditorProps) { const { value, selected, setSelected } = useHtmlEditor() return ( @@ -81,6 +80,10 @@ export function HtmlEditor({ onChange }: EditorProps) { ) } +interface EditorProps { + value: HtmlNode + onChange(value: HtmlNode): void +} interface TagEditorProps extends EditorProps { onRemove(): void } From 2c8e3c37ba9cc045ec11764de69d703579b8356c Mon Sep 17 00:00:00 2001 From: John Otander Date: Tue, 24 May 2022 18:15:44 -0600 Subject: [PATCH 3/3] Tweak styles --- apps/docs/components/playground/Layout.tsx | 3 +-- apps/docs/pages/html-editor.tsx | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/docs/components/playground/Layout.tsx b/apps/docs/components/playground/Layout.tsx index 3eaf96b0..4f5b86eb 100644 --- a/apps/docs/components/playground/Layout.tsx +++ b/apps/docs/components/playground/Layout.tsx @@ -9,8 +9,7 @@ export const Layout = (props: Props) => {
diff --git a/apps/docs/pages/html-editor.tsx b/apps/docs/pages/html-editor.tsx index 21f17adc..1c71d8d3 100644 --- a/apps/docs/pages/html-editor.tsx +++ b/apps/docs/pages/html-editor.tsx @@ -23,7 +23,9 @@ export default function HtmlEditorExample() {
- +
+ +
)