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/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 4d731749..1c71d8d3 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,15 @@ 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..b1e35243
--- /dev/null
+++ b/packages/gui/src/components/html/Provider.tsx
@@ -0,0 +1,53 @@
+import { createContext, ReactNode, 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 | null
+ setSelected: (newSelection: ElementPath | null) => void
+}
+
+export function useHtmlEditor() {
+ const context = useContext(HtmlEditorContext)
+ return context
+}
+
+const HtmlEditorContext = createContext(DEFAULT_HTML_EDITOR_VALUE)
+
+type HtmlEditorProviderProps = {
+ value: HtmlNode
+ children: ReactNode
+}
+export function HtmlEditorProvider({
+ children,
+ value,
+}: HtmlEditorProviderProps) {
+ const [selected, setSelected] = useState([])
+
+ const fullContext = {
+ value,
+ selected,
+ setSelected: (newSelection: ElementPath | null) =>
+ setSelected(newSelection),
+ }
+
+ return (
+
+ {children}
+
+ )
+}
diff --git a/packages/gui/src/components/html/Renderer.tsx b/packages/gui/src/components/html/Renderer.tsx
index 918c943e..6b484326 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..a94e8e38 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,
@@ -26,20 +27,16 @@ const HTML_TAGS = [
HTMLTag.Div,
]
-interface EditorProps {
- value: HtmlNode
+interface HtmlEditorProps {
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 }: HtmlEditorProps) {
+ const { value, selected, setSelected } = useHtmlEditor()
+
return (