Skip to content

Commit

Permalink
wip: shoelace
Browse files Browse the repository at this point in the history
  • Loading branch information
tpluscode committed Dec 13, 2024
1 parent 88e7c5d commit c8f4b73
Show file tree
Hide file tree
Showing 16 changed files with 183 additions and 241 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import type { Meta } from '@storybook/web-components'
import * as shoelace from '@hydrofoil/shaperone-wc-shoelace/components.js'
import * as templates from '@hydrofoil/shaperone-wc-shoelace/templates.js'
import * as editors from '@hydrofoil/shaperone-wc-shoelace/components.js'
import { button, focusNode, object, property } from '@hydrofoil/shaperone-wc-shoelace/templates.js'
import type { ConfigCallback } from '@hydrofoil/shaperone-wc/configure.js'
import { defaultMeta, Grouping, TextEditors } from '../common.js'

const configure: ConfigCallback = ({ components, renderer }) => {
components.pushComponents(shoelace)
renderer.setTemplates(templates)
components.pushComponents(editors)
renderer.pushComponents({
'focus-node': focusNode,
button,
object,
property,
})
}

/**
Expand Down
7 changes: 1 addition & 6 deletions packages/core/models/components/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@ export default {
const addedArray = Array.isArray(toAdd) ? toAdd : Object.values(toAdd)

for (const component of addedArray) {
const previous = components.components[component.editor.value]
const shouldAddComponent = !previous

if (shouldAddComponent) {
newComponents.components[component.editor.value] = decorate(components.decorators, component)
}
newComponents.components[component.editor.value] = decorate(components.decorators, component)
}
})
},
Expand Down
27 changes: 4 additions & 23 deletions packages/wc-shoelace/components.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,8 @@
import type { Lazy, SingleEditorComponent } from '@hydrofoil/shaperone-wc'
import { dash } from '@tpluscode/rdf-ns-builders'
import type { ComponentInstance } from '@hydrofoil/shaperone-core/models/components'
import { html } from 'lit'
import isGraphPointer from 'is-graph-pointer'
import type { GraphPointer } from 'clownface'
import type { BooleanSelectEditor } from '@hydrofoil/shaperone-core/lib/components/booleanSelect.js'
import '@shoelace-style/shoelace/dist/components/skeleton/skeleton.js'

export { autocomplete } from './components/autocomplete.js'
export { enumSelect } from './components/enumSelect.js'
export { instancesSelect } from './components/instancesSelect.js'

interface EditorState extends ComponentInstance {
noLabel?: boolean
}

export const textField: Lazy<SingleEditorComponent<EditorState>> = {
editor: dash.TextFieldEditor,
async lazyRender() {
const { inputRenderer } = await import('./renderer/input.js')

return inputRenderer()
},
}
export { TextField } from './editors/TextField.js'

/*
interface TextFieldWithLang extends ComponentInstance {
language?: string
}
Expand Down Expand Up @@ -92,3 +72,4 @@ export const boolean: Lazy<BooleanSelectEditor> = {
return render
},
}
*/
46 changes: 46 additions & 0 deletions packages/wc-shoelace/editors/TextField.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { SingleEditorComponent } from '@hydrofoil/shaperone-wc'
import type { TextFieldEditor } from '@hydrofoil/shaperone-core/components.js'
import { dash } from '@tpluscode/rdf-ns-builders'
import { css, html } from 'lit'
import { state } from 'lit/decorators.js'
import type SlInput from '@shoelace-style/shoelace/dist/components/input/input.component.js'

export class TextField extends SingleEditorComponent implements TextFieldEditor {
static editor = dash.TextFieldEditor

static get styles() {
return css`
sl-skeleton {
width: 100px;
}
`
}

@state()
private ready = false

get type(): SlInput['type'] {
return 'text'
}

connectedCallback() {
import('@shoelace-style/shoelace/dist/components/input/input.js').then(() => {
this.ready = true
})

super.connectedCallback()
}

protected render(): unknown {
if (!this.ready) {
return html`
<sl-skeleton effect="sheen"></sl-skeleton>`
}

return html`
<sl-input .value="${this.value?.object?.value || ''}"
type="${this.type}"
.readonly="${this.property.shape.readOnly || false}"
@sl-change="${(e: any) => this.setValue(e.target.value)}"></sl-input>`
}
}
20 changes: 20 additions & 0 deletions packages/wc-shoelace/elements/Button.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import '@shoelace-style/shoelace/dist/components/icon-button/icon-button.js'
import { html, LitElement } from 'lit'

