mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-23 07:10:41 +00:00
Support styling
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<link rel="stylesheet" href="./styles/nodes.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="header"></div>
|
||||
|
||||
35
public/styles/nodes.css
Normal file
35
public/styles/nodes.css
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
.node {
|
||||
font-size: 18px;
|
||||
font-family: Arial, Helvetica, sans-serif
|
||||
}
|
||||
|
||||
.list-entry:not(:last-child),
|
||||
.map-entry:not(:last-child),
|
||||
.object-node:not(:last-child) > .object-fields {
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.list-entry,
|
||||
.map-entry,
|
||||
.object-fields {
|
||||
padding-left: 0.7rem;
|
||||
}
|
||||
|
||||
.list-entry {
|
||||
border-left: 2px solid #0b552a;
|
||||
}
|
||||
|
||||
.map-fields {
|
||||
border-left: 2px solid #066fb4;
|
||||
}
|
||||
|
||||
.object-fields {
|
||||
border-left: 2px solid #b9b9b9;
|
||||
}
|
||||
.object-node > label {
|
||||
user-select: none;
|
||||
}
|
||||
.object-node > label.collapse {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -51,5 +51,5 @@ export const SandboxSchema = new ObjectNode({
|
||||
nbt: 'hi'
|
||||
}
|
||||
}),
|
||||
transform: (v) => v?.condition === 'foo' ? ({...v, test: 'hello'}) : v
|
||||
transform: (v: any) => v?.condition === 'foo' ? ({...v, test: 'hello'}) : v
|
||||
});
|
||||
|
||||
@@ -87,6 +87,10 @@ export class RangeNode extends AbstractNode<IRange> implements StateNode<IRange>
|
||||
${input}`
|
||||
}
|
||||
|
||||
getClassName() {
|
||||
return 'range-node'
|
||||
}
|
||||
|
||||
static isExact(v?: IRange) {
|
||||
return v === undefined || typeof v === 'number'
|
||||
}
|
||||
|
||||
@@ -37,4 +37,8 @@ export class ResourceNode extends EnumNode {
|
||||
return super.renderRaw(path, value, view, options)
|
||||
}
|
||||
}
|
||||
|
||||
getClassName() {
|
||||
return 'enum-node'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ SCHEMAS.register('location-predicate', new ObjectNode({
|
||||
x: new RangeNode(),
|
||||
y: new RangeNode(),
|
||||
z: new RangeNode()
|
||||
}),
|
||||
}, {collapse: true}),
|
||||
biome: new ResourceNode(COLLECTIONS.get('biomes')),
|
||||
feature: new EnumNode(COLLECTIONS.get('structures')),
|
||||
dimension: new ResourceNode(COLLECTIONS.get('dimensions'), {additional: true}),
|
||||
@@ -61,8 +61,8 @@ SCHEMAS.register('location-predicate', new ObjectNode({
|
||||
light: new RangeNode()
|
||||
}),
|
||||
smokey: new BooleanNode(),
|
||||
block: new ReferenceNode('block-predicate'),
|
||||
fluid: new ReferenceNode('fluid-predicate')
|
||||
block: new ReferenceNode('block-predicate', {collapse: true}),
|
||||
fluid: new ReferenceNode('fluid-predicate', {collapse: true})
|
||||
}))
|
||||
|
||||
SCHEMAS.register('statistic-predicate', new ObjectNode({
|
||||
@@ -87,7 +87,7 @@ SCHEMAS.register('player-predicate', new ObjectNode({
|
||||
)
|
||||
}))
|
||||
|
||||
SCHEMAS.register('status-effect', new ObjectNode({
|
||||
SCHEMAS.register('status-effect-predicate', new ObjectNode({
|
||||
amplifier: new RangeNode(),
|
||||
duration: new RangeNode(),
|
||||
ambient: new BooleanNode(),
|
||||
@@ -106,22 +106,22 @@ SCHEMAS.register('entity-predicate', new ObjectNode({
|
||||
type: new StringNode(),
|
||||
nbt: new StringNode(),
|
||||
team: new StringNode(),
|
||||
location: new ReferenceNode('location-predicate'),
|
||||
distance: new ReferenceNode('distance-predicate'),
|
||||
location: new ReferenceNode('location-predicate', {collapse: true}),
|
||||
distance: new ReferenceNode('distance-predicate', {collapse: true}),
|
||||
flags: new ObjectNode({
|
||||
is_on_fire: new BooleanNode(),
|
||||
is_sneaking: new BooleanNode(),
|
||||
is_sprinting: new BooleanNode(),
|
||||
is_swimming: new BooleanNode(),
|
||||
is_baby: new BooleanNode()
|
||||
}),
|
||||
}, {collapse: true}),
|
||||
equipment: new MapNode(
|
||||
new EnumNode(COLLECTIONS.get('slots')),
|
||||
new ReferenceNode('item-predicate')
|
||||
),
|
||||
// vehicle: new ReferenceNode('entity-predicate'),
|
||||
// targeted_entity: new ReferenceNode('entity-predicate'),
|
||||
player: new ReferenceNode('player-predicate'),
|
||||
vehicle: new ReferenceNode('entity-predicate', {collapse: true}),
|
||||
targeted_entity: new ReferenceNode('entity-predicate', {collapse: true}),
|
||||
player: new ReferenceNode('player-predicate', {collapse: true}),
|
||||
fishing_hook: new ObjectNode({
|
||||
in_open_water: new BooleanNode()
|
||||
}),
|
||||
|
||||
@@ -17,6 +17,7 @@ export interface StateNode<T> extends INode<T> {
|
||||
export type RenderOptions = {
|
||||
hideLabel?: boolean
|
||||
syncModel?: boolean
|
||||
collapse?: boolean
|
||||
}
|
||||
|
||||
export type NodeChildren = {
|
||||
@@ -82,8 +83,12 @@ export abstract class AbstractNode<T> implements INode<T> {
|
||||
const id = view.register(el => {
|
||||
this.mounted(el, path, view)
|
||||
})
|
||||
return `<div data-id="${id}">${this.renderRaw(path, value, view, options)}</div>`
|
||||
return `<div data-id="${id}" class="node ${this.getClassName()}">
|
||||
${this.renderRaw(path, value, view, options)}
|
||||
</div>`
|
||||
}
|
||||
|
||||
abstract renderRaw(path: Path, value: T, view: TreeView, options?: RenderOptions): string
|
||||
|
||||
abstract getClassName(): string
|
||||
}
|
||||
|
||||
@@ -21,4 +21,8 @@ export class BooleanNode extends AbstractNode<boolean> {
|
||||
<button${value === false ? ' style="font-weight: bold"' : ' '} data-id="${falseButton}">False</button>
|
||||
<button${value === true ? ' style="font-weight: bold"' : ' '} data-id="${trueButton}">True</button>`
|
||||
}
|
||||
|
||||
getClassName() {
|
||||
return 'boolean-node'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Path } from '../model/Path'
|
||||
|
||||
export class EnumNode extends AbstractNode<string> implements StateNode<string> {
|
||||
protected options: string[]
|
||||
static className = 'enum-node'
|
||||
|
||||
constructor(options: string[], mods?: NodeMods<string> | string) {
|
||||
super(typeof mods === 'string' ? {
|
||||
@@ -29,4 +30,8 @@ export class EnumNode extends AbstractNode<string> implements StateNode<string>
|
||||
${this.options.map(o => `<option value="${o}">${o}</option>`).join('')}
|
||||
</select>`
|
||||
}
|
||||
|
||||
getClassName() {
|
||||
return 'enum-node'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export class ListNode extends AbstractNode<IObject[]> {
|
||||
})
|
||||
return `<label>${path.last()}:</label>
|
||||
<button data-id="${button}">Add</button>
|
||||
<div style="padding-left:8px">
|
||||
<div class="list-fields">
|
||||
${value.map((obj, index) => {
|
||||
return this.renderEntry(path.push(index), obj, view)
|
||||
}).join('')}
|
||||
@@ -44,8 +44,12 @@ export class ListNode extends AbstractNode<IObject[]> {
|
||||
const button = view.registerClick(el => {
|
||||
view.model.set(path, undefined)
|
||||
})
|
||||
return `<div><button data-id="${button}">Remove</button>
|
||||
return `<div class="list-entry"><button data-id="${button}">Remove</button>
|
||||
${this.children.render(path, value, view, {hideLabel: true})}
|
||||
</div>`
|
||||
}
|
||||
|
||||
getClassName() {
|
||||
return 'list-node'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export class MapNode extends AbstractNode<IMap> {
|
||||
return `<label>${path.last()}:</label>
|
||||
${this.keys.renderRaw(path, '', view, {hideLabel: true, syncModel: false})}
|
||||
<button data-id="${button}">Add</button>
|
||||
<div style="padding-left:8px">
|
||||
<div class="map-fields">
|
||||
${Object.keys(value).map(key => {
|
||||
return this.renderEntry(path.push(key), value[key], view)
|
||||
}).join('')}
|
||||
@@ -48,8 +48,12 @@ export class MapNode extends AbstractNode<IMap> {
|
||||
const button = view.registerClick(el => {
|
||||
view.model.set(path, undefined)
|
||||
})
|
||||
return `<div><button data-id="${button}">Remove</button>
|
||||
return `<div class="map-entry"><button data-id="${button}">Remove</button>
|
||||
${this.values.render(path, value, view)}
|
||||
</div>`
|
||||
}
|
||||
|
||||
getClassName() {
|
||||
return 'map-node'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,4 +39,8 @@ export class NumberNode extends AbstractNode<number> implements StateNode<number
|
||||
return `${options?.hideLabel ? `` : `<label>${path.last()}</label>`}
|
||||
<input value="${value ?? ''}">`
|
||||
}
|
||||
|
||||
getClassName() {
|
||||
return 'number-node'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,15 +19,21 @@ export type FilteredChildren = {
|
||||
[Case]?: NestedNodeChildren
|
||||
}
|
||||
|
||||
export interface ObjectNodeMods extends NodeMods<object> {
|
||||
collapse?: boolean
|
||||
}
|
||||
|
||||
export class ObjectNode extends AbstractNode<IObject> {
|
||||
fields: NodeChildren
|
||||
cases: NestedNodeChildren
|
||||
filter?: string
|
||||
collapse?: boolean
|
||||
|
||||
constructor(fields: FilteredChildren, mods?: NodeMods<IObject>) {
|
||||
constructor(fields: FilteredChildren, mods?: ObjectNodeMods) {
|
||||
super({
|
||||
default: () => ({}),
|
||||
...mods})
|
||||
this.collapse = mods?.collapse ?? false
|
||||
const {[Switch]: _switch, [Case]: _case, ..._fields} = fields
|
||||
this.fields = _fields
|
||||
this.cases = _case ?? {}
|
||||
@@ -46,14 +52,38 @@ export class ObjectNode extends AbstractNode<IObject> {
|
||||
}
|
||||
|
||||
renderRaw(path: Path, value: IObject, view: TreeView, options?: RenderOptions) {
|
||||
value = value ?? {}
|
||||
const activeCase = this.filter ? this.cases[value[this.filter]] : {};
|
||||
const activeFields = {...this.fields, ...activeCase}
|
||||
return `${options?.hideLabel ? `` : `<label>${path.last()}:</label>
|
||||
<div style="padding-left:8px">`}
|
||||
${Object.keys(activeFields).map(f => {
|
||||
return activeFields[f].render(path.push(f), value[f], view)
|
||||
}).join('')}
|
||||
${options?.hideLabel ? `` : `</div>`}`
|
||||
if (options?.hideLabel) {
|
||||
value = value ?? {}
|
||||
return this.renderFields(path, value, view, activeFields)
|
||||
} else if (this.collapse || options?.collapse) {
|
||||
if (value === undefined) {
|
||||
const id = view.registerClick(() => view.model.set(path, this.default()))
|
||||
return `<label class="collapse closed" data-id="${id}">${path.last()}</label>`
|
||||
} else {
|
||||
const id = view.registerClick(() => view.model.set(path, undefined))
|
||||
return `<label class="collapse open" data-id="${id}">${path.last()}</label>
|
||||
<div class="object-fields">
|
||||
${this.renderFields(path, value, view, activeFields)}
|
||||
</div>`
|
||||
}
|
||||
} else {
|
||||
value = value ?? {}
|
||||
return `<label>${path.last()}</label>
|
||||
<div class="object-fields">
|
||||
${this.renderFields(path, value, view, activeFields)}
|
||||
</div>`
|
||||
}
|
||||
}
|
||||
|
||||
renderFields(path: Path, value: IObject, view: TreeView, activeFields: NodeChildren) {
|
||||
return Object.keys(activeFields).map(f => {
|
||||
return activeFields[f].render(path.push(f), value[f], view)
|
||||
}).join('')
|
||||
}
|
||||
|
||||
getClassName() {
|
||||
return 'object-node'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,19 @@ import { TreeView } from '../view/TreeView'
|
||||
import { Path } from '../model/Path'
|
||||
import { SCHEMAS } from '../Registries'
|
||||
|
||||
export interface AnyNodeMods extends NodeMods<any> {
|
||||
[name: string]: any
|
||||
}
|
||||
|
||||
export class ReferenceNode extends AbstractNode<any> {
|
||||
protected reference: () => INode<any>
|
||||
options: RenderOptions
|
||||
|
||||
constructor(id: string, mods?: NodeMods<any>) {
|
||||
constructor(id: string, mods?: AnyNodeMods) {
|
||||
super(mods)
|
||||
this.options = {
|
||||
collapse: mods?.collapse
|
||||
}
|
||||
this.reference = () => SCHEMAS.get(id)
|
||||
}
|
||||
|
||||
@@ -20,10 +28,14 @@ export class ReferenceNode extends AbstractNode<any> {
|
||||
}
|
||||
|
||||
render(path: Path, value: any, view: TreeView, options?: RenderOptions) {
|
||||
return this.reference()?.render(path, value, view, options)
|
||||
return this.reference()?.render(path, value, view, {...this.options, ...options})
|
||||
}
|
||||
|
||||
renderRaw(path: Path, value: any, view: TreeView, options?: RenderOptions) {
|
||||
return this.reference()?.renderRaw(path, value, view, options)
|
||||
return this.reference()?.renderRaw(path, value, view, {...this.options, ...options})
|
||||
}
|
||||
|
||||
getClassName() {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,4 +28,8 @@ export class StringNode extends AbstractNode<string> implements StateNode<string
|
||||
return `${options?.hideLabel ? `` : `<label>${path.last()}</label>`}
|
||||
<input value="${value ?? ''}">`
|
||||
}
|
||||
|
||||
getClassName() {
|
||||
return 'string-node'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user