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(`
+
+ `),
+}
+
+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() {
)