export class Button extends LitElement {
render() {
let name = 'plus-square'
if (this.slot === 'remove-object') {
name = 'x-square'
}

return html`<sl-icon-button style="font-size: 1.75em"
name="${name}"
label="${this.label}"
></sl-icon-button>`
}

get label() {
return this.slot === 'remove-object' ? 'Remove value' : 'Add value'
}
}
22 changes: 4 additions & 18 deletions packages/wc-shoelace/elements/sh-sl-object.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { css, html, LitElement } from 'lit'
import { customElement, property } from 'lit/decorators.js'

@customElement('sh-sl-object')
export class ShaperoneShoelaceObject extends LitElement {
export default class extends LitElement {
static get styles() {
return css`
:host {
display: flex;
justify-content: space-between;
}
slot {
slot.editor {
display: block;
flex: 1 ;
}
Expand All @@ -21,22 +19,10 @@ export class ShaperoneShoelaceObject extends LitElement {
`
}

@property({ type: Boolean })
canBeRemoved?: boolean

@property({ type: String })
removeIcon?: string

render() {
let removeRow = html``
if (this.canBeRemoved) {
removeRow = html`<sl-icon-button name="${this.removeIcon || 'x-square'}"
label="Remove value"
@click="${() => this.dispatchEvent(new Event('removed'))}"></sl-icon-button>`
}
return html`
<slot></slot>
${removeRow}
<slot class="editor"></slot>
<slot name="remove-object"></slot>
`
}
}
44 changes: 14 additions & 30 deletions packages/wc-shoelace/elements/sh-sl-property.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { TemplateResult } from 'lit'
import { css, html, LitElement } from 'lit'
import { customElement, property, query } from 'lit/decorators.js'
import '@shoelace-style/shoelace/dist/components/icon-button/icon-button.js'
import { property, query } from 'lit/decorators.js'
import type { PropertyElement } from '@hydrofoil/shaperone-wc/components/index.js'
import type { FocusNodeState, PropertyState } from '@hydrofoil/shaperone-core/models/forms/index.js'
import { localizedLabel } from '@rdfjs-elements/lit-helpers/localizedLabel.js'
import { sh } from '@tpluscode/rdf-ns-builders'

@customElement('sh-sl-property')
export class ShSlProperty extends LitElement {
export class ShSlProperty extends LitElement implements PropertyElement {
static get styles() {
return css`
:host {
Expand Down Expand Up @@ -35,17 +36,11 @@ export class ShSlProperty extends LitElement {
`
}

@property({ type: Boolean })
public canAddValue?: boolean

@property({ type: String })
public label?: string

@property({ type: String })
public helpText?: string
@property({ type: Object })
public focusNode!: FocusNodeState

@property({ type: String })
public addIcon?: string
@property({ type: Object })
public property!: PropertyState

@property({ type: Boolean })
private __slotEmpty = true
Expand All @@ -54,27 +49,16 @@ export class ShSlProperty extends LitElement {
private __slot!: HTMLSlotElement

render() {
let addRow: TemplateResult = html``
if (this.canAddValue) {
addRow = html`<slot name="add-object">
<sl-icon-button style="font-size: 1.75em"
name="${this.addIcon || 'plus-square'}"
label="Add value"
@click="${() => this.dispatchEvent(new Event('added'))}"
></sl-icon-button>
</slot>`
}

let helpText: any = ''
if (this.helpText) {
helpText = html`<p id="help-text">${this.helpText}</p>`
if (this.property.shape.description) {
helpText = html`<p id="help-text">${localizedLabel(this.property.shape, { property: sh.description })}</p>`
}

return html`
<p id="label">${this.label}</p>
<p id="label">${localizedLabel(this.property.shape, { property: sh.name })}</p>
<slot class="${this.__slotEmpty ? 'empty' : ''}" @slotchange="${this.__hasAssignedElements}"></slot>
${helpText}
${addRow}
<slot name="add-object"></slot>
`
}

Expand Down
20 changes: 0 additions & 20 deletions packages/wc-shoelace/renderer/input.ts

This file was deleted.

101 changes: 21 additions & 80 deletions packages/wc-shoelace/templates.ts
Original file line number Diff line number Diff line change
@@ -1,86 +1,26 @@
import type { TemplateResult } from 'lit'
import { html } from 'lit'
import type { PropertyTemplate, ObjectTemplate, FocusNodeTemplate } from '@hydrofoil/shaperone-wc/templates.js'
import { repeat } from 'lit/directives/repeat.js'
import { localizedLabel } from '@rdfjs-elements/lit-helpers/localizedLabel.js'
import { sh } from '@tpluscode/rdf-ns-builders'
import type { PropertyState } from '@hydrofoil/shaperone-core/models/forms'
import { settings } from './settings.js'

interface AddObject {
(property: PropertyState): TemplateResult | ''
}

declare module '@hydrofoil/shaperone-wc/templates.js' {
interface RenderTemplates {
shoelace?: {
addObject?: AddObject
}
}
}

interface ShoelacePropertyTemplate extends PropertyTemplate {
addObjectIcon?: string
}

export const property: ShoelacePropertyTemplate = (renderer, { property: state }) => {
const editors = state.selectedEditor
? renderer.renderMultiEditor()
: html`${repeat(state.objects, object => html`${renderer.renderObject({ object })}`)}`

const customAddObject = renderer.context.templates.shoelace?.addObject?.(state)

function onAdd(e: CustomEvent<Parameters<typeof renderer.actions.addObject>[0]>) {
const detail = e.detail ? { ...e.detail } : {}
if (!detail.componentState) {
detail.componentState = {}
import { html, LitElement } from 'lit'
import type { FocusNodeElement } from '@hydrofoil/shaperone-wc/components/index.js'
import type { FocusNodeState } from '@hydrofoil/shaperone-core/models/forms/index.js'
import { property } from 'lit/decorators.js'

export { ShSlProperty as property } from './elements/sh-sl-property.js'
export { default as object } from './elements/sh-sl-object.js'
export { Button as button } from './elements/Button.js'

export class focusNode extends LitElement implements FocusNodeElement {
@property({ type: Object })
public focusNode!: FocusNodeState

render() {
if (this.focusNode.groups.length === 1) {
return html`
<sl-tab-group>
<slot></slot>
</sl-tab-group>`
}

Object.entries(settings.newFieldDefaults)
.forEach(([key, value]) => {
if (!(key in detail.componentState!)) {
detail.componentState![key] = value
}
})

renderer.actions.addObject(detail)
e.stopPropagation()
}

return html`
<sh-sl-property .label="${localizedLabel(state.shape, { property: sh.name }) as any}"
.helpText="${localizedLabel(state.shape, { property: sh.description }) as any}"
.canAddValue="${state.canAdd && !state.selectedEditor}"
.addIcon="${property.addObjectIcon}"
@added="${onAdd}"
>
${editors}
${customAddObject ? html`<section slot="add-object">${customAddObject}</section>` : ''}
</sh-sl-property>
`
}

property.loadDependencies = () => [
import('./elements/sh-sl-property.js'),
]

interface ShoelaceObjectTemplate extends ObjectTemplate {
removeIcon?: string
}

export const object: ShoelaceObjectTemplate = renderer => html`
<sh-sl-object .canBeRemoved="${renderer.property.canRemove}"
@removed="${renderer.actions.remove}"
.removeIcon="${object.removeIcon}"
>
${renderer.renderEditor()}
</sh-sl-object>
`

object.loadDependencies = () => [
import('./elements/sh-sl-object.js'),
]

/*
export const focusNode: FocusNodeTemplate = (renderer, { focusNode: { groups } }) => {
if (groups.length === 1) {
return renderer.renderGroup({ group: groups[0] })
Expand All @@ -105,3 +45,4 @@ focusNode.loadDependencies = () => [
import('@shoelace-style/shoelace/dist/components/tab/tab.js'),
import('@shoelace-style/shoelace/dist/components/tab-panel/tab-panel.js'),
]
*/
Loading

0 comments on commit c8f4b73

Please sign in to comment.